Merge "Add node composition request validation"
This commit is contained in:
commit
ef5ee75884
@ -4,3 +4,4 @@
|
||||
|
||||
pbr>=2.0 # Apache-2.0
|
||||
sushy>=0.1.0 # Apache-2.0
|
||||
validictory>=1.0.0
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import validictory
|
||||
|
||||
from sushy import exceptions
|
||||
from sushy.resources import base
|
||||
@ -23,6 +24,7 @@ from sushy import utils
|
||||
|
||||
from rsd_lib.resources.node import constants as node_cons
|
||||
from rsd_lib.resources.node import mappings as node_maps
|
||||
from rsd_lib.resources.node import schemas as node_schemas
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -389,13 +391,73 @@ class NodeCollection(base.ResourceCollectionBase):
|
||||
resource=self._path)
|
||||
return compose_action
|
||||
|
||||
def compose_node(self, properties={}):
|
||||
def _create_compose_request(self, name=None, description=None,
|
||||
processor_req=None, memory_req=None,
|
||||
remote_drive_req=None, local_drive_req=None,
|
||||
ethernet_interface_req=None):
|
||||
|
||||
request = {}
|
||||
|
||||
if name is not None:
|
||||
request['Name'] = name
|
||||
if description is not None:
|
||||
request['Description'] = description
|
||||
|
||||
if processor_req is not None:
|
||||
validictory.validate(processor_req,
|
||||
node_schemas.processor_req_schema,
|
||||
required_by_default=False)
|
||||
request['Processors'] = processor_req
|
||||
|
||||
if memory_req is not None:
|
||||
validictory.validate(memory_req,
|
||||
node_schemas.memory_req_schema,
|
||||
required_by_default=False)
|
||||
request['Memory'] = memory_req
|
||||
|
||||
if remote_drive_req is not None:
|
||||
validictory.validate(remote_drive_req,
|
||||
node_schemas.remote_drive_req_schema,
|
||||
required_by_refault=False)
|
||||
request['RemoteDrives'] = remote_drive_req
|
||||
|
||||
if local_drive_req is not None:
|
||||
validictory.validate(local_drive_req,
|
||||
node_schemas.local_drive_req_schema,
|
||||
required_by_default=False)
|
||||
request['LocalDrives'] = local_drive_req
|
||||
|
||||
if ethernet_interface_req is not None:
|
||||
validictory.validate(ethernet_interface_req,
|
||||
node_schemas.ethernet_interface_req_schema,
|
||||
required_by_default=False)
|
||||
request['EthernetInterfaces'] = ethernet_interface_req
|
||||
|
||||
return request
|
||||
|
||||
def compose_node(self, name=None, description=None,
|
||||
processor_req=None, memory_req=None,
|
||||
remote_drive_req=None, local_drive_req=None,
|
||||
ethernet_interface_req=None):
|
||||
"""Compose a node from RackScale hardware
|
||||
|
||||
:param properties: The properties requested for node composition
|
||||
:param name: Name of node
|
||||
:param description: Description of node
|
||||
:param processor_req: JSON for node processors
|
||||
:param memory_req: JSON for node memory modules
|
||||
:param remote_drive_req: JSON for node remote drives
|
||||
:param local_drive_req: JSON for node local drives
|
||||
:param ethernet_interface_req: JSON for node ethernet ports
|
||||
:returns: The location of the composed node
|
||||
"""
|
||||
target_uri = self._get_compose_action_element().target_uri
|
||||
properties = self._create_compose_request(
|
||||
name=name, description=description,
|
||||
processor_req=processor_req,
|
||||
memory_req=memory_req,
|
||||
remote_drive_req=remote_drive_req,
|
||||
local_drive_req=local_drive_req,
|
||||
ethernet_interface_req=ethernet_interface_req)
|
||||
resp = self._conn.post(target_uri, data=properties)
|
||||
LOG.info("Node created at %s", resp.headers['Location'])
|
||||
node_url = resp.headers['Location']
|
||||
|
173
rsd_lib/resources/node/schemas.py
Normal file
173
rsd_lib/resources/node/schemas.py
Normal file
@ -0,0 +1,173 @@
|
||||
# Copyright (c) 2017 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.
|
||||
|
||||
processor_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'Model': {'type': 'string'},
|
||||
'TotalCores': {'type': 'number'},
|
||||
'AchievableSpeedMHz': {'type': 'number'},
|
||||
'InstructionSet': {
|
||||
'type': 'string',
|
||||
'enum': ['x86', 'x86-64', 'IA-64', 'ARM-A32',
|
||||
'ARM-A64', 'MIPS32', 'MIPS64', 'OEM']
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
}
|
||||
|
||||
memory_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityMiB': {'type': 'number'},
|
||||
'MemoryDeviceType': {
|
||||
'type': 'string',
|
||||
'enum': ['DDR', 'DDR2', 'DDR3', 'DDR4', 'DDR4_SDRAM',
|
||||
'DDR4E_SDRAM', 'LPDDR4_SDRAM', 'DDR3_SDRAM',
|
||||
'LPDDR3_SDRAM', 'DDR2_SDRAM', 'DDR2_SDRAM_FB_DIMM',
|
||||
'DDR2_SDRAM_FB_DIMM_PROBE', 'DDR_SGRAM',
|
||||
'DDR_SDRAM', 'ROM', 'SDRAM', 'EDO',
|
||||
'FastPageMode', 'PipelinedNibble']
|
||||
},
|
||||
'SpeedMHz': {'type': 'number'},
|
||||
'Manufacturer': {'type': 'string'},
|
||||
'DataWidthBits': {'type': 'number'},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
}
|
||||
|
||||
remote_drive_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityGiB': {'type': 'number'},
|
||||
'iSCSIAddress': {'type': 'string'},
|
||||
'Master': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'Type': {
|
||||
'type': 'string',
|
||||
'enum': ['Snapshot', 'Clone']
|
||||
},
|
||||
'Address': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
}
|
||||
|
||||
local_drive_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'CapacityGiB': {'type': 'number'},
|
||||
'Type': {
|
||||
'type': 'string',
|
||||
'enum': ['HDD', 'SSD']
|
||||
},
|
||||
'MinRPM': {'type': 'number'},
|
||||
'SerialNumber': {'type': 'string'},
|
||||
'Interface': {
|
||||
'type': 'string',
|
||||
'enum': ['SAS', 'SATA', 'NVMe']
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'FabricSwitch': {'type': 'boolean'}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
}
|
||||
|
||||
ethernet_interface_req_schema = {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'SpeedMbps': {'type': 'number'},
|
||||
'PrimaryVLAN': {'type': 'number'},
|
||||
'VLANs': {
|
||||
'type': 'array',
|
||||
'additionalItems': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'VLANId': {'type': 'number'},
|
||||
'Tagged': {'type': 'boolean'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'Resource': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'Chassis': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'@odata.id': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}]
|
||||
}
|
@ -14,11 +14,12 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
import validictory
|
||||
|
||||
from sushy import exceptions
|
||||
from sushy.resources.system import system
|
||||
import testtools
|
||||
|
||||
from rsd_lib.resources.node import constants as node_cons
|
||||
from rsd_lib.resources.node import node
|
||||
@ -392,24 +393,32 @@ class NodeCollectionTestCase(testtools.TestCase):
|
||||
self.assertEqual('/redfish/v1/Nodes/Actions/Allocate',
|
||||
value.target_uri)
|
||||
|
||||
def test_compose_node_no_properties(self):
|
||||
def test_compose_node_no_reqs(self):
|
||||
result = self.node_col.compose_node()
|
||||
self.node_col._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Actions/Allocate', data={})
|
||||
self.assertEqual(result, '/redfish/v1/Nodes/1')
|
||||
|
||||
def test_compose_node_properties(self):
|
||||
props = {
|
||||
def test_compose_node_reqs(self):
|
||||
reqs = {
|
||||
'Name': 'test',
|
||||
'Description': 'this is a test node',
|
||||
'Processors': [{
|
||||
'TotalCores': 2
|
||||
'TotalCores': 4
|
||||
}],
|
||||
'Memory': [{
|
||||
'CapacityMiB': 16000
|
||||
'CapacityMiB': 8000
|
||||
}]
|
||||
}
|
||||
result = self.node_col.compose_node(properties=props)
|
||||
result = self.node_col.compose_node(
|
||||
name='test', description='this is a test node',
|
||||
processor_req=[{'TotalCores': 4}],
|
||||
memory_req=[{'CapacityMiB': 8000}])
|
||||
self.node_col._conn.post.assert_called_once_with(
|
||||
'/redfish/v1/Nodes/Actions/Allocate', data=props)
|
||||
'/redfish/v1/Nodes/Actions/Allocate', data=reqs)
|
||||
self.assertEqual(result, '/redfish/v1/Nodes/1')
|
||||
|
||||
def test_compose_node_invalid_reqs(self):
|
||||
self.assertRaises(validictory.validator.FieldValidationError,
|
||||
self.node_col.compose_node,
|
||||
processor_req='invalid')
|
||||
|
Loading…
x
Reference in New Issue
Block a user