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
# under the License.
import jsonschema
import logging
from sushy import exceptions
from sushy.resources import base
from sushy import utils
from rsd_lib.resources.v2_3.storage_service import volume_schemas
from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__)
@ -207,3 +209,57 @@ class VolumeCollection(base.ResourceCollectionBase):
"""
super(VolumeCollection, self).__init__(connector, path,
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.
import json
import jsonschema
import mock
import testtools
from sushy import exceptions
from rsd_lib.resources.v2_3.storage_service import volume
from rsd_lib.tests.unit.fakes import request_fakes
class StorageServiceTestCase(testtools.TestCase):
@ -165,6 +167,10 @@ class VolumeCollectionTestCase(testtools.TestCase):
with open('rsd_lib/tests/unit/json_samples/v2_3/'
'volume_collection.json', 'r') as f:
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.conn, '/redfish/v1/StorageServices/NVMeoE1/Volumes',
redfish_version='1.0.2')
@ -195,3 +201,98 @@ class VolumeCollectionTestCase(testtools.TestCase):
redfish_version=self.volume_col.redfish_version)
self.assertIsInstance(members, list)
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")