00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 import os
00019 import urlparse
00020 import copy
00021
00022 import na
00023 from lsm import (Volume, FileSystem, FsSnapshot, NfsExport,
00024 AccessGroup, System, Capabilities, Disk, Pool,
00025 IStorageAreaNetwork, INfs, LsmError, ErrorNumber, JobStatus,
00026 md5, VERSION, common_urllib2_error_handler,
00027 search_property, TargetPort)
00028
00029
00030 e_map = {
00031 na.Filer.ENOSPC: ErrorNumber.NOT_ENOUGH_SPACE,
00032 na.Filer.ENO_SUCH_VOLUME: ErrorNumber.NOT_FOUND_VOLUME,
00033 na.Filer.ESIZE_TOO_LARGE: ErrorNumber.NOT_ENOUGH_SPACE,
00034 na.Filer.ENOSPACE: ErrorNumber.NOT_ENOUGH_SPACE,
00035 na.Filer.ENO_SUCH_FS: ErrorNumber.NOT_FOUND_FS,
00036 na.Filer.EAPILICENSE: ErrorNumber.NOT_LICENSED,
00037 na.Filer.EFSDOESNOTEXIST: ErrorNumber.NOT_FOUND_FS,
00038 na.Filer.EFSOFFLINE: ErrorNumber.NO_SUPPORT_ONLINE_CHANGE,
00039 na.Filer.EFSNAMEINVALID: ErrorNumber.INVALID_ARGUMENT,
00040 na.Filer.ESERVICENOTLICENSED: ErrorNumber.NOT_LICENSED,
00041 na.Filer.ECLONE_LICENSE_EXPIRED: ErrorNumber.NOT_LICENSED,
00042 na.Filer.ECLONE_NOT_LICENSED: ErrorNumber.NOT_LICENSED,
00043 na.Filer.EINVALID_ISCSI_NAME: ErrorNumber.INVALID_ARGUMENT,
00044 na.Filer.ETIMEOUT: ErrorNumber.TIMEOUT,
00045 na.Filer.EUNKNOWN: ErrorNumber.PLUGIN_BUG,
00046 na.Filer.EDUPE_VOLUME_PATH: ErrorNumber.NAME_CONFLICT,
00047 na.Filer.ENAVOL_NAME_DUPE: ErrorNumber.NAME_CONFLICT,
00048 na.Filer.ECLONE_NAME_EXISTS: ErrorNumber.NAME_CONFLICT
00049 }
00050
00051
00052 def error_map(oe):
00053 """
00054 Maps a ontap error code to a lsm error code.
00055 Returns a tuple containing error code and text.
00056 """
00057 if oe.errno in e_map:
00058 return e_map[oe.errno], oe.reason
00059 else:
00060 return ErrorNumber.PLUGIN_BUG, \
00061 oe.reason + " (vendor error code= " + str(oe.errno) + ")"
00062
00063
00064 def handle_ontap_errors(method):
00065 def na_wrapper(*args, **kwargs):
00066 try:
00067 return method(*args, **kwargs)
00068 except LsmError:
00069 raise
00070 except na.FilerError as oe:
00071 error_code, error_msg = error_map(oe)
00072 raise LsmError(error_code, error_msg)
00073 except Exception as e:
00074 common_urllib2_error_handler(e)
00075
00076 return na_wrapper
00077
00078
00079 _INIT_TYPE_CONV = {
00080 'iscsi': AccessGroup.INIT_TYPE_ISCSI_IQN,
00081 'fcp': AccessGroup.INIT_TYPE_WWPN,
00082 'mixed': AccessGroup.INIT_TYPE_ISCSI_WWPN_MIXED,
00083 }
00084
00085
00086 def _na_init_type_to_lsm(na_ag):
00087 if 'initiator-group-type' in na_ag:
00088 if na_ag['initiator-group-type'] in _INIT_TYPE_CONV.keys():
00089 return _INIT_TYPE_CONV[na_ag['initiator-group-type']]
00090 else:
00091 return AccessGroup.INIT_TYPE_OTHER
00092 return AccessGroup.INIT_TYPE_UNKNOWN
00093
00094
00095 def _lsm_vol_to_na_vol_path(vol):
00096 return vol.id
00097
00098
00099 class Ontap(IStorageAreaNetwork, INfs):
00100 TMO_CONV = 1000.0
00101
00102 (SS_JOB, SPLIT_JOB) = ('ontap-ss-file-restore', 'ontap-clone-split')
00103
00104 VOLUME_PREFIX = '/vol'
00105
00106 NA_VOL_STATUS_TO_LSM = {
00107 'offline': Pool.STATUS_STOPPED,
00108 'online': Pool.STATUS_OK,
00109 'restricted': Pool.STATUS_OTHER,
00110 'unknown': Pool.STATUS_UNKNOWN,
00111 'creating': Pool.STATUS_INITIALIZING,
00112 'failed': Pool.STATUS_ERROR,
00113 'partial': Pool.STATUS_ERROR,
00114
00115 }
00116
00117 NA_VOL_STATUS_TO_LSM_STATUS_INFO = {
00118 'partial': 'all the disks in the volume are not available.',
00119 'restricted': 'volume is restricted to protocol accesses',
00120 }
00121
00122
00123 _STRIP_SIZE = 4096
00124 _OPT_IO_SIZE = 65536
00125
00126 def __init__(self):
00127 self.f = None
00128 self.sys_info = None
00129
00130 @handle_ontap_errors
00131 def plugin_register(self, uri, password, timeout, flags=0):
00132 ssl = False
00133 u = urlparse.urlparse(uri)
00134
00135 if u.scheme.lower() == 'ontap+ssl':
00136 ssl = True
00137
00138 self.f = na.Filer(u.hostname, u.username, password,
00139 timeout / Ontap.TMO_CONV, ssl)
00140
00141 i = self.f.system_info()
00142
00143 self.sys_info = System(i['system-id'], i['system-name'],
00144 System.STATUS_OK, '')
00145 return self.f.validate()
00146
00147 def time_out_set(self, ms, flags=0):
00148 self.f.timeout = int(ms / Ontap.TMO_CONV)
00149
00150 def time_out_get(self, flags=0):
00151 return int(self.f.timeout * Ontap.TMO_CONV)
00152
00153 def plugin_unregister(self, flags=0):
00154 pass
00155
00156 @staticmethod
00157 def _create_vpd(sn):
00158 """
00159 Construct the vpd83 for this lun
00160 """
00161 return "60a98000" + ''.join(["%02x" % ord(x) for x in sn])
00162
00163 @staticmethod
00164 def _lsm_lun_name(path_name):
00165 return os.path.basename(path_name)
00166
00167 def _lun(self, l):
00168 block_size = int(l['block-size'])
00169 num_blocks = int(l['size']) / block_size
00170 pool_id = "/".join(l['path'].split('/')[0:3])
00171 vol_id = l['path']
00172 vol_name = os.path.basename(vol_id)
00173 admin_state = Volume.ADMIN_STATE_ENABLED
00174 if l['online'] == 'false':
00175 admin_state = Volume.ADMIN_STATE_DISABLED
00176
00177 return Volume(vol_id, vol_name,
00178 Ontap._create_vpd(l['serial-number']), block_size,
00179 num_blocks, admin_state, self.sys_info.id, pool_id)
00180
00181 def _vol(self, v, pools=None):
00182 pool_name = v['containing-aggregate']
00183
00184 if pools is None:
00185 pools = self.pools()
00186
00187 for p in pools:
00188 if p.name == pool_name:
00189 return FileSystem(v['uuid'], v['name'], int(v['size-total']),
00190 int(v['size-available']), p.id,
00191 self.sys_info.id)
00192
00193 @staticmethod
00194 def _ss(s):
00195
00196
00197 return FsSnapshot(md5(s['name'] + s['access-time']), s['name'],
00198 s['access-time'])
00199
00200 _NA_DISK_TYPE_TO_LSM = {
00201 'ATA': Disk.TYPE_ATA,
00202 'BSAS': Disk.TYPE_SATA,
00203 'EATA': Disk.TYPE_ATA,
00204 'FCAL': Disk.TYPE_FC,
00205 'FSAS': Disk.TYPE_NL_SAS,
00206 'LUN': Disk.TYPE_OTHER,
00207 'MSATA': Disk.TYPE_SATA,
00208 'SAS': Disk.TYPE_SAS,
00209 'SATA': Disk.TYPE_SATA,
00210 'SCSI': Disk.TYPE_SCSI,
00211 'SSD': Disk.TYPE_SSD,
00212 'XATA': Disk.TYPE_ATA,
00213 'XSAS': Disk.TYPE_SAS,
00214 'unknown': Disk.TYPE_UNKNOWN,
00215 }
00216
00217 @staticmethod
00218 def _disk_type_of(na_disk):
00219 """
00220 Convert na_disk['effective-disk-type'] to LSM disk type.
00221 """
00222 na_disk_type = na_disk['effective-disk-type']
00223 if na_disk_type in Ontap._NA_DISK_TYPE_TO_LSM.keys():
00224 return Ontap._NA_DISK_TYPE_TO_LSM[na_disk_type]
00225 return Disk.TYPE_UNKNOWN
00226
00227 @staticmethod
00228 def _disk_id(na_disk):
00229 """
00230 The md5sum of na_disk['disk-uid']
00231 """
00232 return md5(na_disk['disk-uid'])
00233
00234 @staticmethod
00235 def _status_of_na_disk(na_disk):
00236 """
00237 Retrieve Disk.status from NetApp ONTAP disk-detail-info.
00238 TODO: API document does not provide enough explaination.
00239 Need lab test to verify.
00240 """
00241 status = 0
00242
00243 if 'raid-state' in na_disk:
00244 rs = na_disk['raid-state']
00245 if rs == "broken":
00246 if na_disk['broken-details'] == 'admin removed' or \
00247 na_disk['broken-details'] == 'admin failed':
00248 status |= Disk.STATUS_REMOVED
00249 elif na_disk['broken-details'] == 'admin testing':
00250 status |= Disk.STATUS_STOPPED | \
00251 Disk.STATUS_MAINTENANCE_MODE
00252 else:
00253 status |= Disk.STATUS_ERROR
00254 elif rs == "unknown":
00255 status |= Disk.STATUS_UNKNOWN
00256 elif rs == 'zeroing':
00257 status |= Disk.STATUS_INITIALIZING | Disk.STATUS_SPARE_DISK
00258 elif rs == 'reconstructing' or rs == 'copy':
00259
00260
00261 status |= Disk.STATUS_OK | Disk.STATUS_RECONSTRUCT
00262 elif rs == 'spare':
00263 if 'is-zeroed' in na_disk and na_disk['is-zeroed'] == 'true':
00264 status |= Disk.STATUS_OK | Disk.STATUS_SPARE_DISK
00265 else:
00266
00267
00268
00269
00270 status |= Disk.STATUS_STOPPED | Disk.STATUS_SPARE_DISK
00271 elif rs == 'present':
00272 status |= Disk.STATUS_OK
00273 elif rs == 'partner':
00274
00275
00276 return Disk.STATUS_OTHER
00277
00278 if 'is-prefailed' in na_disk and na_disk['is-prefailed'] == 'true':
00279 status |= Disk.STATUS_STOPPING
00280
00281 if 'is-offline' in na_disk and na_disk['is-offline'] == 'true':
00282 status |= Disk.STATUS_ERROR
00283
00284 if 'aggregate' not in na_disk:
00285
00286
00287
00288 status |= Disk.STATUS_FREE
00289
00290 if status == 0:
00291 status = Disk.STATUS_UNKNOWN
00292
00293 return status
00294
00295 @staticmethod
00296 def _status_info_of_na_disk(na_disk):
00297 """
00298 Provide more explainaion in Disk.status_info.
00299 TODO: API document does not provide enough explaination.
00300 Need lab test to verify.
00301 """
00302 status_info = ''
00303 if 'raid-state' in na_disk:
00304 rs = na_disk['raid-state']
00305 if rs == 'reconstructing':
00306 status_info = "Reconstruction progress: %s%%" %\
00307 str(na_disk['reconstruction-percent'])
00308 if 'broken-details' in na_disk:
00309 status_info = na_disk['broken-details']
00310 return status_info
00311
00312 def _disk(self, d, flag):
00313 status = Ontap._status_of_na_disk(d)
00314 return Disk(self._disk_id(d), d['name'],
00315 Ontap._disk_type_of(d),
00316 int(d['bytes-per-sector']), int(d['physical-blocks']),
00317 status, self.sys_info.id)
00318
00319 @handle_ontap_errors
00320 def volumes(self, search_key=None, search_value=None, flags=0):
00321 luns = self.f.luns_get_all()
00322 return search_property(
00323 [self._lun(l) for l in luns], search_key, search_value)
00324
00325
00326
00327 _AGGR_RAID_STATUS_CONV = {
00328 'normal': Pool.STATUS_OK,
00329 'verifying': Pool.STATUS_OK | Pool.STATUS_VERIFYING,
00330 'copying': Pool.STATUS_INITIALIZING,
00331 'ironing': Pool.STATUS_OK | Pool.STATUS_VERIFYING,
00332 'resyncing': Pool.STATUS_OK | Pool.STATUS_DEGRADED |
00333 Pool.STATUS_RECONSTRUCTING,
00334 'mirror degraded': Pool.STATUS_OK | Pool.STATUS_DEGRADED,
00335 'needs check': Pool.STATUS_ERROR,
00336 'initializing': Pool.STATUS_INITIALIZING,
00337 'growing': Pool.STATUS_OK | Pool.STATUS_GROWING,
00338 'partial': Pool.STATUS_ERROR,
00339 'noparity': Pool.STATUS_OTHER,
00340 'degraded': Pool.STATUS_OK | Pool.STATUS_DEGRADED,
00341 'reconstruct': Pool.STATUS_OK | Pool.STATUS_DEGRADED |
00342 Pool.STATUS_RECONSTRUCTING,
00343 'out-of-date': Pool.STATUS_OTHER,
00344 'foreign': Pool.STATUS_OTHER,
00345 }
00346
00347 _AGGR_RAID_ST_INFO_CONV = {
00348 'copying': 'The aggregate is currently the target aggregate of an'
00349 'active aggr copy operation. ',
00350 'invalid': 'The aggregate does not contain any volume and no volume'
00351 'can be added to it. Typically this happens after an '
00352 'aborted aggregate copy operation. ',
00353 'needs check': 'A WAFL consistency check needs to be performed on '
00354 'the aggregate. ',
00355 'partial': 'Two or more disks are missing.',
00356
00357 'noparity': 'NetApp ONTAP mark this aggregate as "noparity". ',
00358
00359 'out-of-date': 'NetApp ONTAP mark this aggregate as "out-of-date". ',
00360 'foreign': "The disks that the aggregate contains were moved to the"
00361 "current node from another node. "
00362 }
00363
00364 @staticmethod
00365 def _status_of_na_aggr(na_aggr):
00366 """
00367 Use aggr-info['state'] and ['raid-status'] for Pool.status and
00368 status_info.
00369 Return (status, status_info)
00370 """
00371 status = 0
00372 status_info = ''
00373 na_aggr_raid_status_list = list(
00374 x.strip() for x in na_aggr['raid-status'].split(','))
00375 for na_aggr_raid_status in na_aggr_raid_status_list:
00376 if na_aggr_raid_status in Ontap._AGGR_RAID_STATUS_CONV.keys():
00377 status |= Ontap._AGGR_RAID_STATUS_CONV[na_aggr_raid_status]
00378 if na_aggr_raid_status in Ontap._AGGR_RAID_ST_INFO_CONV.keys():
00379 status_info += \
00380 Ontap._AGGR_RAID_ST_INFO_CONV[na_aggr_raid_status]
00381
00382
00383 na_aggr_state = na_aggr['state'].strip()
00384
00385 if na_aggr_state == 'online' or na_aggr_state == 'creating':
00386 pass
00387 elif na_aggr_state == 'offline':
00388
00389 if status & Pool.STATUS_RECONSTRUCTING:
00390 status -= Pool.STATUS_RECONSTRUCTING
00391 status |= Pool.STATUS_DEGRADED
00392 status |= Pool.STATUS_STOPPED
00393 else:
00394 status_info += "%s " % na_aggr_state
00395
00396 if status == 0:
00397 status = Pool.STATUS_OK
00398
00399 return status, status_info
00400
00401 def _pool_from_na_aggr(self, na_aggr, flags):
00402 pool_id = na_aggr['name']
00403 pool_name = na_aggr['name']
00404 total_space = int(na_aggr['size-total'])
00405 free_space = int(na_aggr['size-available'])
00406 system_id = self.sys_info.id
00407 (status, status_info) = self._status_of_na_aggr(na_aggr)
00408
00409 element_type = (Pool.ELEMENT_TYPE_POOL | Pool.ELEMENT_TYPE_FS)
00410
00411
00412
00413 if pool_name == 'aggr0':
00414 element_type = element_type | Pool.ELEMENT_TYPE_SYS_RESERVED
00415
00416 return Pool(pool_id, pool_name, element_type, 0, total_space,
00417 free_space, status, status_info, system_id)
00418
00419 @staticmethod
00420 def _status_info_of_na_vol(na_vol):
00421 na_vol_state = na_vol['state']
00422 if na_vol_state in Ontap.NA_VOL_STATUS_TO_LSM_STATUS_INFO.keys():
00423 return Ontap.NA_VOL_STATUS_TO_LSM_STATUS_INFO[na_vol_state]
00424 return ''
00425
00426 @staticmethod
00427 def _pool_id_of_na_vol_name(na_vol_name):
00428 return "%s/%s" % (Ontap.VOLUME_PREFIX, na_vol_name)
00429
00430 def _pool_from_na_vol(self, na_vol, na_aggrs, flags):
00431 element_type = Pool.ELEMENT_TYPE_VOLUME
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 if 'space-reserve' in na_vol and \
00449 'space-reserve-enabled' in na_vol and \
00450 'reserve' in na_vol and \
00451 na_vol['space-reserve-enabled'] == 'true':
00452
00453
00454 if na_vol['space-reserve'] == 'file':
00455
00456
00457 element_type |= Pool.ELEMENT_TYPE_VOLUME_THIN
00458 element_type |= Pool.ELEMENT_TYPE_VOLUME_FULL
00459 elif na_vol['space-reserve'] == 'volume':
00460
00461
00462 if na_vol['reserve'] == na_vol['reserve-required']:
00463
00464
00465
00466 element_type |= Pool.ELEMENT_TYPE_VOLUME_FULL
00467 else:
00468 element_type |= Pool.ELEMENT_TYPE_VOLUME_THIN
00469 elif na_vol['space-reserve'] == 'none':
00470 element_type |= Pool.ELEMENT_TYPE_VOLUME_THIN
00471
00472 pool_name = na_vol['name']
00473 pool_id = self._pool_id_of_na_vol_name(na_vol['name'])
00474 total_space = int(na_vol['size-total'])
00475 free_space = int(na_vol['size-available'])
00476 system_id = self.sys_info.id
00477 status = Pool.STATUS_UNKNOWN
00478 status_info = ''
00479 if 'containing-aggregate' in na_vol:
00480 for na_aggr in na_aggrs:
00481 if na_aggr['name'] == na_vol['containing-aggregate']:
00482 status = self._status_of_na_aggr(na_aggr)[0]
00483 if not (status & Pool.STATUS_OK):
00484 status_info = "Parrent pool '%s'" \
00485 % na_aggr['name']
00486 break
00487
00488 if status & Pool.STATUS_OK and na_vol['state'] == 'offline':
00489 status = Pool.STATUS_STOPPED
00490 status_info = 'Disabled by admin'
00491
00492
00493
00494 if pool_name == '/vol/vol0':
00495 element_type |= Pool.ELEMENT_TYPE_SYS_RESERVED
00496
00497 return Pool(pool_id, pool_name, element_type, 0, total_space,
00498 free_space, status, status_info, system_id)
00499
00500 @handle_ontap_errors
00501 def capabilities(self, system, flags=0):
00502 cap = Capabilities()
00503 cap.set(Capabilities.VOLUMES)
00504 cap.set(Capabilities.VOLUME_CREATE)
00505 cap.set(Capabilities.VOLUME_RESIZE)
00506 cap.set(Capabilities.VOLUME_REPLICATE)
00507 cap.set(Capabilities.VOLUME_REPLICATE_CLONE)
00508 cap.set(Capabilities.VOLUME_COPY_RANGE_BLOCK_SIZE)
00509 cap.set(Capabilities.VOLUME_COPY_RANGE)
00510 cap.set(Capabilities.VOLUME_COPY_RANGE_CLONE)
00511 cap.set(Capabilities.VOLUME_DELETE)
00512 cap.set(Capabilities.VOLUME_ENABLE)
00513 cap.set(Capabilities.VOLUME_DISABLE)
00514 cap.set(Capabilities.VOLUME_ISCSI_CHAP_AUTHENTICATION)
00515 cap.set(Capabilities.VOLUME_MASK)
00516 cap.set(Capabilities.VOLUME_UNMASK)
00517 cap.set(Capabilities.ACCESS_GROUPS)
00518 cap.set(Capabilities.ACCESS_GROUP_CREATE_WWPN)
00519 cap.set(Capabilities.ACCESS_GROUP_CREATE_ISCSI_IQN)
00520 cap.set(Capabilities.ACCESS_GROUP_DELETE)
00521 cap.set(Capabilities.ACCESS_GROUP_INITIATOR_ADD_WWPN)
00522 cap.set(Capabilities.ACCESS_GROUP_INITIATOR_ADD_ISCSI_IQN)
00523 cap.set(Capabilities.ACCESS_GROUP_INITIATOR_DELETE)
00524 cap.set(Capabilities.VOLUMES_ACCESSIBLE_BY_ACCESS_GROUP)
00525 cap.set(Capabilities.ACCESS_GROUPS_GRANTED_TO_VOLUME)
00526 cap.set(Capabilities.VOLUME_CHILD_DEPENDENCY)
00527 cap.set(Capabilities.VOLUME_CHILD_DEPENDENCY_RM)
00528 cap.set(Capabilities.FS)
00529 cap.set(Capabilities.FS_DELETE)
00530 cap.set(Capabilities.FS_RESIZE)
00531 cap.set(Capabilities.FS_CREATE)
00532 cap.set(Capabilities.FS_CLONE)
00533 cap.set(Capabilities.FILE_CLONE)
00534 cap.set(Capabilities.FS_SNAPSHOTS)
00535 cap.set(Capabilities.FS_SNAPSHOT_CREATE)
00536 cap.set(Capabilities.FS_SNAPSHOT_DELETE)
00537 cap.set(Capabilities.FS_SNAPSHOT_RESTORE)
00538 cap.set(Capabilities.FS_CHILD_DEPENDENCY)
00539 cap.set(Capabilities.FS_CHILD_DEPENDENCY_RM)
00540 cap.set(Capabilities.EXPORT_AUTH)
00541 cap.set(Capabilities.EXPORTS)
00542 cap.set(Capabilities.EXPORT_FS)
00543 cap.set(Capabilities.EXPORT_REMOVE)
00544 cap.set(Capabilities.EXPORT_CUSTOM_PATH)
00545 cap.set(Capabilities.TARGET_PORTS)
00546 cap.set(Capabilities.DISKS)
00547 cap.set(Capabilities.VOLUME_RAID_INFO)
00548 cap.set(Capabilities.POOL_MEMBER_INFO)
00549 return cap
00550
00551 @handle_ontap_errors
00552 def plugin_info(self, flags=0):
00553 return "NetApp Filer support", VERSION
00554
00555 @handle_ontap_errors
00556 def disks(self, search_key=None, search_value=None, flags=0):
00557 disks = self.f.disks()
00558 return search_property(
00559 [self._disk(d, flags) for d in disks], search_key, search_value)
00560
00561 @handle_ontap_errors
00562 def pools(self, search_key=None, search_value=None, flags=0):
00563 pools = []
00564 na_aggrs = self.f.aggregates()
00565 for na_aggr in na_aggrs:
00566 pools.extend([self._pool_from_na_aggr(na_aggr, flags)])
00567 na_vols = self.f.volumes()
00568 for na_vol in na_vols:
00569 pools.extend([self._pool_from_na_vol(na_vol, na_aggrs, flags)])
00570 return search_property(pools, search_key, search_value)
00571
00572 @handle_ontap_errors
00573 def systems(self, flags=0):
00574 return [self.sys_info]
00575
00576 def _get_volume(self, vol_name, pool_id):
00577 return self._lun(self.f.luns_get_specific(pool_id, vol_name, None)[0])
00578
00579 @handle_ontap_errors
00580 def volume_create(self, pool, volume_name, size_bytes, provisioning,
00581 flags=0):
00582
00583 if not pool.element_type & Pool.ELEMENT_TYPE_VOLUME:
00584 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
00585 "Pool not suitable for creating volumes")
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595 flag_thin = False
00596 if provisioning == Volume.PROVISION_THIN:
00597 flag_thin = True
00598
00599 na_vol_name = pool.name
00600
00601 lun_name = self.f.lun_build_name(na_vol_name, volume_name)
00602
00603 try:
00604 self.f.lun_create(lun_name, size_bytes, flag_thin)
00605 except na.FilerError as fe:
00606 if fe.errno == na.FilerError.EVDISK_ERROR_SIZE_TOO_LARGE:
00607 raise LsmError(
00608 ErrorNumber.NOT_ENOUGH_SPACE,
00609 "No enough requested free size in pool")
00610 elif fe.errno == na.FilerError.EVDISK_ERROR_VDISK_EXISTS:
00611 raise LsmError(
00612 ErrorNumber.NAME_CONFLICT,
00613 "Requested volume name is already used by other volume")
00614 elif fe.errno == na.FilerError.EVDISK_ERROR_SIZE_TOO_SMALL:
00615
00616
00617 min_size = self.f.lun_min_size()
00618 return self.volume_create(
00619 pool, volume_name, min_size, provisioning, flags)
00620 elif fe.errno == na.FilerError.EVDISK_ERROR_NO_SUCH_VOLUME:
00621
00622 self._check_na_volume(na_vol_name)
00623 else:
00624 raise
00625
00626
00627 return None, self._get_volume(lun_name, pool.id)
00628
00629 @staticmethod
00630 def _vol_to_na_volume_name(volume):
00631 return os.path.dirname(_lsm_vol_to_na_vol_path(volume))[5:]
00632
00633 @handle_ontap_errors
00634 def volume_delete(self, volume, flags=0):
00635 try:
00636 self.f.lun_delete(_lsm_vol_to_na_vol_path(volume))
00637 except na.FilerError as f_error:
00638
00639
00640 if f_error.errno == na.FilerError.EVDISK_ERROR_VDISK_EXPORTED:
00641 raise LsmError(ErrorNumber.IS_MASKED,
00642 "Volume is masked to access group")
00643 raise
00644 return None
00645
00646 @staticmethod
00647 def _size_kb_padded(size_bytes):
00648 return int((size_bytes / 1024) * 1.3)
00649
00650 @handle_ontap_errors
00651 def volume_resize(self, volume, new_size_bytes, flags=0):
00652 try:
00653 self.f.lun_resize(_lsm_vol_to_na_vol_path(volume), new_size_bytes)
00654 except na.FilerError as fe:
00655 if fe.errno == na.FilerError.EVDISK_ERROR_SIZE_TOO_SMALL:
00656 min_size = self.f.lun_min_size()
00657 try:
00658 self.f.lun_resize(_lsm_vol_to_na_vol_path(volume),
00659 min_size)
00660 except na.FilerError as fe:
00661 if fe.errno == na.FilerError.EVDISK_ERROR_SIZE_UNCHANGED:
00662
00663
00664 pass
00665 else:
00666 raise
00667 elif fe.errno == na.FilerError.EVDISK_ERROR_SIZE_UNCHANGED:
00668 raise LsmError(ErrorNumber.NO_STATE_CHANGE,
00669 "Requested size is the same as current "
00670 "volume size")
00671 else:
00672 raise
00673 return None, self._get_volume(_lsm_vol_to_na_vol_path(volume),
00674 volume.pool_id)
00675
00676 def _check_na_volume(self, na_vol_name):
00677 na_vols = self.f.volumes(volume_name=na_vol_name)
00678 if len(na_vols) == 0:
00679 raise LsmError(ErrorNumber.NOT_FOUND_POOL,
00680 "Pool not found")
00681 elif len(na_vols) == 1:
00682
00683 if na_vols[0]['state'] == 'offline':
00684 raise LsmError(ErrorNumber.POOL_NOT_READY,
00685 "Pool not ready for volume creation")
00686 else:
00687 raise LsmError(ErrorNumber.PLUGIN_BUG,
00688 "volume_create(): "
00689 "Got 2 or more na_vols: %s" % na_vols)
00690
00691 def _volume_on_aggr(self, pool, volume):
00692 search = Ontap._vol_to_na_volume_name(volume)
00693 contained_volumes = self.f.aggregate_volume_names(pool.name)
00694 return search in contained_volumes
00695
00696 @handle_ontap_errors
00697 def volume_replicate(self, pool, rep_type, volume_src, name, flags=0):
00698
00699
00700 if rep_type != Volume.REPLICATE_CLONE:
00701 raise LsmError(ErrorNumber.NO_SUPPORT, "rep_type not supported")
00702
00703
00704
00705 if pool is None or self._volume_on_aggr(pool, volume_src):
00706
00707 dest = os.path.dirname(_lsm_vol_to_na_vol_path(volume_src)) + '/' \
00708 + name
00709 self.f.clone(_lsm_vol_to_na_vol_path(volume_src), dest)
00710 return None, self._get_volume(dest, volume_src.pool_id)
00711 else:
00712
00713
00714 raise LsmError(ErrorNumber.NO_SUPPORT,
00715 "Unable to replicate volume to different pool")
00716
00717 @handle_ontap_errors
00718 def volume_replicate_range_block_size(self, system, flags=0):
00719 return 4096
00720
00721 @handle_ontap_errors
00722 def volume_replicate_range(self, rep_type, volume_src, volume_dest, ranges,
00723 flags=0):
00724 if rep_type != Volume.REPLICATE_CLONE:
00725 raise LsmError(ErrorNumber.NO_SUPPORT, "rep_type not supported")
00726 self.f.clone(_lsm_vol_to_na_vol_path(volume_src),
00727 _lsm_vol_to_na_vol_path(volume_dest), None, ranges)
00728
00729 @handle_ontap_errors
00730 def volume_enable(self, volume, flags=0):
00731 try:
00732 return self.f.lun_online(_lsm_vol_to_na_vol_path(volume))
00733 except na.FilerError as fe:
00734 if fe.errno == na.FilerError.EVDISK_ERROR_VDISK_NOT_DISABLED:
00735 raise LsmError(ErrorNumber.NO_STATE_CHANGE,
00736 "Volume is already enabled")
00737 raise
00738
00739 @handle_ontap_errors
00740 def volume_disable(self, volume, flags=0):
00741 try:
00742 return self.f.lun_offline(_lsm_vol_to_na_vol_path(volume))
00743 except na.FilerError as fe:
00744 if fe.errno == na.FilerError.EVDISK_ERROR_VDISK_NOT_ENABLED:
00745 raise LsmError(ErrorNumber.NO_STATE_CHANGE,
00746 "Volume is already disabled")
00747 raise
00748
00749 @handle_ontap_errors
00750 def volume_mask(self, access_group, volume, flags=0):
00751 igroups = self.f.igroups(group_name=access_group.name)
00752 if len(igroups) != 1:
00753 raise LsmError(ErrorNumber.NOT_FOUND_ACCESS_GROUP,
00754 "AccessGroup %s(%d) not found" %
00755 (access_group.name, access_group.id))
00756
00757 cur_init_ids = Ontap._initiators_in_group(igroups[0])
00758 if len(cur_init_ids) == 0:
00759 raise LsmError(
00760 ErrorNumber.EMPTY_ACCESS_GROUP,
00761 "Refuse to do volume masking against empty access group")
00762 try:
00763 self.f.lun_map(access_group.name, _lsm_vol_to_na_vol_path(volume))
00764 except na.FilerError as fe:
00765 if fe.errno == na.FilerError.EVDISK_ERROR_INITGROUP_HAS_VDISK:
00766 raise LsmError(
00767 ErrorNumber.NO_STATE_CHANGE,
00768 "Volume is already masked to requested access group")
00769 else:
00770 raise
00771 return None
00772
00773 @handle_ontap_errors
00774 def volume_unmask(self, access_group, volume, flags=0):
00775 try:
00776 self.f.lun_unmap(
00777 access_group.name, _lsm_vol_to_na_vol_path(volume))
00778 except na.FilerError as filer_error:
00779 if filer_error.errno == na.FilerError.EVDISK_ERROR_NO_SUCH_LUNMAP:
00780 raise LsmError(
00781 ErrorNumber.NO_STATE_CHANGE,
00782 "Volume is not masked to requested access group")
00783 else:
00784 raise
00785 return None
00786
00787 @staticmethod
00788 def _initiators_in_group(g):
00789 rc = []
00790 if g:
00791 if 'initiators' in g and g['initiators'] is not None:
00792 initiators = na.to_list(g['initiators']['initiator-info'])
00793 for i in initiators:
00794 rc.append(i['initiator-name'])
00795 return rc
00796
00797 def _access_group(self, g):
00798 name = g['initiator-group-name']
00799
00800 if 'initiator-group-uuid' in g:
00801 ag_id = g['initiator-group-uuid']
00802 else:
00803 ag_id = md5(name)
00804
00805 return AccessGroup(ag_id, name, Ontap._initiators_in_group(g),
00806 _na_init_type_to_lsm(g), self.sys_info.id)
00807
00808 @handle_ontap_errors
00809 def access_groups(self, search_key=None, search_value=None, flags=0):
00810 groups = self.f.igroups()
00811 return search_property(
00812 [self._access_group(g) for g in groups], search_key, search_value)
00813
00814 @handle_ontap_errors
00815 def access_group_create(self, name, init_id, init_type, system,
00816 flags=0):
00817 if self.sys_info.id != system.id:
00818 raise LsmError(ErrorNumber.NOT_FOUND_SYSTEM,
00819 "System %s not found" % system.id)
00820
00821
00822
00823
00824
00825 cur_lsm_groups = self.access_groups()
00826 for cur_lsm_group in cur_lsm_groups:
00827 if cur_lsm_group.name == name:
00828 raise LsmError(
00829 ErrorNumber.NAME_CONFLICT,
00830 "Requested access group name is already used by other "
00831 "access group")
00832 if init_id in cur_lsm_group.init_ids:
00833 raise LsmError(
00834 ErrorNumber.EXISTS_INITIATOR,
00835 "Requested initiator is already used by other "
00836 "access group")
00837
00838 if init_type == AccessGroup.INIT_TYPE_ISCSI_IQN:
00839 self.f.igroup_create(name, 'iscsi')
00840 elif init_type == AccessGroup.INIT_TYPE_WWPN:
00841 self.f.igroup_create(name, 'fcp')
00842 else:
00843 raise LsmError(ErrorNumber.NO_SUPPORT,
00844 "ONTAP only support iSCSI and FC/FCoE, but got "
00845 "init_type: %d" % init_type)
00846
00847 self.f.igroup_add_initiator(name, init_id)
00848
00849 groups = self.access_groups()
00850 for g in groups:
00851 if g.name == name:
00852 return g
00853
00854 raise LsmError(ErrorNumber.PLUGIN_BUG,
00855 "access_group_create(): Unable to find access group "
00856 "%s just created!" % name)
00857
00858 @handle_ontap_errors
00859 def access_group_delete(self, access_group, flags=0):
00860 try:
00861 return self.f.igroup_delete(access_group.name)
00862 except na.FilerError as f_error:
00863 if f_error.errno == \
00864 na.FilerError.EVDISK_ERROR_INITGROUP_MAPS_EXIST:
00865 raise LsmError(ErrorNumber.IS_MASKED,
00866 "Access Group has volume masked")
00867 raise
00868
00869 @handle_ontap_errors
00870 def access_group_initiator_add(self, access_group, init_id, init_type,
00871 flags=0):
00872 try:
00873 self.f.igroup_add_initiator(access_group.name, init_id)
00874 except na.FilerError as oe:
00875 if oe.errno == na.FilerError.IGROUP_ALREADY_HAS_INIT:
00876 return copy.deepcopy(access_group)
00877 elif oe.errno == na.FilerError.NO_SUCH_IGROUP:
00878 raise LsmError(ErrorNumber.NOT_FOUND_ACCESS_GROUP,
00879 "AccessGroup %s(%d) not found" %
00880 (access_group.name, access_group.id))
00881 else:
00882 raise
00883 na_ags = self.f.igroups(access_group.name)
00884 if len(na_ags) != 1:
00885 raise LsmError(ErrorNumber.PLUGIN_BUG,
00886 "access_group_initiator_add(): Got unexpected"
00887 "(not 1) count of na_ag: %s" % na_ags)
00888
00889 return self._access_group(na_ags[0])
00890
00891 @handle_ontap_errors
00892 def access_group_initiator_delete(self, access_group, init_id, init_type,
00893 flags=0):
00894 igroups = self.f.igroups(group_name=access_group.name)
00895 if len(igroups) != 1:
00896 raise LsmError(ErrorNumber.NOT_FOUND_ACCESS_GROUP,
00897 "AccessGroup %s(%d) not found" %
00898 (access_group.name, access_group.id))
00899
00900 cur_init_ids = Ontap._initiators_in_group(igroups[0])
00901 if init_id not in cur_init_ids:
00902 raise LsmError(
00903 ErrorNumber.NO_STATE_CHANGE,
00904 "Initiator %s does not exist in access group %s" %
00905 (init_id, access_group.name))
00906
00907 if len(cur_init_ids) == 1:
00908 raise LsmError(
00909 ErrorNumber.LAST_INIT_IN_ACCESS_GROUP,
00910 "Refuse to remove last initiator from access group")
00911
00912 self.f.igroup_del_initiator(access_group.name, init_id)
00913
00914 na_ags = self.f.igroups(access_group.name)
00915 if len(na_ags) != 1:
00916 raise LsmError(ErrorNumber.PLUGIN_BUG,
00917 "access_group_initiator_add(): Got unexpected"
00918 "(not 1) count of na_ag: %s" % na_ags)
00919
00920 return self._access_group(na_ags[0])
00921
00922 @handle_ontap_errors
00923 def volumes_accessible_by_access_group(self, access_group, flags=0):
00924 rc = []
00925
00926 if len(access_group.init_ids):
00927 luns = self.f.lun_initiator_list_map_info(access_group.init_ids[0],
00928 access_group.name)
00929 rc = [self._lun(l) for l in luns]
00930
00931 return rc
00932
00933 @handle_ontap_errors
00934 def access_groups_granted_to_volume(self, volume, flags=0):
00935 groups = self.f.lun_map_list_info(_lsm_vol_to_na_vol_path(volume))
00936 return [self._access_group(g) for g in groups]
00937
00938 @handle_ontap_errors
00939 def iscsi_chap_auth(self, init_id, in_user, in_password, out_user,
00940 out_password, flags=0):
00941 if out_user and out_password and \
00942 (in_user is None or in_password is None):
00943 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
00944 "out_user and out_password only supported if "
00945 "inbound is supplied")
00946
00947 self.f.iscsi_initiator_add_auth(init_id, in_user, in_password,
00948 out_user, out_password)
00949
00950 @staticmethod
00951 def _rpercent(total, current):
00952 p = 1 - (current / float(total))
00953 p = min(int(100 * p), 100)
00954 return p
00955
00956 def _restore_file_status(self, num):
00957 running = self.f.snapshot_file_restore_num()
00958
00959 if running:
00960 running = min(num, running)
00961 return JobStatus.INPROGRESS, Ontap._rpercent(num, running), None
00962
00963 return JobStatus.COMPLETE, 100, None
00964
00965 def _clone_split_status(self, volumes):
00966 vols = volumes.split(',')
00967 current = len(vols)
00968
00969
00970
00971 running = self.f.volume_split_status()
00972
00973 for v in vols:
00974 if v not in running:
00975 current -= 1
00976
00977 if not running:
00978 return JobStatus.COMPLETE, 100, None
00979 else:
00980 return JobStatus.INPROGRESS, \
00981 Ontap._rpercent(len(vols), current), None
00982
00983 @handle_ontap_errors
00984 def job_status(self, job_id, flags=0):
00985 if job_id is None and '@' not in job_id:
00986 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
00987 "Invalid job, missing @")
00988
00989 job = job_id.split('@', 2)
00990
00991 if job[0] == Ontap.SS_JOB:
00992 return self._restore_file_status(int(job[1]))
00993 elif job[0] == Ontap.SPLIT_JOB:
00994 return self._clone_split_status(job[1])
00995
00996 raise LsmError(ErrorNumber.INVALID_ARGUMENT, "Invalid job")
00997
00998 @handle_ontap_errors
00999 def job_free(self, job_id, flags=0):
01000 return None
01001
01002 @handle_ontap_errors
01003 def fs(self, search_key=None, search_value=None, flags=0):
01004 volumes = self.f.volumes()
01005 pools = self.pools()
01006 return search_property(
01007 [self._vol(v, pools) for v in volumes], search_key, search_value)
01008
01009 @handle_ontap_errors
01010 def fs_delete(self, fs, flags=0):
01011 self.f.volume_delete(fs.name)
01012
01013 @handle_ontap_errors
01014 def fs_resize(self, fs, new_size_bytes, flags=0):
01015 diff = new_size_bytes - fs.total_space
01016
01017 diff = Ontap._size_kb_padded(diff)
01018 self.f.volume_resize(fs.name, diff)
01019 return None, self._vol(self.f.volumes(fs.name)[0])
01020
01021 @handle_ontap_errors
01022 def fs_create(self, pool, name, size_bytes, flags=0):
01023 self.f.volume_create(pool.name, name, size_bytes)
01024 return None, self._vol(self.f.volumes(name)[0])
01025
01026 @handle_ontap_errors
01027 def fs_clone(self, src_fs, dest_fs_name, snapshot=None, flags=0):
01028 self.f.volume_clone(src_fs.name, dest_fs_name, snapshot)
01029 return None, self._vol(self.f.volumes(dest_fs_name)[0])
01030
01031 @staticmethod
01032 def build_name(volume_name, relative_name):
01033 return "/vol/%s/%s" % (volume_name, relative_name)
01034
01035 @handle_ontap_errors
01036 def fs_file_clone(self, fs, src_file_name, dest_file_name, snapshot=None,
01037 flags=0):
01038 full_src = Ontap.build_name(fs.name, src_file_name)
01039 full_dest = Ontap.build_name(fs.name, dest_file_name)
01040
01041 ss = None
01042 if snapshot:
01043 ss = snapshot.name
01044
01045 self.f.clone(full_src, full_dest, ss)
01046 return None
01047
01048 @handle_ontap_errors
01049 def fs_snapshots(self, fs, flags=0):
01050 snapshots = self.f.snapshots(fs.name)
01051 return [Ontap._ss(s) for s in snapshots]
01052
01053 @handle_ontap_errors
01054 def fs_snapshot_create(self, fs, snapshot_name, flags=0):
01055
01056 snap = self.f.snapshot_create(fs.name, snapshot_name)
01057 return None, Ontap._ss(snap)
01058
01059 @handle_ontap_errors
01060 def fs_snapshot_delete(self, fs, snapshot, flags=0):
01061 self.f.snapshot_delete(fs.name, snapshot.name)
01062
01063 def _ss_restore_files(self, volume_name, snapshot_name, files,
01064 restore_files):
01065 for i in range(len(files)):
01066 src = Ontap.build_name(volume_name, files[i])
01067 dest = None
01068 if restore_files and len(restore_files):
01069 dest = Ontap.build_name(volume_name, restore_files[i])
01070 self.f.snapshot_restore_file(snapshot_name, src, dest)
01071
01072 @handle_ontap_errors
01073 def fs_snapshot_restore(self, fs, snapshot, files, restore_files,
01074 all_files=False, flags=0):
01075 """
01076 Restores a FS or files on a FS.
01077 Note: Restoring an individual file is a O(n) operation, i.e. time it
01078 takes to restore a file depends on the file size. Reverting an entire
01079 FS is O(1). Try to avoid restoring individual files from a snapshot.
01080 """
01081 if files is None and all_files:
01082 self.f.snapshot_restore_volume(fs.name, snapshot.name)
01083 return None
01084 elif files:
01085 if restore_files and len(files) != len(restore_files):
01086 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
01087 "num files != num restore_files")
01088
01089 self._ss_restore_files(fs.name, snapshot.name, files,
01090 restore_files)
01091 return "%s@%d" % (Ontap.SS_JOB, len(files))
01092 else:
01093 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
01094 "Invalid parameter combination")
01095
01096 @handle_ontap_errors
01097 def export_auth(self, flags=0):
01098 """
01099 Returns the types of authentication that are available for NFS
01100 """
01101 return self.f.export_auth_types()
01102
01103 @staticmethod
01104 def _get_group(access_group, e):
01105 rc = []
01106
01107 if access_group in e:
01108 for r in na.to_list(e[access_group]['exports-hostname-info']):
01109 if 'all-hosts' in r:
01110 if r['all-hosts'] == 'true':
01111 rc.append('*')
01112 else:
01113 rc.append(r['name'])
01114 return rc
01115
01116 @staticmethod
01117 def _get_value(key, e):
01118 if key in e:
01119 return e[key]
01120 else:
01121 return None
01122
01123 @staticmethod
01124 def _get_volume_id(volumes, vol_name):
01125 for v in volumes:
01126 if v.name == vol_name:
01127 return v.id
01128 raise RuntimeError("Volume not found in volumes:" +
01129 ":".join(volumes) + " " + vol_name)
01130
01131 @staticmethod
01132 def _get_volume_from_path(path):
01133
01134 return path[5:].split('/')[0]
01135
01136 @staticmethod
01137 def _export(volumes, e):
01138 if 'actual-pathname' in e:
01139 path = e['actual-pathname']
01140 export = e['pathname']
01141 else:
01142 path = e['pathname']
01143 export = e['pathname']
01144
01145 vol_name = Ontap._get_volume_from_path(path)
01146 fs_id = Ontap._get_volume_id(volumes, vol_name)
01147
01148 return NfsExport(md5(vol_name + fs_id), fs_id, export,
01149 e['sec-flavor']['sec-flavor-info']['flavor'],
01150 Ontap._get_group('root', e),
01151 Ontap._get_group('read-write', e),
01152 Ontap._get_group('read-only', e),
01153 NfsExport.ANON_UID_GID_NA, NfsExport.ANON_UID_GID_NA,
01154 None)
01155
01156 @handle_ontap_errors
01157 def exports(self, search_key=None, search_value=None, flags=0):
01158
01159
01160 v = self.fs()
01161 return search_property(
01162 [Ontap._export(v, e) for e in self.f.nfs_exports()],
01163 search_key, search_value)
01164
01165 def _get_volume_from_id(self, fs_id):
01166 fs = self.fs()
01167 for i in fs:
01168 if i.id == fs_id:
01169 return i
01170 raise RuntimeError("fs id not found in fs:" + fs_id)
01171
01172 def _current_export(self, export_path):
01173 """
01174 Checks to see if we already have this export.
01175 """
01176 cur_exports = self.exports()
01177 for ce in cur_exports:
01178 if ce.export_path == export_path:
01179 return True
01180
01181 return False
01182
01183 @handle_ontap_errors
01184 def export_fs(self, fs_id, export_path, root_list, rw_list, ro_list,
01185 anon_uid, anon_gid, auth_type, options, flags=0):
01186 """
01187 Creates or modifies the specified export
01188 """
01189
01190 if not (anon_gid == -1 or anon_gid == 0xFFFFFFFFFFFFFFFF):
01191 raise LsmError(ErrorNumber.INVALID_ARGUMENT,
01192 "ontap plugin does not support "
01193 "anon_gid setting")
01194
01195
01196 vol = self._get_volume_from_id(fs_id)
01197
01198
01199
01200 if export_path is None:
01201 export_path = '/vol/' + vol.name
01202
01203
01204
01205 if self._current_export(export_path):
01206 method = self.f.nfs_export_fs_modify2
01207 else:
01208 method = self.f.nfs_export_fs2
01209
01210 method('/vol/' + vol.name,
01211 export_path,
01212 ro_list,
01213 rw_list,
01214 root_list,
01215 anon_uid,
01216 auth_type)
01217
01218 current_exports = self.exports()
01219 for e in current_exports:
01220 if e.fs_id == fs_id and e.export_path == export_path:
01221 return e
01222
01223 raise LsmError(ErrorNumber.PLUGIN_BUG,
01224 "export not created successfully!")
01225
01226 @handle_ontap_errors
01227 def export_remove(self, export, flags=0):
01228 self.f.nfs_export_remove([export.export_path])
01229
01230 @handle_ontap_errors
01231 def volume_child_dependency(self, volume, flags=0):
01232 return False
01233
01234 @handle_ontap_errors
01235 def volume_child_dependency_rm(self, volume, flags=0):
01236 return None
01237
01238 @handle_ontap_errors
01239 def fs_child_dependency(self, fs, files=None, flags=0):
01240 rc = False
01241
01242
01243
01244 if not files:
01245 children = self.f.volume_children(fs.name)
01246 if children:
01247 rc = True
01248 return rc
01249
01250 @handle_ontap_errors
01251 def fs_child_dependency_rm(self, fs, files=None, flags=0):
01252 if files:
01253 return None
01254 else:
01255 children = self.f.volume_children(fs.name)
01256 if children:
01257 for c in children:
01258 self.f.volume_split_clone(c)
01259 return "%s@%s" % (Ontap.SPLIT_JOB, ",".join(children))
01260 return None
01261
01262 @handle_ontap_errors
01263 def target_ports(self, search_key=None, search_value=None, flags=0):
01264 tp = []
01265
01266
01267 fcp = self.f.fcp_list()
01268
01269 for f in fcp:
01270 a = f['addr']
01271 adapter = f['adapter']
01272 tp.append(TargetPort(md5(a), TargetPort.TYPE_FC, a, a, a,
01273 adapter, self.sys_info.id))
01274
01275 node_name = self.f.iscsi_node_name()
01276 iscsi = self.f.iscsi_list()
01277 for i in iscsi:
01278
01279 service_address = node_name
01280 network_address = "%s:%s" % (i['ip'], i['port'])
01281 physical_address = i['mac']
01282 physical_name = i['interface']
01283 tid = md5(service_address + network_address + physical_address +
01284 physical_name)
01285 tp.append(TargetPort(tid, TargetPort.TYPE_ISCSI,
01286 service_address,
01287 network_address,
01288 physical_address,
01289 physical_name,
01290 self.sys_info.id))
01291
01292 return search_property(tp, search_key, search_value)
01293
01294 @staticmethod
01295 def _raid_type_of_na_aggr(na_aggr):
01296 na_raid_statuses = na_aggr['raid-status'].split(',')
01297 if 'mixed_raid_type' in na_raid_statuses:
01298 return Volume.RAID_TYPE_MIXED
01299 elif 'raid0' in na_raid_statuses:
01300 return Volume.RAID_TYPE_RAID0
01301 elif 'raid4' in na_raid_statuses:
01302 return Volume.RAID_TYPE_RAID4
01303 elif 'raid_dp' in na_raid_statuses:
01304 return Volume.RAID_TYPE_RAID6
01305 return Volume.RAID_TYPE_UNKNOWN
01306
01307 @handle_ontap_errors
01308 def volume_raid_info(self, volume, flags=0):
01309 na_vol_name = Ontap._get_volume_from_path(volume.pool_id)
01310 na_vol = self.f.volumes(volume_name=na_vol_name)
01311 if len(na_vol) == 0:
01312
01313 raise LsmError(
01314 ErrorNumber.NOT_FOUND_VOLUME,
01315 "Volume not found")
01316 if len(na_vol) != 1:
01317 raise LsmError(
01318 ErrorNumber.PLUGIN_BUG,
01319 "volume_raid_info(): Got 2+ na_vols from self.f.volumes() "
01320 "%s" % na_vol)
01321
01322 na_vol = na_vol[0]
01323 na_aggr_name = na_vol['containing-aggregate']
01324 na_aggr = self.f.aggregates(aggr_name=na_aggr_name)[0]
01325 raid_type = Ontap._raid_type_of_na_aggr(na_aggr)
01326 disk_count = int(na_aggr['disk-count'])
01327
01328 return [
01329 raid_type, Ontap._STRIP_SIZE, disk_count, Ontap._STRIP_SIZE,
01330 Ontap._OPT_IO_SIZE]
01331
01332 @handle_ontap_errors
01333 def pool_member_info(self, pool, flags=0):
01334 if pool.element_type & Pool.ELEMENT_TYPE_VOLUME:
01335
01336 raid_type = Volume.RAID_TYPE_OTHER
01337 member_type = Pool.MEMBER_TYPE_POOL
01338 na_vol = self.f.volumes(volume_name=pool.name)[0]
01339 disk_ids = [na_vol['containing-aggregate']]
01340 else:
01341
01342 member_type = Pool.MEMBER_TYPE_DISK
01343 na_aggr = self.f.aggregates(aggr_name=pool.name)[0]
01344 raid_type = Ontap._raid_type_of_na_aggr(na_aggr)
01345 disk_ids = list(
01346 Ontap._disk_id(d)
01347 for d in self.f.disks()
01348 if 'aggregate' in d and d['aggregate'] == pool.name)
01349
01350 return raid_type, member_type, disk_ids