"""
SoftLayer.file
~~~~~~~~~~~~~~~
File Storage Manager
:license: MIT, see LICENSE for more details.
"""
from SoftLayer import exceptions
from SoftLayer.managers import storage_utils
from SoftLayer import utils
# pylint: disable=too-many-public-methods
[docs]class FileStorageManager(utils.IdentifierMixin, object):
"""Manages file Storage volumes."""
def __init__(self, client):
self.configuration = {}
self.client = client
[docs] def list_file_volumes(self, datacenter=None, username=None,
storage_type=None, **kwargs):
"""Returns a list of file volumes.
:param datacenter: Datacenter short name (e.g.: dal09)
:param username: Name of volume.
:param storage_type: Type of volume: Endurance or Performance
:param kwargs:
:return: Returns a list of file volumes.
"""
if 'mask' not in kwargs:
items = [
'id',
'username',
'capacityGb',
'bytesUsed',
'serviceResource.datacenter[name]',
'serviceResourceBackendIpAddress',
'activeTransactionCount',
'fileNetworkMountAddress',
'replicationPartnerCount'
]
kwargs['mask'] = ','.join(items)
_filter = utils.NestedDict(kwargs.get('filter') or {})
_filter['nasNetworkStorage']['serviceResource']['type']['type'] = \
(utils.query_filter('!~ NAS'))
_filter['nasNetworkStorage']['storageType']['keyName'] = (
utils.query_filter('*FILE_STORAGE*'))
if storage_type:
_filter['nasNetworkStorage']['storageType']['keyName'] = (
utils.query_filter('%s_FILE_STORAGE*' % storage_type.upper()))
if datacenter:
_filter['nasNetworkStorage']['serviceResource']['datacenter'][
'name'] = (utils.query_filter(datacenter))
if username:
_filter['nasNetworkStorage']['username'] = \
(utils.query_filter(username))
kwargs['filter'] = _filter.to_dict()
return self.client.call('Account', 'getNasNetworkStorage', **kwargs)
[docs] def get_file_volume_details(self, volume_id, **kwargs):
"""Returns details about the specified volume.
:param volume_id: ID of volume.
:param kwargs:
:return: Returns details about the specified volume.
"""
if 'mask' not in kwargs:
items = [
'id',
'username',
'password',
'capacityGb',
'bytesUsed',
'snapshotCapacityGb',
'parentVolume.snapshotSizeBytes',
'storageType.keyName',
'serviceResource.datacenter[name]',
'serviceResourceBackendIpAddress',
'fileNetworkMountAddress',
'storageTierLevel',
'provisionedIops',
'lunId',
'originalVolumeName',
'originalSnapshotName',
'originalVolumeSize',
'activeTransactionCount',
'activeTransactions.transactionStatus[friendlyName]',
'replicationPartnerCount',
'replicationStatus',
'replicationPartners[id,username,'
'serviceResourceBackendIpAddress,'
'serviceResource[datacenter[name]],'
'replicationSchedule[type[keyname]]]',
]
kwargs['mask'] = ','.join(items)
return self.client.call('Network_Storage', 'getObject',
id=volume_id, **kwargs)
[docs] def get_file_volume_access_list(self, volume_id, **kwargs):
"""Returns a list of authorized hosts for a specified volume.
:param volume_id: ID of volume.
:param kwargs:
:return: Returns a list of authorized hosts for a specified volume.
"""
if 'mask' not in kwargs:
items = [
'id',
'allowedVirtualGuests[allowedHost[credential, sourceSubnet]]',
'allowedHardware[allowedHost[credential]]',
'allowedSubnets[allowedHost[credential]]',
'allowedIpAddresses[allowedHost[credential]]',
]
kwargs['mask'] = ','.join(items)
return self.client.call('Network_Storage', 'getObject',
id=volume_id, **kwargs)
[docs] def get_file_volume_snapshot_list(self, volume_id, **kwargs):
"""Returns a list of snapshots for the specified volume.
:param volume_id: ID of volume.
:param kwargs:
:return: Returns a list of snapshots for the specified volume.
"""
if 'mask' not in kwargs:
items = [
'id',
'notes',
'snapshotSizeBytes',
'storageType[keyName]',
'snapshotCreationTimestamp',
'intervalSchedule',
'hourlySchedule',
'dailySchedule',
'weeklySchedule'
]
kwargs['mask'] = ','.join(items)
return self.client.call('Network_Storage', 'getSnapshots',
id=volume_id, **kwargs)
[docs] def authorize_host_to_volume(self, volume_id,
hardware_ids=None,
virtual_guest_ids=None,
ip_address_ids=None,
subnet_ids=None,
**kwargs):
"""Authorizes hosts to File Storage Volumes
:param volume_id: The File volume to authorize hosts to
:param hardware_ids: A List of SoftLayer_Hardware ids
:param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
:param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
:param subnet_ids: A List of SoftLayer_Network_Subnet ids
:return: Returns an array of
SoftLayer_Network_Storage_Allowed_Host objects
which now have access to the given File volume
"""
host_templates = []
storage_utils.populate_host_templates(host_templates,
hardware_ids,
virtual_guest_ids,
ip_address_ids,
subnet_ids)
return self.client.call('Network_Storage', 'allowAccessFromHostList',
host_templates, id=volume_id, **kwargs)
[docs] def deauthorize_host_to_volume(self, volume_id,
hardware_ids=None,
virtual_guest_ids=None,
ip_address_ids=None,
subnet_ids=None,
**kwargs):
"""Revokes authorization of hosts to File Storage Volumes
:param volume_id: The File volume to deauthorize hosts to
:param hardware_ids: A List of SoftLayer_Hardware ids
:param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids
:param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids
:param subnet_ids: A List of SoftLayer_Network_Subnet ids
:return: Returns an array of
SoftLayer_Network_Storage_Allowed_Host objects
which have access to the given File volume
"""
host_templates = []
storage_utils.populate_host_templates(host_templates,
hardware_ids,
virtual_guest_ids,
ip_address_ids,
subnet_ids)
return self.client.call('Network_Storage', 'removeAccessFromHostList',
host_templates, id=volume_id, **kwargs)
[docs] def order_replicant_volume(self, volume_id, snapshot_schedule,
location, tier=None):
"""Places an order for a replicant file volume.
:param volume_id: The ID of the primary volume to be replicated
:param snapshot_schedule: The primary volume's snapshot
schedule to use for replication
:param location: The location for the ordered replicant volume
:param tier: The tier (IOPS per GB) of the primary volume
:return: Returns a SoftLayer_Container_Product_Order_Receipt
"""
file_mask = 'billingItem[activeChildren,hourlyFlag],'\
'storageTierLevel,osType,staasVersion,'\
'hasEncryptionAtRest,snapshotCapacityGb,schedules,'\
'intervalSchedule,hourlySchedule,dailySchedule,'\
'weeklySchedule,storageType[keyName],provisionedIops'
file_volume = self.get_file_volume_details(volume_id,
mask=file_mask)
order = storage_utils.prepare_replicant_order_object(
self, snapshot_schedule, location, tier, file_volume, 'file'
)
return self.client.call('Product_Order', 'placeOrder', order)
[docs] def get_replication_partners(self, volume_id):
"""Acquires list of replicant volumes pertaining to the given volume.
:param volume_id: The ID of the primary volume to be replicated
:return: Returns an array of SoftLayer_Location objects
"""
return self.client.call('Network_Storage',
'getReplicationPartners',
id=volume_id)
[docs] def get_replication_locations(self, volume_id):
"""Acquires list of the datacenters to which a volume can be replicated.
:param volume_id: The ID of the primary volume to be replicated
:return: Returns an array of SoftLayer_Network_Storage objects
"""
return self.client.call('Network_Storage',
'getValidReplicationTargetDatacenterLocations',
id=volume_id)
[docs] def order_duplicate_volume(self, origin_volume_id, origin_snapshot_id=None,
duplicate_size=None, duplicate_iops=None,
duplicate_tier_level=None,
duplicate_snapshot_size=None,
hourly_billing_flag=False):
"""Places an order for a duplicate file volume.
:param origin_volume_id: The ID of the origin volume to be duplicated
:param origin_snapshot_id: Origin snapshot ID to use for duplication
:param duplicate_size: Size/capacity for the duplicate volume
:param duplicate_iops: The IOPS per GB for the duplicate volume
:param duplicate_tier_level: Tier level for the duplicate volume
:param duplicate_snapshot_size: Snapshot space size for the duplicate
:param hourly_billing_flag: Billing type, monthly (False)
or hourly (True), default to monthly.
:return: Returns a SoftLayer_Container_Product_Order_Receipt
"""
file_mask = 'id,billingItem[location,hourlyFlag],snapshotCapacityGb,'\
'storageType[keyName],capacityGb,originalVolumeSize,'\
'provisionedIops,storageTierLevel,'\
'staasVersion,hasEncryptionAtRest'
origin_volume = self.get_file_volume_details(origin_volume_id,
mask=file_mask)
order = storage_utils.prepare_duplicate_order_object(
self, origin_volume, duplicate_iops, duplicate_tier_level,
duplicate_size, duplicate_snapshot_size, 'file',
hourly_billing_flag
)
if origin_snapshot_id is not None:
order['duplicateOriginSnapshotId'] = origin_snapshot_id
return self.client.call('Product_Order', 'placeOrder', order)
[docs] def order_modified_volume(self, volume_id, new_size=None, new_iops=None, new_tier_level=None):
"""Places an order for modifying an existing file volume.
:param volume_id: The ID of the volume to be modified
:param new_size: The new size/capacity for the volume
:param new_iops: The new IOPS for the volume
:param new_tier_level: The new tier level for the volume
:return: Returns a SoftLayer_Container_Product_Order_Receipt
"""
mask_items = [
'id',
'billingItem',
'storageType[keyName]',
'capacityGb',
'provisionedIops',
'storageTierLevel',
'staasVersion',
'hasEncryptionAtRest',
]
file_mask = ','.join(mask_items)
volume = self.get_file_volume_details(volume_id, mask=file_mask)
order = storage_utils.prepare_modify_order_object(
self, volume, new_iops, new_tier_level, new_size
)
return self.client.call('Product_Order', 'placeOrder', order)
[docs] def delete_snapshot(self, snapshot_id):
"""Deletes the specified snapshot object.
:param snapshot_id: The ID of the snapshot object to delete.
"""
return self.client.call('Network_Storage', 'deleteObject',
id=snapshot_id)
[docs] def order_file_volume(self, storage_type, location, size,
iops=None, tier_level=None, snapshot_size=None,
service_offering='storage_as_a_service',
hourly_billing_flag=False):
"""Places an order for a file volume.
:param storage_type: 'performance' or 'endurance'
:param location: Name of the datacenter in which to order the volume
:param size: Size of the desired volume, in GB
:param iops: Number of IOPs for a "Performance" order
:param tier_level: Tier level to use for an "Endurance" order
:param snapshot_size: The size of optional snapshot space,
if snapshot space should also be ordered (None if not ordered)
:param service_offering: Requested offering package to use in the order
('storage_as_a_service', 'enterprise', or 'performance')
:param hourly_billing_flag: Billing type, monthly (False)
or hourly (True), default to monthly.
"""
order = storage_utils.prepare_volume_order_object(
self, storage_type, location, size, iops, tier_level,
snapshot_size, service_offering, 'file', hourly_billing_flag
)
return self.client.call('Product_Order', 'placeOrder', order)
[docs] def create_snapshot(self, volume_id, notes='', **kwargs):
"""Creates a snapshot on the given file volume.
:param integer volume_id: The id of the volume
:param string notes: The notes or "name" to assign the snapshot
:return: Returns the id of the new snapshot
"""
return self.client.call('Network_Storage', 'createSnapshot',
notes, id=volume_id, **kwargs)
[docs] def enable_snapshots(self, volume_id, schedule_type, retention_count, minute, hour, day_of_week, **kwargs):
"""Enables snapshots for a specific file volume at a given schedule
:param integer volume_id: The id of the volume
:param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
:param integer retention_count: The number of snapshots to attempt to retain in this schedule
:param integer minute: The minute of the hour at which HOURLY, DAILY, and WEEKLY snapshots should be taken
:param integer hour: The hour of the day at which DAILY and WEEKLY snapshots should be taken
:param string|integer day_of_week: The day of the week on which WEEKLY snapshots should be taken,
either as a string ('SUNDAY') or integer ('0' is Sunday)
:return: Returns whether successfully scheduled or not
"""
return self.client.call('Network_Storage', 'enableSnapshots',
schedule_type,
retention_count,
minute,
hour,
day_of_week,
id=volume_id,
**kwargs)
[docs] def disable_snapshots(self, volume_id, schedule_type):
"""Disables snapshots for a specific file volume at a given schedule
:param integer volume_id: The id of the volume
:param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY'
:return: Returns whether successfully disabled or not
"""
return self.client.call('Network_Storage', 'disableSnapshots', schedule_type, id=volume_id)
[docs] def list_volume_schedules(self, volume_id):
"""Lists schedules for a given volume
:param integer volume_id: The id of the volume
:return: Returns list of schedules assigned to a given volume
"""
volume_detail = self.client.call(
'Network_Storage',
'getObject',
id=volume_id,
mask='schedules[type,properties[type]]')
return utils.lookup(volume_detail, 'schedules')
[docs] def order_snapshot_space(self, volume_id, capacity, tier, upgrade, **kwargs):
"""Orders snapshot space for the given file volume.
:param integer volume_id: The ID of the volume
:param integer capacity: The capacity to order, in GB
:param float tier: The tier level of the file volume, in IOPS per GB
:param boolean upgrade: Flag to indicate if this order is an upgrade
:return: Returns a SoftLayer_Container_Product_Order_Receipt
"""
file_mask = 'id,billingItem[location,hourlyFlag],'\
'storageType[keyName],storageTierLevel,provisionedIops,'\
'staasVersion,hasEncryptionAtRest'
file_volume = self.get_file_volume_details(volume_id,
mask=file_mask,
**kwargs)
order = storage_utils.prepare_snapshot_order_object(
self, file_volume, capacity, tier, upgrade)
return self.client.call('Product_Order', 'placeOrder', order)
[docs] def cancel_snapshot_space(self, volume_id, reason='No longer needed', immediate=False):
"""Cancels snapshot space for a given volume.
:param integer volume_id: The volume ID
:param string reason: The reason for cancellation
:param boolean immediate: Cancel immediately or on anniversary date
"""
file_volume = self.get_file_volume_details(
volume_id,
mask='mask[id,billingItem[activeChildren,hourlyFlag]]')
if 'activeChildren' not in file_volume['billingItem']:
raise exceptions.SoftLayerError(
'No snapshot space found to cancel')
children_array = file_volume['billingItem']['activeChildren']
billing_item_id = None
for child in children_array:
if child['categoryCode'] == 'storage_snapshot_space':
billing_item_id = child['id']
break
if not billing_item_id:
raise exceptions.SoftLayerError(
'No snapshot space found to cancel')
if utils.lookup(file_volume, 'billingItem', 'hourlyFlag'):
immediate = True
return self.client['Billing_Item'].cancelItem(
immediate,
True,
reason,
id=billing_item_id)
[docs] def restore_from_snapshot(self, volume_id, snapshot_id):
"""Restores a specific volume from a snapshot
:param integer volume_id: The ID of the volume
:param integer snapshot_id: The id of the restore point
:return: Returns whether successfully restored or not
"""
return self.client.call('Network_Storage', 'restoreFromSnapshot',
snapshot_id, id=volume_id)
[docs] def cancel_file_volume(self, volume_id, reason='No longer needed', immediate=False):
"""Cancels the given file storage volume.
:param integer volume_id: The volume ID
:param string reason: The reason for cancellation
:param boolean immediate: Cancel immediately or on anniversary date
"""
file_volume = self.get_file_volume_details(
volume_id,
mask='mask[id,billingItem[id,hourlyFlag]]')
if 'billingItem' not in file_volume:
raise exceptions.SoftLayerError('The volume has already been canceled')
billing_item_id = file_volume['billingItem']['id']
if utils.lookup(file_volume, 'billingItem', 'hourlyFlag'):
immediate = True
return self.client['Billing_Item'].cancelItem(
immediate,
True,
reason,
id=billing_item_id)
[docs] def failover_to_replicant(self, volume_id, replicant_id, immediate=False):
"""Failover to a volume replicant.
:param integer volume_id: The ID of the volume
:param integer replicant_id: ID of replicant to failover to
:param boolean immediate: Flag indicating if failover is immediate
:return: Returns whether failover was successful or not
"""
return self.client.call('Network_Storage', 'failoverToReplicant',
replicant_id, immediate, id=volume_id)
[docs] def failback_from_replicant(self, volume_id, replicant_id):
"""Failback from a volume replicant.
:param integer volume_id: The ID of the volume
:param integer replicant_id: ID of replicant to failback from
:return: Returns whether failback was successful or not
"""
return self.client.call('Network_Storage', 'failbackFromReplicant',
replicant_id, id=volume_id)