Lin Yang 609075345c Reformat all files with black auto formatter
Change-Id: I037b6b4a8d08862893060c5fe85865e9e11ac486
2019-09-11 16:36:53 -07:00

291 lines
9.2 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):]