00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 import urllib2
00022 import sys
00023 import urlparse
00024 try:
00025 import simplejson as json
00026 except ImportError:
00027 import json
00028 import base64
00029 import time
00030 import traceback
00031 import copy
00032
00033 from lsm import (AccessGroup, Capabilities, ErrorNumber, FileSystem, INfs,
00034 IStorageAreaNetwork, LsmError, NfsExport, Pool,
00035 FsSnapshot, System, VERSION, Volume, md5, error,
00036 common_urllib2_error_handler, search_property)
00037
00038
00039 def handle_nstor_errors(method):
00040 def nstor_wrapper(*args, **kwargs):
00041 try:
00042 return method(*args, **kwargs)
00043 except LsmError as lsm:
00044 raise
00045 except Exception as e:
00046 error("Unexpected exception:\n" + traceback.format_exc())
00047 raise LsmError(ErrorNumber.PLUGIN_BUG, str(e),
00048 traceback.format_exc())
00049 return nstor_wrapper
00050
00051
00052 class NexentaStor(INfs, IStorageAreaNetwork):
00053
00054 _V3_PORT = '2000'
00055 _V4_PORT = '8457'
00056
00057 def plugin_info(self, flags=0):
00058
00059 return "NexentaStor support", VERSION
00060
00061 def __init__(self):
00062 self.uparse = None
00063 self.password = None
00064 self.timeout = None
00065 self._system = None
00066 self._port = NexentaStor._V3_PORT
00067 self._scheme = 'http'
00068
00069 def _ns_request(self, path, data):
00070 response = None
00071 parms = json.dumps(data)
00072
00073 url = '%s://%s:%s/%s' % \
00074 (self._scheme, self.uparse.hostname, self._port, path)
00075 request = urllib2.Request(url, parms)
00076
00077 username = self.uparse.username or 'admin'
00078 base64string = base64.encodestring('%s:%s' %
00079 (username, self.password))[:-1]
00080 request.add_header('Authorization', 'Basic %s' % base64string)
00081 request.add_header('Content-Type', 'application/json')
00082 try:
00083 response = urllib2.urlopen(request, timeout=self.timeout / 1000)
00084 except Exception as e:
00085 try:
00086 common_urllib2_error_handler(e)
00087 except LsmError as lsm_e:
00088 exc_info = sys.exc_info()
00089 if lsm_e.code == ErrorNumber.NETWORK_CONNREFUSED:
00090 if not self.uparse.port and \
00091 self._port == NexentaStor._V3_PORT:
00092 self._port = NexentaStor._V4_PORT
00093 return self._ns_request(path, data)
00094
00095 raise exc_info[0], exc_info[1], exc_info[2]
00096
00097 resp_json = response.read()
00098 resp = json.loads(resp_json)
00099 if resp['error']:
00100
00101 if 'message' in resp['error']:
00102 msg = resp['error']['message']
00103
00104 if 'dataset already exists' in msg:
00105 raise LsmError(ErrorNumber.NAME_CONFLICT, msg)
00106
00107 if 'Unable to destroy hostgroup' in msg:
00108 raise LsmError(ErrorNumber.IS_MASKED, msg)
00109
00110 raise LsmError(ErrorNumber.PLUGIN_BUG, resp['error'])
00111 return resp['result']
00112
00113 def _request(self, method, obj, params):
00114 return self._ns_request('rest/nms', {"method": method, "object": obj,
00115 "params": params})
00116
00117 @property
00118 def system(self):
00119 if self._system is None:
00120 license_info = self._request("get_license_info", "appliance", [])
00121 fqdn = self._request("get_fqdn", "appliance", [])
00122 self._system = System(license_info['machine_sig'], fqdn,
00123 System.STATUS_OK, '')
00124 return self._system
00125
00126 def plugin_register(self, uri, password, timeout, flags=0):
00127 self.uparse = urlparse.urlparse(uri)
00128 self.password = password or 'nexenta'
00129 self.timeout = timeout
00130
00131 if self.uparse.port:
00132 self._port = self.uparse.port
00133
00134 if self.uparse.scheme.lower() == 'nstor+ssl':
00135 self._scheme = 'https'
00136
00137 @staticmethod
00138 def _to_bytes(size):
00139 if size.lower().endswith('k'):
00140 return int(float(size[:-1]) * 1024)
00141 if size.lower().endswith('m'):
00142 return int(float(size[:-1]) * 1024 * 1024)
00143 if size.lower().endswith('g'):
00144 return int(float(size[:-1]) * 1024 * 1024 * 1024)
00145 if size.lower().endswith('t'):
00146 return int(float(size[:-1]) * 1024 * 1024 * 1024 * 1024)
00147 if size.lower().endswith('p'):
00148 return int(float(size[:-1]) * 1024 * 1024 * 1024 * 1024 * 1024)
00149 if size.lower().endswith('e'):
00150 return int(
00151 float(size[:-1]) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)
00152 return size
00153
00154 @handle_nstor_errors
00155 def pools(self, search_key=None, search_value=None, flags=0):
00156 pools_list = self._request("get_all_names", "volume", [""])
00157
00158 pools = []
00159 for pool in pools_list:
00160 if pool == 'syspool':
00161 continue
00162 pool_info = self._request("get_child_props", "volume",
00163 [str(pool), ""])
00164
00165 pools.append(Pool(pool_info['name'], pool_info['name'],
00166 Pool.ELEMENT_TYPE_VOLUME |
00167 Pool.ELEMENT_TYPE_VOLUME_THIN |
00168 Pool.ELEMENT_TYPE_FS,
00169 0,
00170 NexentaStor._to_bytes(pool_info['size']),
00171 NexentaStor._to_bytes(pool_info['free']),
00172 Pool.STATUS_UNKNOWN, '', self.system.id))
00173
00174 return search_property(pools, search_key, search_value)
00175
00176 @handle_nstor_errors
00177 def fs(self, search_key=None, search_value=None, flags=0):
00178 fs_list = self._request("get_all_names", "folder", [""])
00179
00180 fss = []
00181 pools = {}
00182 for fs in fs_list:
00183 pool_name = NexentaStor._get_pool_id(fs)
00184 if pool_name == 'syspool':
00185 continue
00186 if pool_name not in pools:
00187 pool_info = self._request("get_child_props", "volume",
00188 [str(fs), ""])
00189 pools[pool_name] = pool_info
00190 else:
00191 pool_info = pools[pool_name]
00192 fss.append(
00193 FileSystem(fs, fs, NexentaStor._to_bytes(pool_info['size']),
00194 self._to_bytes(pool_info['available']), pool_name,
00195 fs))
00196
00197 return search_property(fss, search_key, search_value)
00198
00199 @handle_nstor_errors
00200 def fs_create(self, pool, name, size_bytes, flags=0):
00201 """
00202 Consider you have 'data' pool and folder 'a' in it (data/a)
00203 If you want create 'data/a/b', command line should look like:
00204 --create-fs=a/b --pool=data --size=1G
00205 """
00206 if name.startswith(pool.name + '/'):
00207 chunks = name.split('/')[1:]
00208 name = '/'.join(chunks)
00209 fs_name = self._request("create", "folder", [pool.name, name])[0]
00210 filesystem = FileSystem(fs_name, fs_name, pool.total_space,
00211 pool.free_space, pool.id, fs_name)
00212 return None, filesystem
00213
00214 @handle_nstor_errors
00215 def fs_delete(self, fs, flags=0):
00216 result = self._request("destroy", "folder", [fs.name, "-r"])
00217 return
00218
00219 @handle_nstor_errors
00220 def fs_snapshots(self, fs, flags=0):
00221 snapshot_list = self._request("get_names", "snapshot", [fs.name])
00222
00223 snapshots = []
00224 for snapshot in snapshot_list:
00225 snapshot_info = self._request("get_child_props", "snapshot",
00226 [snapshot, "creation_seconds"])
00227 snapshots.append(FsSnapshot(snapshot, snapshot,
00228 snapshot_info['creation_seconds']))
00229
00230 return snapshots
00231
00232 @handle_nstor_errors
00233 def fs_snapshot_create(self, fs, snapshot_name, flags=0):
00234 full_name = "%s@%s" % (fs.name, snapshot_name)
00235
00236 self._request("create", "snapshot", [full_name, "0"])
00237 snapshot_info = self._request("get_child_props", "snapshot",
00238 [full_name, "creation_seconds"])
00239 return None, FsSnapshot(full_name, full_name,
00240 snapshot_info['creation_seconds'])
00241
00242 @handle_nstor_errors
00243 def fs_snapshot_delete(self, fs, snapshot, flags=0):
00244 self._request("destroy", "snapshot", [snapshot.name, ""])
00245 return
00246
00247 @handle_nstor_errors
00248 def time_out_set(self, ms, flags=0):
00249 self.timeout = ms
00250 return
00251
00252 @handle_nstor_errors
00253 def time_out_get(self, flags=0):
00254 return self.timeout
00255
00256 @handle_nstor_errors
00257 def plugin_unregister(self, flags=0):
00258 return
00259
00260 @handle_nstor_errors
00261 def job_status(self, job_id, flags=0):
00262 return
00263
00264 @handle_nstor_errors
00265 def job_free(self, job_id, flags=0):
00266 return
00267
00268 @handle_nstor_errors
00269 def capabilities(self, system, flags=0):
00270 c = Capabilities()
00271
00272
00273 c.set(Capabilities.FS)
00274 c.set(Capabilities.FS_DELETE)
00275
00276 c.set(Capabilities.FS_CREATE)
00277 c.set(Capabilities.FS_CLONE)
00278
00279 c.set(Capabilities.FS_SNAPSHOTS)
00280 c.set(Capabilities.FS_SNAPSHOT_CREATE)
00281 c.set(Capabilities.FS_SNAPSHOT_DELETE)
00282 c.set(Capabilities.FS_SNAPSHOT_RESTORE)
00283
00284 c.set(Capabilities.FS_CHILD_DEPENDENCY)
00285 c.set(Capabilities.FS_CHILD_DEPENDENCY_RM)
00286
00287
00288
00289 c.set(Capabilities.EXPORT_AUTH)
00290 c.set(Capabilities.EXPORTS)
00291 c.set(Capabilities.EXPORT_FS)
00292 c.set(Capabilities.EXPORT_REMOVE)
00293 c.set(Capabilities.EXPORT_CUSTOM_PATH)
00294
00295
00296 c.set(Capabilities.VOLUMES)
00297 c.set(Capabilities.VOLUME_CREATE)
00298 c.set(Capabilities.VOLUME_RESIZE)
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 c.set(Capabilities.VOLUME_DELETE)
00309
00310
00311 c.set(Capabilities.VOLUME_MASK)
00312 c.set(Capabilities.VOLUME_UNMASK)
00313 c.set(Capabilities.ACCESS_GROUPS)
00314 c.set(Capabilities.ACCESS_GROUP_CREATE_ISCSI_IQN)
00315 c.set(Capabilities.ACCESS_GROUP_DELETE)
00316 c.set(Capabilities.ACCESS_GROUP_INITIATOR_ADD_ISCSI_IQN)
00317 c.set(Capabilities.ACCESS_GROUP_INITIATOR_DELETE)
00318 c.set(Capabilities.VOLUMES_ACCESSIBLE_BY_ACCESS_GROUP)
00319 c.set(Capabilities.ACCESS_GROUPS_GRANTED_TO_VOLUME)
00320 c.set(Capabilities.VOLUME_CHILD_DEPENDENCY)
00321 c.set(Capabilities.VOLUME_CHILD_DEPENDENCY_RM)
00322
00323 c.set(Capabilities.VOLUME_ISCSI_CHAP_AUTHENTICATION)
00324
00325 return c
00326
00327 @handle_nstor_errors
00328 def systems(self, flags=0):
00329 return [self.system]
00330
00331 @handle_nstor_errors
00332 def fs_resize(self, fs, new_size_bytes, flags=0):
00333 raise LsmError(ErrorNumber.NO_SUPPORT, "Not implemented")
00334
00335 @staticmethod
00336 def _get_pool_id(fs_name):
00337 return fs_name.split('/')[0]
00338
00339 @handle_nstor_errors
00340 def fs_clone(self, src_fs, dest_fs_name, snapshot=None, flags=0):
00341 folder = src_fs.name.split('/')[0]
00342 dest = folder + '/' + dest_fs_name
00343
00344 if snapshot is None:
00345
00346 name = src_fs.name.split('/')[0]
00347 snapshot = self.fs_snapshot_create(
00348 src_fs, name + "_clone_ss_" + md5(time.ctime()))[1]
00349
00350 self._request("clone", "folder", [snapshot.name, dest])
00351 pool_id = NexentaStor._get_pool_id(dest)
00352 pool_info = self._request("get_child_props", "volume", [pool_id, ""])
00353 fs = FileSystem(dest, dest, NexentaStor._to_bytes(pool_info['size']),
00354 NexentaStor._to_bytes(pool_info['available']), pool_id,
00355 self.system.id)
00356 return None, fs
00357
00358 @handle_nstor_errors
00359 def fs_snapshot_restore(self, fs, snapshot, files, restore_files,
00360 all_files=False, flags=0):
00361 self._request("rollback", "snapshot", [snapshot.name, '-r'])
00362 return
00363
00364 def _dependencies_list(self, fs_name, volume=False):
00365 obj = "folder"
00366 if volume:
00367 obj = 'volume'
00368 pool_id = NexentaStor._get_pool_id(fs_name)
00369 fs_list = self._request("get_all_names", "folder", ["^%s/" % pool_id])
00370
00371 dependency_list = []
00372 for filesystem in fs_list:
00373 origin = self._request("get_child_prop", "folder",
00374 [filesystem, 'origin'])
00375 if origin.startswith("%s/" % fs_name) or \
00376 origin.startswith("%s@" % fs_name):
00377 dependency_list.append(filesystem)
00378 return dependency_list
00379
00380 @handle_nstor_errors
00381 def fs_child_dependency(self, fs, files, flags=0):
00382
00383
00384 return len(self._dependencies_list(fs.name)) > 0
00385
00386 @handle_nstor_errors
00387 def fs_child_dependency_rm(self, fs, files, flags=0):
00388 dep_list = self._dependencies_list(fs.name)
00389 for dep in dep_list:
00390 clone_name = dep.split('@')[0]
00391 self._request("promote", "folder", [clone_name])
00392 return None
00393
00394 @handle_nstor_errors
00395 def export_auth(self, flags=0):
00396 """
00397 Returns the types of authentication that are available for NFS
00398 """
00399 result = self._request("get_share_confopts", "netstorsvc",
00400 ['svc:/network/nfs/server:default'])
00401 rc = []
00402 methods = result['auth_type']['opts'].split(';')
00403 for m in methods:
00404 rc.append(m.split('=>')[0])
00405 return rc
00406
00407 @handle_nstor_errors
00408 def exports(self, search_key=None, search_value=None, flags=0):
00409 """
00410 Get a list of all exported file systems on the controller.
00411 """
00412 exp_list = self._request("get_shared_folders", "netstorsvc",
00413 ['svc:/network/nfs/server:default', ''])
00414
00415 exports = []
00416 for e in exp_list:
00417 opts = self._request("get_shareopts", "netstorsvc",
00418 ['svc:/network/nfs/server:default', e])
00419 exports.append(NfsExport(md5(opts['name']), e, opts['name'],
00420 opts['auth_type'], opts['root'],
00421 opts['read_write'], opts['read_only'],
00422 'N/A', 'N/A', opts['extra_options']))
00423
00424 return search_property(exports, search_key, search_value)
00425
00426 @handle_nstor_errors
00427 def export_fs(self, fs_id, export_path, root_list, rw_list, ro_list,
00428 anon_uid, anon_gid, auth_type, options, flags=0):
00429 """
00430 Exports a filesystem as specified in the export
00431 """
00432 if export_path is None:
00433 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
00434 "Export path is required")
00435
00436 md5_id = md5(export_path)
00437 fs_dict = {'auth_type': 'sys', 'anonymous': 'false'}
00438 if ro_list:
00439 fs_dict['read_only'] = ','.join(ro_list)
00440 if rw_list:
00441 fs_dict['read_write'] = ','.join(rw_list)
00442 if anon_uid or anon_gid:
00443 fs_dict['anonymous'] = 'true'
00444 if root_list:
00445 fs_dict['root'] = ','.join(root_list)
00446 if auth_type:
00447 fs_dict['auth_type'] = str(auth_type)
00448 if '*' in rw_list:
00449 fs_dict['anonymous'] = 'true'
00450 if options:
00451 fs_dict['extra_options'] = str(options)
00452
00453 result = self._request("share_folder", "netstorsvc",
00454 ['svc:/network/nfs/server:default',
00455 fs_id, fs_dict])
00456 return NfsExport(md5_id, fs_id, export_path, auth_type, root_list,
00457 rw_list, ro_list, anon_uid, anon_gid, options)
00458
00459 @handle_nstor_errors
00460 def export_remove(self, export, flags=0):
00461 """
00462 Removes the specified export
00463 """
00464 self._request("unshare_folder", "netstorsvc",
00465 ['svc:/network/nfs/server:default', export.fs_id, '0'])
00466 return
00467
00468
00469
00470 @staticmethod
00471 def _calc_group(name):
00472 return 'lsm_' + md5(name)[0:8]
00473
00474 @handle_nstor_errors
00475 def volumes(self, search_key=None, search_value=None, flags=0):
00476 """
00477 Returns an array of volume objects
00478
00479 """
00480 vol_list = []
00481 lu_list = self._request("get_names", "zvol", [""])
00482
00483
00484
00485
00486
00487 for lu in lu_list:
00488 try:
00489 lu_props = self._request("get_lu_props", "scsidisk", [lu])
00490 except:
00491 lu_props = {'guid': '', 'state': 'N/A'}
00492
00493 zvol_props = self._request("get_child_props", "zvol", [lu, ""])
00494
00495 block_size = NexentaStor._to_bytes(zvol_props['volblocksize'])
00496 size_bytes = int(zvol_props['size_bytes'])
00497 num_of_blocks = size_bytes / block_size
00498 admin_state = Volume.ADMIN_STATE_ENABLED
00499
00500 vol_list.append(
00501 Volume(lu, lu, lu_props['guid'].lower(),
00502 block_size, num_of_blocks,
00503 admin_state, self.system.id,
00504 NexentaStor._get_pool_id(lu)))
00505
00506 return search_property(vol_list, search_key, search_value)
00507
00508 @handle_nstor_errors
00509 def volume_create(self, pool, volume_name, size_bytes, provisioning,
00510 flags=0):
00511 """
00512 Creates a volume, given a pool, volume name, size and provisioning
00513
00514 returns a tuple (job_id, new volume)
00515 Note: Tuple return values are mutually exclusive, when one
00516 is None the other must be valid.
00517 """
00518 if volume_name.startswith(pool.name + '/'):
00519 chunks = volume_name.split('/')[1:]
00520 volume_name = '/'.join(chunks)
00521 sparse = provisioning in (Volume.PROVISION_DEFAULT,
00522 Volume.PROVISION_THIN,
00523 Volume.PROVISION_UNKNOWN)
00524 if sparse:
00525 sparse = '1'
00526 else:
00527 sparse = '0'
00528 name = '%s/%s' % (pool.name, volume_name)
00529 block_size = ''
00530
00531 self._request("create", "zvol",
00532 [name, str(size_bytes), block_size, sparse])
00533
00534 self._request("set_child_prop", "zvol", [name, 'compression', 'on'])
00535
00536 self._request("set_child_prop", "zvol",
00537 [name, 'logbias', 'throughput'])
00538
00539 self._request("create_lu", "scsidisk", [name, []])
00540 vols = self.volumes('id', name)
00541 return None, vols[0]
00542
00543 @handle_nstor_errors
00544 def volume_delete(self, volume, flags=0):
00545 """
00546 Deletes a volume.
00547
00548 Returns None on success, else raises an LsmError
00549 """
00550 ag = self.access_groups_granted_to_volume(volume)
00551
00552 if len(ag):
00553 raise LsmError(ErrorNumber.IS_MASKED,
00554 "Volume is masked to access group")
00555
00556 self._request("delete_lu", "scsidisk", [volume.id])
00557 self._request("destroy", "zvol", [volume.id, ''])
00558 return
00559
00560 @handle_nstor_errors
00561 def volume_resize(self, volume, new_size_bytes, flags=0):
00562 """
00563 Re-sizes a volume.
00564
00565 Returns a tuple (job_id, re-sized_volume)
00566 Note: Tuple return values are mutually exclusive, when one
00567 is None the other must be valid.
00568 """
00569 self._request("set_child_prop", "zvol",
00570 [volume.name, 'volsize', str(new_size_bytes)])
00571 self._request("realign_size", "scsidisk", [volume.name])
00572 new_num_of_blocks = new_size_bytes / volume.block_size
00573 return None, Volume(volume.id, volume.name, volume.vpd83,
00574 volume.block_size, new_num_of_blocks,
00575 volume.admin_state, volume.system_id,
00576 volume.pool_id)
00577
00578 @handle_nstor_errors
00579 def volume_replicate(self, pool, rep_type, volume_src, name, flags=0):
00580 """
00581 Replicates a volume from the specified pool. In this library, to
00582 replicate means to create a new volume which is a copy of the source.
00583
00584 Returns a tuple (job_id, replicated volume)
00585 Note: Tuple return values are mutually exclusive, when one
00586 is None the other must be valid.
00587 """
00588 raise LsmError(ErrorNumber.NO_SUPPORT,
00589 "volume_replicate not implemented")
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628 @handle_nstor_errors
00629 def iscsi_chap_auth(self, init_id, in_user, in_password, out_user,
00630 out_password, flags=0):
00631 """
00632 Register a user/password for the specified initiator for CHAP
00633 authentication.
00634 """
00635 if in_user is None:
00636 in_user = ""
00637
00638 if in_password is None:
00639 in_password = ""
00640
00641 if out_user is not None or out_password is not None:
00642 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
00643 "outbound chap authentication is not supported at "
00644 "this time")
00645
00646 try:
00647 self._request("create_initiator", "iscsitarget",
00648 [init_id,
00649 {'initiatorchapuser': in_user,
00650 'initiatorchapsecret': in_password}])
00651 except:
00652 self._request("modify_initiator", "iscsitarget",
00653 [init_id,
00654 {'initiatorchapuser': in_user,
00655 'initiatorchapsecret': in_password}])
00656
00657 self._request("modify_initiator", "iscsitarget",
00658 [init_id,
00659 {'initiatorchapuser': in_user,
00660 'initiatorchapsecret': in_password}])
00661 return
00662
00663 def _get_views(self, volume_name):
00664 results = []
00665 try:
00666 results = self._request("list_lun_mapping_entries", "scsidisk",
00667 [volume_name])
00668 except Exception:
00669 pass
00670 return results
00671
00672 def _volume_mask(self, group_name, volume_name):
00673 self._request("add_lun_mapping_entry", "scsidisk",
00674 [volume_name, {'host_group': group_name}])
00675 return
00676
00677 @handle_nstor_errors
00678 def volume_mask(self, access_group, volume, flags=0):
00679 """
00680 Allows an access group to access a volume.
00681 """
00682
00683 if list(v.id for v in
00684 self.volumes_accessible_by_access_group(access_group)
00685 if v.id == volume.id):
00686 raise LsmError(
00687 ErrorNumber.NO_STATE_CHANGE,
00688 "Volume is already masked to requested access group")
00689
00690 self._volume_mask(access_group.name, volume.name)
00691 return
00692
00693 @handle_nstor_errors
00694 def volume_unmask(self, access_group, volume, flags=0):
00695 """
00696 Revokes access for an access group for a volume
00697 """
00698 views = self._get_views(volume.name)
00699 view_number = -1
00700 for view in views:
00701 if view['host_group'] == access_group.name:
00702 view_number = view['entry_number']
00703 if view_number == -1:
00704 raise LsmError(ErrorNumber.NO_STATE_CHANGE,
00705 "There is no such mapping for volume %s" %
00706 volume.name)
00707
00708 self._request("remove_lun_mapping_entry", "scsidisk",
00709 [volume.name, view_number])
00710 return
00711
00712 @handle_nstor_errors
00713 def access_groups(self, search_key=None, search_value=None, flags=0):
00714 """
00715 Returns a list of access groups
00716 """
00717 hg_list = self._request("list_hostgroups", "stmf", [])
00718
00719 ag_list = []
00720 for hg in hg_list:
00721 init_ids = self._request("list_hostgroup_members", "stmf", [hg])
00722 ag_list.append(
00723 AccessGroup(hg, hg, init_ids, AccessGroup.INIT_TYPE_ISCSI_IQN,
00724 self.system.id))
00725 return search_property(ag_list, search_key, search_value)
00726
00727 @handle_nstor_errors
00728 def access_group_create(self, name, init_id, init_type, system,
00729 flags=0):
00730 """
00731 Creates of access group
00732 """
00733 if system.id != self.system.id:
00734 raise LsmError(ErrorNumber.NOT_FOUND_SYSTEM,
00735 "System %s not found" % system.id)
00736 if init_type != AccessGroup.INIT_TYPE_ISCSI_IQN:
00737 raise LsmError(ErrorNumber.NO_SUPPORT,
00738 "Nstor only support iSCSI Access Group")
00739
00740 for ag in self.access_groups():
00741 if init_id in ag.init_ids:
00742 raise LsmError(ErrorNumber.EXISTS_INITIATOR,
00743 "%s is already part of %s access group" %
00744 (init_id, ag.name))
00745
00746 if name == ag.name:
00747 raise LsmError(ErrorNumber.NAME_CONFLICT,
00748 "Access group with name exists!")
00749 self._request("create_hostgroup", "stmf", [name])
00750 self._add_initiator(name, init_id)
00751
00752 return AccessGroup(name, name, [init_id], init_type, system.id)
00753
00754 @handle_nstor_errors
00755 def access_group_delete(self, access_group, flags=0):
00756 """
00757 Deletes an access group
00758 """
00759 vols = self.volumes_accessible_by_access_group(access_group)
00760 if len(vols):
00761 raise LsmError(ErrorNumber.IS_MASKED,
00762 "Access Group has volume(s) masked")
00763
00764 self._request("destroy_hostgroup", "stmf", [access_group.name])
00765 return
00766
00767 @handle_nstor_errors
00768 def _add_initiator(self, group_name, initiator_id, remove=False):
00769 command = "add_hostgroup_member"
00770 if remove:
00771 command = "remove_hostgroup_member"
00772
00773 self._request(command, "stmf", [group_name, initiator_id])
00774 return
00775
00776 def _access_group_initiators(self, access_group):
00777 hg_list = self._request("list_hostgroups", "stmf", [])
00778 if access_group.name not in hg_list:
00779 raise LsmError(ErrorNumber.NOT_FOUND_ACCESS_GROUP,
00780 "AccessGroup %s(%s) not found" %
00781 (access_group.name, access_group.id))
00782
00783 return self._request("list_hostgroup_members", "stmf",
00784 [access_group.name])
00785
00786 @handle_nstor_errors
00787 def access_group_initiator_add(self, access_group, init_id, init_type,
00788 flags=0):
00789 """
00790 Adds an initiator to an access group
00791 """
00792 if init_type != AccessGroup.INIT_TYPE_ISCSI_IQN:
00793 raise LsmError(ErrorNumber.NO_SUPPORT,
00794 "Nstor only support iSCSI Access Group")
00795
00796 init_ids = self._access_group_initiators(access_group)
00797 if init_id in init_ids:
00798
00799 return copy.deepcopy(access_group)
00800
00801 self._add_initiator(access_group.name, init_id)
00802 init_ids = self._request("list_hostgroup_members", "stmf",
00803 [access_group.name])
00804 return AccessGroup(access_group.name, access_group.name,
00805 init_ids, AccessGroup.INIT_TYPE_ISCSI_IQN,
00806 access_group.id)
00807
00808 @handle_nstor_errors
00809 def access_group_initiator_delete(self, access_group, init_id, init_type,
00810 flags=0):
00811 """
00812 Deletes an initiator from an access group
00813 """
00814 init_ids = self._access_group_initiators(access_group)
00815 if init_id not in init_ids:
00816
00817 return copy.deepcopy(access_group)
00818 self._add_initiator(access_group.name, init_id, True)
00819 init_ids = self._request("list_hostgroup_members", "stmf",
00820 [access_group.name])
00821 return AccessGroup(access_group.name, access_group.name,
00822 init_ids, AccessGroup.INIT_TYPE_ISCSI_IQN,
00823 access_group.id)
00824
00825 @handle_nstor_errors
00826 def volumes_accessible_by_access_group(self, access_group, flags=0):
00827 """
00828 Returns the list of volumes that access group has access to.
00829 """
00830 volumes = []
00831 all_volumes_list = self.volumes(flags=flags)
00832 for vol in all_volumes_list:
00833 for view in self._get_views(vol.name):
00834 if view['host_group'] == access_group.name:
00835 volumes.append(vol)
00836 return volumes
00837
00838 @handle_nstor_errors
00839 def access_groups_granted_to_volume(self, volume, flags=0):
00840 """
00841 Returns the list of access groups that have access to the specified
00842 """
00843 ag_list = self.access_groups(flags=flags)
00844
00845 hg = []
00846 for view in self._get_views(volume.name):
00847 for ag in ag_list:
00848 if ag.name == view['host_group']:
00849 hg.append(ag)
00850 return hg
00851
00852 @handle_nstor_errors
00853 def volume_child_dependency(self, volume, flags=0):
00854 """
00855 Returns True if this volume has other volumes which are dependant on
00856 it. Implies that this volume cannot be deleted or possibly modified
00857 because it would affect its children.
00858 """
00859 return len(self._dependencies_list(volume.name, True)) > 0
00860
00861 @handle_nstor_errors
00862 def volume_child_dependency_rm(self, volume, flags=0):
00863 """
00864 If this volume has child dependency, this method call will fully
00865 replicate the blocks removing the relationship between them. This
00866 should return None (success) if volume_child_dependency would return
00867 False.
00868
00869 Note: This operation could take a very long time depending on the size
00870 of the volume and the number of child dependencies.
00871
00872 Returns None if complete else job id, raises LsmError on errors.
00873 """
00874 dep_list = self._dependencies_list(volume.name)
00875 for dep in dep_list:
00876 clone_name = dep.split('@')[0]
00877 self._request("promote", "volume", [clone_name])
00878 return None