Support to create new volume

Change-Id: I909820d590a1f95fe4d3cd4a53ac021ac5dbc6d4
This commit is contained in:
Lin Yang 2018-04-11 12:45:20 -07:00
parent 3be4548f07
commit f04f4a743a
3 changed files with 223 additions and 0 deletions

View File

@ -13,12 +13,14 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import jsonschema
import logging import logging
from sushy import exceptions from sushy import exceptions
from sushy.resources import base from sushy.resources import base
from sushy import utils from sushy import utils
from rsd_lib.resources.v2_3.storage_service import volume_schemas
from rsd_lib import utils as rsd_lib_utils from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -207,3 +209,57 @@ class VolumeCollection(base.ResourceCollectionBase):
""" """
super(VolumeCollection, self).__init__(connector, path, super(VolumeCollection, self).__init__(connector, path,
redfish_version) redfish_version)
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):
"""Compose a node from RackScale hardware
: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):]

View File

@ -0,0 +1,66 @@
# Copyright (c) 2018 Intel, Corp.
#
# 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.
capacity_req_schema = {
'type': 'number'
}
access_capabilities_req_schema = {
'type': 'array',
'items': {
'type': 'string',
'enum': ['Read', 'Write', 'WriteOnce', 'Append', 'Streaming']
}
}
capacity_sources_req_schema = {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'ProvidingPools': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'@odata.id': {'type': 'string'}
},
'additionalProperties': False
}
}
},
'additionalProperties': False
}
}
replica_infos_req_schema = {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'ReplicaType': {'type': 'string'},
'Replica': {
'type': 'object',
'properties': {
'@odata.id': {'type': 'string'}
}
}
},
'additionalProperties': False
}
}
bootable_req_schema = {
'type': 'boolean'
}

View File

@ -14,12 +14,14 @@
# under the License. # under the License.
import json import json
import jsonschema
import mock import mock
import testtools import testtools
from sushy import exceptions from sushy import exceptions
from rsd_lib.resources.v2_3.storage_service import volume from rsd_lib.resources.v2_3.storage_service import volume
from rsd_lib.tests.unit.fakes import request_fakes
class StorageServiceTestCase(testtools.TestCase): class StorageServiceTestCase(testtools.TestCase):
@ -165,6 +167,10 @@ class VolumeCollectionTestCase(testtools.TestCase):
with open('rsd_lib/tests/unit/json_samples/v2_3/' with open('rsd_lib/tests/unit/json_samples/v2_3/'
'volume_collection.json', 'r') as f: 'volume_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read()) self.conn.get.return_value.json.return_value = json.loads(f.read())
self.conn.post.return_value = request_fakes.fake_request_post(
None, headers={"Location": "https://localhost:8443/redfish/v1/"
"StorageServices/NVMeoE1/Volumes/2"})
self.volume_col = volume.VolumeCollection( self.volume_col = volume.VolumeCollection(
self.conn, '/redfish/v1/StorageServices/NVMeoE1/Volumes', self.conn, '/redfish/v1/StorageServices/NVMeoE1/Volumes',
redfish_version='1.0.2') redfish_version='1.0.2')
@ -195,3 +201,98 @@ class VolumeCollectionTestCase(testtools.TestCase):
redfish_version=self.volume_col.redfish_version) redfish_version=self.volume_col.redfish_version)
self.assertIsInstance(members, list) self.assertIsInstance(members, list)
self.assertEqual(1, len(members)) self.assertEqual(1, len(members))
def test_create_volume(self):
reqs = {
"AccessCapabilities": [
"Read",
"Write"
],
"CapacityBytes": 10737418240,
"CapacitySources": [
{
"ProvidingPools": [
{
"@odata.id": "/redfish/v1/StorageServices/1/"
"StoragePools/2"
}
]
}
],
"ReplicaInfos": [
{
"ReplicaType": "Clone",
"Replica": {
"@odata.id": "/redfish/v1/StorageServices/NVMeoE1/"
"Volumes/1"
}
}
],
"Oem": {
"Intel_RackScale": {
"Bootable": True
}
}
}
result = self.volume_col.create_volume(
capacity=10737418240,
access_capabilities=["Read", "Write"],
capacity_sources=[{
"ProvidingPools": [{
"@odata.id": "/redfish/v1/StorageServices/1/StoragePools/2"
}]
}],
replica_infos=[{
"ReplicaType": "Clone",
"Replica": {
"@odata.id": "/redfish/v1/StorageServices/NVMeoE1/"
"Volumes/1"
}
}],
bootable=True)
self.volume_col._conn.post.assert_called_once_with(
'/redfish/v1/StorageServices/NVMeoE1/Volumes', data=reqs)
self.assertEqual(result,
'/redfish/v1/StorageServices/NVMeoE1/Volumes/2')
def test_create_volume_with_invalid_reqs(self):
self.assertRaises(jsonschema.exceptions.ValidationError,
self.volume_col.create_volume,
capacity='invalid_capacity')
result = self.volume_col.create_volume(capacity=1024)
self.assertEqual(result,
'/redfish/v1/StorageServices/NVMeoE1/Volumes/2')
invalid_access_capabilities = ["Write", "invalid"]
self.assertRaises(jsonschema.exceptions.ValidationError,
self.volume_col.create_volume,
capacity=1024,
access_capabilities=invalid_access_capabilities)
invalid_capacity_sources = [{
"ProvidingPools": {
"@odata.id": "/redfish/v1/StorageServices/1/StoragePools/2"
}
}]
self.assertRaises(jsonschema.exceptions.ValidationError,
self.volume_col.create_volume,
capacity=1024,
capacity_sources=invalid_capacity_sources)
invalid_replica_infos = [{
"Invalid": "Clone",
"Replica": {
"@odata.id": "/redfish/v1/StorageServices/NVMeoE1/"
"Volumes/1"
}
}]
self.assertRaises(jsonschema.exceptions.ValidationError,
self.volume_col.create_volume,
capacity=1024,
replica_infos=invalid_replica_infos)
self.assertRaises(jsonschema.exceptions.ValidationError,
self.volume_col.create_volume,
capacity=1024,
bootable="True")