]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/cephadm/services/iscsi.py
5 from typing
import List
, cast
, Optional
6 from ipaddress
import ip_address
, IPv6Address
8 from mgr_module
import HandleCommandResult
9 from ceph
.deployment
.service_spec
import IscsiServiceSpec
11 from orchestrator
import DaemonDescription
, DaemonDescriptionStatus
12 from .cephadmservice
import CephadmDaemonDeploySpec
, CephService
15 logger
= logging
.getLogger(__name__
)
18 class IscsiService(CephService
):
21 def config(self
, spec
: IscsiServiceSpec
) -> None: # type: ignore
22 assert self
.TYPE
== spec
.service_type
24 self
.mgr
._check
_pool
_exists
(spec
.pool
, spec
.service_name())
26 def prepare_create(self
, daemon_spec
: CephadmDaemonDeploySpec
) -> CephadmDaemonDeploySpec
:
27 assert self
.TYPE
== daemon_spec
.daemon_type
29 spec
= cast(IscsiServiceSpec
, self
.mgr
.spec_store
[daemon_spec
.service_name
].spec
)
30 igw_id
= daemon_spec
.daemon_id
32 keyring
= self
.get_keyring_with_caps(self
.get_auth_entity(igw_id
),
33 ['mon', 'profile rbd, '
34 'allow command "osd blocklist", '
35 'allow command "config-key get" with "key" prefix "iscsi/"',
36 'mgr', 'allow command "service status"',
40 if isinstance(spec
.ssl_cert
, list):
41 cert_data
= '\n'.join(spec
.ssl_cert
)
43 cert_data
= spec
.ssl_cert
44 ret
, out
, err
= self
.mgr
.check_mon_command({
45 'prefix': 'config-key set',
46 'key': f
'iscsi/{utils.name_to_config_section("iscsi")}.{igw_id}/iscsi-gateway.crt',
51 if isinstance(spec
.ssl_key
, list):
52 key_data
= '\n'.join(spec
.ssl_key
)
54 key_data
= spec
.ssl_key
55 ret
, out
, err
= self
.mgr
.check_mon_command({
56 'prefix': 'config-key set',
57 'key': f
'iscsi/{utils.name_to_config_section("iscsi")}.{igw_id}/iscsi-gateway.key',
61 # add active mgr ip address to trusted list so dashboard can access
62 trusted_ip_list
= spec
.trusted_ip_list
if spec
.trusted_ip_list
else ''
64 trusted_ip_list
+= ','
65 trusted_ip_list
+= self
.mgr
.get_mgr_ip()
68 'client_name': '{}.{}'.format(utils
.name_to_config_section('iscsi'), igw_id
),
69 'trusted_ip_list': trusted_ip_list
,
72 igw_conf
= self
.mgr
.template
.render('services/iscsi/iscsi-gateway.cfg.j2', context
)
74 daemon_spec
.keyring
= keyring
75 daemon_spec
.extra_files
= {'iscsi-gateway.cfg': igw_conf
}
76 daemon_spec
.final_config
, daemon_spec
.deps
= self
.generate_config(daemon_spec
)
77 daemon_spec
.deps
= [self
.mgr
.get_mgr_ip()]
80 def config_dashboard(self
, daemon_descrs
: List
[DaemonDescription
]) -> None:
81 def get_set_cmd_dicts(out
: str) -> List
[dict]:
82 gateways
= json
.loads(out
)['gateways']
84 # TODO: fail, if we don't have a spec
85 spec
= cast(IscsiServiceSpec
,
86 self
.mgr
.spec_store
.all_specs
.get(daemon_descrs
[0].service_name(), None))
87 if spec
.api_secure
and spec
.ssl_cert
and spec
.ssl_key
:
89 'prefix': 'dashboard set-iscsi-api-ssl-verification',
94 'prefix': 'dashboard set-iscsi-api-ssl-verification',
97 for dd
in daemon_descrs
:
98 assert dd
.hostname
is not None
99 # todo: this can fail:
100 spec
= cast(IscsiServiceSpec
,
101 self
.mgr
.spec_store
.all_specs
.get(dd
.service_name(), None))
103 logger
.warning('No ServiceSpec found for %s', dd
)
105 ip
= utils
.resolve_ip(self
.mgr
.inventory
.get_addr(dd
.hostname
))
106 # IPv6 URL encoding requires square brackets enclosing the ip
107 if type(ip_address(ip
)) is IPv6Address
:
110 if spec
.api_secure
and spec
.ssl_cert
and spec
.ssl_key
:
112 service_url
= '{}://{}:{}@{}:{}'.format(
113 protocol
, spec
.api_user
or 'admin', spec
.api_password
or 'admin', ip
, spec
.api_port
or '5000')
114 gw
= gateways
.get(dd
.hostname
)
115 if not gw
or gw
['service_url'] != service_url
:
116 safe_service_url
= '{}://{}:{}@{}:{}'.format(
117 protocol
, '<api-user>', '<api-password>', ip
, spec
.api_port
or '5000')
118 logger
.info('Adding iSCSI gateway %s to Dashboard', safe_service_url
)
120 'prefix': 'dashboard iscsi-gateway-add',
121 'inbuf': service_url
,
126 self
._check
_and
_set
_dashboard
(
127 service_name
='iSCSI',
128 get_cmd
='dashboard iscsi-gateway-list',
129 get_set_cmd_dicts
=get_set_cmd_dicts
133 daemon_ids
: List
[str],
135 known
: Optional
[List
[str]] = None) -> HandleCommandResult
:
136 # if only 1 iscsi, alert user (this is not passable with --force)
137 warn
, warn_message
= self
._enough
_daemons
_to
_stop
(self
.TYPE
, daemon_ids
, 'Iscsi', 1, True)
139 return HandleCommandResult(-errno
.EBUSY
, '', warn_message
)
141 # if reached here, there is > 1 nfs daemon. make sure none are down
143 'ALERT: 1 iscsi daemon is already down. Please bring it back up before stopping this one')
144 iscsi_daemons
= self
.mgr
.cache
.get_daemons_by_type(self
.TYPE
)
145 for i
in iscsi_daemons
:
146 if i
.status
!= DaemonDescriptionStatus
.running
:
147 return HandleCommandResult(-errno
.EBUSY
, '', warn_message
)
149 names
= [f
'{self.TYPE}.{d_id}' for d_id
in daemon_ids
]
150 warn_message
= f
'It is presumed safe to stop {names}'
151 return HandleCommandResult(0, warn_message
, '')
153 def post_remove(self
, daemon
: DaemonDescription
, is_failed_deploy
: bool) -> None:
155 Called after the daemon is removed.
157 logger
.debug(f
'Post remove daemon {self.TYPE}.{daemon.daemon_id}')
159 # remove config for dashboard iscsi gateways
160 ret
, out
, err
= self
.mgr
.mon_command({
161 'prefix': 'dashboard iscsi-gateway-rm',
162 'name': daemon
.hostname
,
165 logger
.info(f
'{daemon.hostname} removed from iscsi gateways dashboard config')
167 # needed to know if we have ssl stuff for iscsi in ceph config
168 iscsi_config_dict
= {}
169 ret
, iscsi_config
, err
= self
.mgr
.mon_command({
170 'prefix': 'config-key dump',
174 iscsi_config_dict
= json
.loads(iscsi_config
)
176 # remove iscsi cert and key from ceph config
177 for iscsi_key
, value
in iscsi_config_dict
.items():
178 if f
'iscsi/client.{daemon.name()}/' in iscsi_key
:
179 ret
, out
, err
= self
.mgr
.mon_command({
180 'prefix': 'config-key rm',
183 logger
.info(f
'{iscsi_key} removed from ceph config')
185 def purge(self
, service_name
: str) -> None:
186 """Removes configuration
188 spec
= cast(IscsiServiceSpec
, self
.mgr
.spec_store
[service_name
].spec
)
190 # remove service configuration from the pool
192 subprocess
.run(['rados',
193 '-k', str(self
.mgr
.get_ceph_option('keyring')),
194 '-n', f
'mgr.{self.mgr.get_mgr_id()}',
195 '-p', cast(str, spec
.pool
),
199 logger
.info(f
'<gateway.conf> removed from {spec.pool}')
200 except subprocess
.CalledProcessError
as ex
:
201 logger
.error(f
'Error executing <<{ex.cmd}>>: {ex.output}')
202 except subprocess
.TimeoutExpired
:
203 logger
.error(f
'timeout (5s) trying to remove <gateway.conf> from {spec.pool}')
206 logger
.exception(f
'failed to purge {service_name}')