@UiApiController('/iscsi', Scope.ISCSI)
class IscsiUi(BaseController):
- REQUIRED_CEPH_ISCSI_CONFIG_VERSION = 8
+ REQUIRED_CEPH_ISCSI_CONFIG_VERSION = 10
@Endpoint()
@ReadPermission
status['message'] = 'There are no gateways defined'
return status
try:
- for gateway in gateways.keys():
+ for gateway in gateways:
try:
IscsiClient.instance(gateway_name=gateway).ping()
except RequestException:
status['available'] = True
except RequestException as e:
if e.content:
- content = json.loads(e.content)
- content_message = content.get('message')
+ try:
+ content = json.loads(e.content)
+ content_message = content.get('message')
+ except ValueError:
+ content_message = e.content
if content_message:
status['message'] = content_message
+
return status
@Endpoint()
def portals(self):
portals = []
gateways_config = IscsiGatewaysConfig.get_gateways_config()
- for name in gateways_config['gateways'].keys():
+ for name in gateways_config['gateways']:
ip_addresses = IscsiClient.instance(gateway_name=name).get_ip_addresses()
portals.append({'name': name, 'ip_addresses': ip_addresses['data']})
return sorted(portals, key=lambda p: '{}.{}'.format(p['name'], p['ip_addresses']))
class Iscsi(BaseController):
@Endpoint('GET', 'discoveryauth')
- @UpdatePermission
+ @ReadPermission
def get_discoveryauth(self):
return self._get_discoveryauth()
raise DashboardException(msg='Target already exists',
code='target_already_exists',
component='iscsi')
- IscsiTarget._validate(target_iqn, portals, disks)
+ IscsiTarget._validate(target_iqn, portals, disks, groups)
IscsiTarget._create(target_iqn, target_controls, acl_enabled, portals, disks, clients,
groups, 0, 100, config)
raise DashboardException(msg='Target IQN already in use',
code='target_iqn_already_in_use',
component='iscsi')
- IscsiTarget._validate(new_target_iqn, portals, disks)
+ IscsiTarget._validate(new_target_iqn, portals, disks, groups)
config = IscsiTarget._delete(target_iqn, config, 0, 50, new_target_iqn, target_controls,
portals, disks, clients, groups)
IscsiTarget._create(new_target_iqn, target_controls, acl_enabled, portals, disks, clients,
deleted_groups = []
for group_id in list(target_config['groups'].keys()):
if IscsiTarget._group_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals, new_groups, group_id, new_clients,
+ new_groups, group_id, new_clients,
new_disks):
deleted_groups.append(group_id)
IscsiClient.instance(gateway_name=gateway_name).delete_group(target_iqn,
TaskManager.current_task().inc_progress(task_progress_inc)
for client_iqn in list(target_config['clients'].keys()):
if IscsiTarget._client_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals, new_clients, client_iqn,
+ new_clients, client_iqn,
new_groups, deleted_groups):
IscsiClient.instance(gateway_name=gateway_name).delete_client(target_iqn,
client_iqn)
TaskManager.current_task().inc_progress(task_progress_inc)
for image_id in target_config['disks']:
if IscsiTarget._target_lun_deletion_required(target, new_target_iqn,
- new_target_controls, new_portals,
+ new_target_controls,
new_disks, image_id):
IscsiClient.instance(gateway_name=gateway_name).delete_target_lun(target_iqn,
image_id)
pool, image = image_id.split('/', 1)
IscsiClient.instance(gateway_name=gateway_name).delete_disk(pool, image)
TaskManager.current_task().inc_progress(task_progress_inc)
- if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals):
+ old_portals_by_host = IscsiTarget._get_portals_by_host(target['portals'])
+ new_portals_by_host = IscsiTarget._get_portals_by_host(new_portals)
+ for old_portal_host, old_portal_ip_list in old_portals_by_host.items():
+ if IscsiTarget._target_portal_deletion_required(old_portal_host,
+ old_portal_ip_list,
+ new_portals_by_host):
+ IscsiClient.instance(gateway_name=gateway_name).delete_gateway(target_iqn,
+ old_portal_host)
+ if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls):
IscsiClient.instance(gateway_name=gateway_name).delete_target(target_iqn)
TaskManager.current_task().set_progress(task_progress_end)
return IscsiClient.instance(gateway_name=gateway_name).get_config()
return None
@staticmethod
- def _group_deletion_required(target, new_target_iqn, new_target_controls, new_portals,
+ def _group_deletion_required(target, new_target_iqn, new_target_controls,
new_groups, group_id, new_clients, new_disks):
- if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals):
+ if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls):
return True
new_group = IscsiTarget._get_group(new_groups, group_id)
if not new_group:
# Check if any client inside this group has changed
for client_iqn in new_group['members']:
if IscsiTarget._client_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals, new_clients, client_iqn,
+ new_clients, client_iqn,
new_groups, []):
return True
# Check if any disk inside this group has changed
for disk in new_group['disks']:
image_id = '{}/{}'.format(disk['pool'], disk['image'])
if IscsiTarget._target_lun_deletion_required(target, new_target_iqn,
- new_target_controls, new_portals,
+ new_target_controls,
new_disks, image_id):
return True
return False
return None
@staticmethod
- def _client_deletion_required(target, new_target_iqn, new_target_controls, new_portals,
+ def _client_deletion_required(target, new_target_iqn, new_target_controls,
new_clients, client_iqn, new_groups, deleted_groups):
- if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals):
+ if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls):
return True
new_client = deepcopy(IscsiTarget._get_client(new_clients, client_iqn))
if not new_client:
return None
@staticmethod
- def _target_lun_deletion_required(target, new_target_iqn, new_target_controls, new_portals,
+ def _target_lun_deletion_required(target, new_target_iqn, new_target_controls,
new_disks, image_id):
- if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls,
- new_portals):
+ if IscsiTarget._target_deletion_required(target, new_target_iqn, new_target_controls):
return True
new_disk = IscsiTarget._get_disk(new_disks, image_id)
if not new_disk:
return False
@staticmethod
- def _target_deletion_required(target, new_target_iqn, new_target_controls, new_portals):
+ def _target_portal_deletion_required(old_portal_host, old_portal_ip_list, new_portals_by_host):
+ if old_portal_host not in new_portals_by_host:
+ return True
+ if sorted(old_portal_ip_list) != sorted(new_portals_by_host[old_portal_host]):
+ return True
+ return False
+
+ @staticmethod
+ def _target_deletion_required(target, new_target_iqn, new_target_controls):
if target['target_iqn'] != new_target_iqn:
return True
if target['target_controls'] != new_target_controls:
return True
- if target['portals'] != new_portals:
- return True
return False
@staticmethod
- def _validate(target_iqn, portals, disks):
+ def _validate(target_iqn, portals, disks, groups):
if not target_iqn:
raise DashboardException(msg='Target IQN is required',
code='target_iqn_required',
image = disk['image']
backstore = disk['backstore']
required_rbd_features = settings['required_rbd_features'][backstore]
- supported_rbd_features = settings['supported_rbd_features'][backstore]
+ unsupported_rbd_features = settings['unsupported_rbd_features'][backstore]
IscsiTarget._validate_image(pool, image, backstore, required_rbd_features,
- supported_rbd_features)
+ unsupported_rbd_features)
+
+ initiators = []
+ for group in groups:
+ initiators = initiators + group['members']
+ if len(initiators) != len(set(initiators)):
+ raise DashboardException(msg='Each initiator can only be part of 1 group at a time',
+ code='initiator_in_multiple_groups',
+ component='iscsi')
@staticmethod
- def _validate_image(pool, image, backstore, required_rbd_features, supported_rbd_features):
+ def _validate_image(pool, image, backstore, required_rbd_features, unsupported_rbd_features):
try:
ioctx = mgr.rados.open_ioctx(pool)
try:
required_rbd_features)),
code='image_missing_required_features',
component='iscsi')
- if img.features() & supported_rbd_features != img.features():
+ if img.features() & unsupported_rbd_features != 0:
raise DashboardException(msg='Image {} cannot be exported using {} '
'backstore because it contains unsupported '
- 'features (supported features are '
+ 'features ('
'{})'.format(image,
backstore,
format_bitmask(
- supported_rbd_features)),
+ unsupported_rbd_features)),
code='image_contains_unsupported_features',
component='iscsi')
if not target_config:
IscsiClient.instance(gateway_name=gateway_name).create_target(target_iqn,
target_controls)
- for host, ip_list in portals_by_host.items():
+ for host, ip_list in portals_by_host.items():
+ if not target_config or host not in target_config['portals']:
IscsiClient.instance(gateway_name=gateway_name).create_gateway(target_iqn,
host,
ip_list)
- TaskManager.current_task().inc_progress(task_progress_inc)
+ TaskManager.current_task().inc_progress(task_progress_inc)
targetauth_action = ('enable_acl' if acl_enabled else 'disable_acl')
IscsiClient.instance(gateway_name=gateway_name).update_targetauth(target_iqn,
targetauth_action)
def _config_to_target(target_iqn, config):
target_config = config['targets'][target_iqn]
portals = []
- for host in target_config['portals'].keys():
- ips = IscsiClient.instance(gateway_name=host).get_ip_addresses()['data']
- portal_ips = [ip for ip in ips if ip in target_config['ip_list']]
- for portal_ip in portal_ips:
+ for host, portal_config in target_config['portals'].items():
+ for portal_ip in portal_config['portal_ip_addresses']:
portal = {
'host': host,
'ip': portal_ip
gateway_name = target['portals'][0]['host']
target_info = IscsiClient.instance(gateway_name=gateway_name).get_targetinfo(target_iqn)
target['info'] = target_info
+ for client in target['clients']:
+ client_iqn = client['client_iqn']
+ client_info = IscsiClient.instance(gateway_name=gateway_name).get_clientinfo(
+ target_iqn, client_iqn)
+ client['info'] = client_info
@staticmethod
def _sorted_portals(portals):