Support to create new volume
Change-Id: I909820d590a1f95fe4d3cd4a53ac021ac5dbc6d4
This commit is contained in:
parent
3be4548f07
commit
f04f4a743a
@ -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):]
|
||||||
|
66
rsd_lib/resources/v2_3/storage_service/volume_schemas.py
Normal file
66
rsd_lib/resources/v2_3/storage_service/volume_schemas.py
Normal 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'
|
||||||
|
}
|
@ -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")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user