Lin Yang 20780a2e0b Add delete method to base resource class
Change-Id: I0a60af9e1751e623e9e266141ab3d8db9d3d4691
2019-09-10 14:47:42 -07:00

260 lines
9.4 KiB
Python

# Copyright 2018 Intel, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import jsonschema
import logging
from sushy import exceptions
from sushy.resources import base
from sushy import utils
from rsd_lib import base as rsd_lib_base
from rsd_lib import common as rsd_lib_common
from rsd_lib.resources.v2_3.storage_service import volume_metrics
from rsd_lib.resources.v2_3.storage_service import volume_schemas
from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__)
class CapacitySourcesField(base.ListField):
providing_pools = base.Field('ProvidingPools',
adapter=utils.get_members_identities)
allocated_Bytes = base.Field(
['ProvidedCapacity', 'Data', 'AllocatedBytes'],
adapter=rsd_lib_utils.num_or_none)
class LinksField(base.CompositeField):
endpoints = base.Field(['Oem', 'Intel_RackScale', 'Endpoints'], default=(),
adapter=utils.get_members_identities)
"""Link to related endpoints of this volume"""
metrics = base.Field(['Oem', 'Intel_RackScale', 'Metrics'],
adapter=rsd_lib_utils.get_resource_identity)
"""Link to telemetry metrics of this volume"""
class IdentifiersField(base.ListField):
durable_name = base.Field('DurableName')
durable_name_format = base.Field('DurableNameFormat')
class ReplicaInfosField(base.ListField):
replica_readonly_access = base.Field('ReplicaReadOnlyAccess')
replica_type = base.Field('ReplicaType')
replica_role = base.Field('ReplicaRole')
replica = base.Field('Replica',
adapter=rsd_lib_utils.get_resource_identity)
class InitializeActionField(base.CompositeField):
target_uri = base.Field('target', required=True)
class VolumeActionsField(base.CompositeField):
initialize = InitializeActionField('#Volume.Initialize')
class Volume(rsd_lib_base.ResourceBase):
identity = base.Field('Id', required=True)
"""The volume identity string"""
description = base.Field('Description')
"""The volume description string"""
name = base.Field('Name')
"""The volume name string"""
model = base.Field('Model')
"""The volume model"""
manufacturer = base.Field('Manufacturer')
"""The volume manufacturer"""
access_capabilities = base.Field('AccessCapabilities')
"""The access capabilities of volume"""
capacity_bytes = base.Field('CapacityBytes',
adapter=rsd_lib_utils.num_or_none)
"""The capacity of volume in bytes"""
allocated_Bytes = base.Field(['Capacity', 'Data', 'AllocatedBytes'],
adapter=rsd_lib_utils.num_or_none)
"""The allocated capacity of volume in bytes"""
capacity_sources = CapacitySourcesField('CapacitySources')
"""The logical drive status"""
identifiers = IdentifiersField('Identifiers')
"""These identifiers list of this volume"""
links = LinksField('Links')
"""These links to related components of this volume"""
replica_infos = ReplicaInfosField('ReplicaInfos')
"""These replica related info of this volume"""
status = rsd_lib_common.StatusField('Status')
"""The volume status"""
bootable = base.Field(['Oem', 'Intel_RackScale', 'Bootable'], adapter=bool)
"""The bootable info of this volume"""
erased = base.Field(['Oem', 'Intel_RackScale', 'Erased'])
"""The erased info of this volume"""
erase_on_detach = base.Field(['Oem', 'Intel_RackScale', 'EraseOnDetach'],
adapter=bool)
"""The rrase on detach info of this volume"""
_actions = VolumeActionsField('Actions', required=True)
def update(self, bootable=None, erased=None):
"""Update volume properties
:param bootable: Change bootable ability of the volume
:param erased: Provide information if the drive was erased
:raises: BadRequestError if at least one param isn't specified
"""
if bootable is None and erased is None:
raise ValueError('At least "bootable" or "erased" parameter has '
'to be specified')
if bootable and not isinstance(bootable, bool):
raise exceptions.InvalidParameterValueError(
parameter='bootable', value=bootable,
valid_values=[True, False])
if erased and not isinstance(erased, bool):
raise exceptions.InvalidParameterValueError(
parameter='erased', value=erased,
valid_values=[True, False])
data = {'Oem': {'Intel_RackScale': {}}}
if bootable is not None:
data['Oem']['Intel_RackScale']['Bootable'] = bootable
if erased is not None:
data['Oem']['Intel_RackScale']['Erased'] = erased
self._conn.patch(self.path, data=data)
def _get_initialize_action_element(self):
initialize_action = self._actions.initialize
if not initialize_action:
raise exceptions.MissingActionError(
action='#Volume.Initialize',
resource=self._path)
return initialize_action
def initialize(self, init_type):
"""Change initialize type of this volume
:param type: volume initialize type
:raises: InvalidParameterValueError if invalid "type" parameter
"""
allowed_init_type_values = ['Fast', 'Slow']
if init_type not in allowed_init_type_values:
raise exceptions.InvalidParameterValueError(
parameter='init_type', value=init_type,
valid_values=allowed_init_type_values)
data = {"InitializeType": init_type}
target_uri = self._get_initialize_action_element().target_uri
self._conn.post(target_uri, data=data)
def _get_metrics_path(self):
"""Helper function to find the Metrics path"""
return utils.get_sub_resource_path_by(self,
['Links',
'Oem',
'Intel_RackScale',
'Metrics'])
@property
@utils.cache_it
def metrics(self):
"""Property to provide reference to `Metrics` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
return volume_metrics.VolumeMetrics(
self._conn, self._get_metrics_path(),
redfish_version=self.redfish_version)
class VolumeCollection(rsd_lib_base.ResourceCollectionBase):
@property
def _resource_type(self):
return Volume
def _create_volume_request(self, capacity, access_capabilities=None,
capacity_sources=None, replica_infos=None,
bootable=None):
request = {}
jsonschema.validate(capacity,
volume_schemas.capacity_req_schema)
request['CapacityBytes'] = capacity
if access_capabilities is not None:
jsonschema.validate(
access_capabilities,
volume_schemas.access_capabilities_req_schema)
request['AccessCapabilities'] = access_capabilities
if capacity_sources is not None:
jsonschema.validate(capacity_sources,
volume_schemas.capacity_sources_req_schema)
request['CapacitySources'] = capacity_sources
if replica_infos is not None:
jsonschema.validate(replica_infos,
volume_schemas.replica_infos_req_schema)
request['ReplicaInfos'] = replica_infos
if bootable is not None:
jsonschema.validate(bootable,
volume_schemas.bootable_req_schema)
request['Oem'] = {"Intel_RackScale": {"Bootable": bootable}}
return request
def create_volume(self, capacity, access_capabilities=None,
capacity_sources=None, replica_infos=None,
bootable=None):
"""Create a new volume
:param capacity: Requested volume capacity in bytes
:param access_capabilities: List of volume access capabilities
:param capacity_sources: JSON for volume providing source
:param replica_infos: JSON for volume replica infos
:param bootable: Determines if the volume should be bootable
:returns: The uri of the new volume
"""
properties = self._create_volume_request(
capacity=capacity, access_capabilities=access_capabilities,
capacity_sources=capacity_sources, replica_infos=replica_infos,
bootable=bootable)
resp = self._conn.post(self._path, data=properties)
LOG.info("Volume created at %s", resp.headers['Location'])
volume_url = resp.headers['Location']
return volume_url[volume_url.find(self._path):]