Commit 57815490 by Zuul Committed by Gerrit Code Review

Merge "Add ExpEther Driver in Valence"

parents d21701e1 a5cc27bf
......@@ -68,3 +68,4 @@ valence.provision.driver =
valence.podmanager.driver =
redfishv1 = valence.podmanagers.podm_base:PodManagerBase
expether = valence.podmanagers.expether_manager:ExpEtherManager
......@@ -17,3 +17,7 @@ PODM_AUTH_BASIC_TYPE = 'basic'
PODM_STATUS_ONLINE = 'Online'
PODM_STATUS_OFFLINE = 'Offline'
PODM_STATUS_UNKNOWN = "Unknown"
HTTP_HEADERS = {"Content-type": "application/json"}
DEVICE_STATES = {'ALLOCATED': 'allocated', 'FREE': 'free'}
......@@ -110,6 +110,10 @@ class RedfishException(ValenceError):
request_id)
class ExpEtherException(ValenceError):
_msg_fmt = "ExpEther Exception"
class ResourceExists(ValenceError):
status = http_client.CONFLICT
_msg_fmt = "Resource Already Exists"
......
# Copyright (c) 2017 NEC, 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.
import logging
import requests
from six.moves import http_client
LOG = logging.getLogger(__name__)
OK = http_client.OK
CREATED = http_client.CREATED
NO_CONTENT = http_client.NO_CONTENT
def get(url, http_auth, **kwargs):
try:
return requests.request('GET', url, verify=False, auth=http_auth,
**kwargs)
except requests.exceptions.RequestException as ex:
LOG.error(ex)
raise ex
def patch(url, http_auth, **kwargs):
try:
return requests.request('PATCH', url, verify=False, auth=http_auth,
**kwargs)
except requests.exceptions.RequestException as ex:
LOG.error(ex)
raise ex
def post(url, http_auth, data=None, **kwargs):
try:
return requests.request('POST', url, data=data, verify=False,
auth=http_auth, **kwargs)
except requests.exceptions.RequestException as ex:
LOG.error(ex)
raise ex
def delete(url, http_auth, **kwargs):
try:
return requests.request('DELETE', url, verify=False, auth=http_auth,
**kwargs)
except requests.exceptions.RequestException as ex:
LOG.error(ex)
raise ex
def put(url, http_auth, **kwargs):
headers = {"Content-Type": "application/json"}
try:
return requests.request('PUT', url, verify=False, headers=headers,
auth=http_auth, **kwargs)
except requests.exceptions.RequestException as ex:
LOG.error(ex)
raise ex
......@@ -49,32 +49,6 @@ class Node(object):
return {key: node_info[key] for key in node_info.keys()
if key in ["uuid", "name", "podm_id", "index", "resource_uri"]}
@staticmethod
def _create_compose_request(name, description, requirements):
request = {}
request["Name"] = name
request["Description"] = description
memory = {}
if "memory" in requirements:
if "capacity_mib" in requirements["memory"]:
memory["CapacityMiB"] = requirements["memory"]["capacity_mib"]
if "type" in requirements["memory"]:
memory["DimmDeviceType"] = requirements["memory"]["type"]
request["Memory"] = [memory]
processor = {}
if "processor" in requirements:
if "model" in requirements["processor"]:
processor["Model"] = requirements["processor"]["model"]
if "total_cores" in requirements["processor"]:
processor["TotalCores"] = (
requirements["processor"]["total_cores"])
request["Processors"] = [processor]
return request
def compose_node(self, request_body):
"""Compose new node
......@@ -97,10 +71,10 @@ class Node(object):
# "description" is optional
description = request_body.get("description", "")
compose_request = self._create_compose_request(name, description,
requirements)
composed_node = self.connection.compose_node(compose_request)
# Moving _create_compose_request to drivers as this can be
# vendor specific request
composed_node = self.connection.compose_node(name, description,
requirements)
composed_node["uuid"] = utils.generate_uuid()
# Only store the minimum set of composed node info into backend db,
......
......@@ -80,6 +80,12 @@ def delete_podmanager(uuid):
p_nodes = db_api.Connection.list_composed_nodes({'podm_id': uuid})
# Delete the nodes w.r.t podmanager from valence DB
for node in p_nodes:
nodes.Node(node['uuid']).delete_composed_node(node['uuid'])
nodes.Node(node['uuid']).delete_composed_node()
# Delete the devices w.r.t podmanager from valence DB
devices_list = db_api.Connection.list_devices(
filters={'podm_id': uuid})
for device in devices_list:
db_api.Connection.delete_device(device['uuid'])
return db_api.Connection.delete_podmanager(uuid)
......@@ -113,7 +113,7 @@ class PooledDevices(object):
db_api.Connection.add_device(dev)
response['status'] = 'SUCCESS'
except exception.ValenceException as e:
LOG.exception("Update devices failed with exception %s", str(e))
except exception.ValenceError:
LOG.exception("Failed to update resources from podm")
response['status'] = 'FAILED'
return response
......@@ -186,6 +186,12 @@ class Flavor(ModelBaseWithTimeStamp):
},
'validate': types.Dict.validate
},
'pci_device': {
'type': {
'validate': types.List.validate
},
'validate': types.Dict.validate
},
'validate': types.Dict.validate
}
}
......
......@@ -33,7 +33,7 @@ class PodManagerBase(object):
return self.get_resource_info_by_url(self.podm_url)
# TODO(): use rsd_lib here
def compose_node(self, request_body):
def compose_node(self, name, description, requirements):
pass
# TODO(): use rsd_lib here
......
......@@ -481,15 +481,49 @@ def build_hierarchy_tree():
return podmtree
def compose_node(request_body):
def _create_compose_request(name, description, requirements):
"""Generate compose node request following podm format
:param name: name of node
:param description: description of node if any
:param requirements: additional requirements of node if any
:return: request body to compose node
"""
request = {}
request["Name"] = name
request["Description"] = description
memory = {}
if "memory" in requirements:
if "capacity_mib" in requirements["memory"]:
memory["CapacityMiB"] = requirements["memory"]["capacity_mib"]
if "type" in requirements["memory"]:
memory["DimmDeviceType"] = requirements["memory"]["type"]
request["Memory"] = [memory]
processor = {}
if "processor" in requirements:
if "model" in requirements["processor"]:
processor["Model"] = requirements["processor"]["model"]
if "total_cores" in requirements["processor"]:
processor["TotalCores"] = (
requirements["processor"]["total_cores"])
request["Processors"] = [processor]
return request
def compose_node(name, description, requirements):
"""Compose new node through podm api.
:param request_body: The request content to compose new node, which should
follow podm format. Valence api directly pass it to
podm right now.
:param name: name of node
:param description: description of node if any
:param requirements: additional requirements of node if any
:returns: The numeric index of new composed node.
"""
request_body = _create_compose_request(name, description, requirements)
# Get url of allocating resource to node
nodes_url = get_base_resource_url('Nodes')
resp = send_request(nodes_url, 'GET')
......
......@@ -47,37 +47,6 @@ class TestAPINodes(unittest.TestCase):
self.assertEqual(expected,
nodes.Node._show_node_brief_info(node_info))
def test_create_compose_request(self):
name = "test_request"
description = "request for testing purposes"
requirements = {
"memory": {
"capacity_mib": "4000",
"type": "DDR3"
},
"processor": {
"model": "Intel",
"total_cores": "4"
}
}
expected = {
"Name": "test_request",
"Description": "request for testing purposes",
"Memory": [{
"CapacityMiB": "4000",
"DimmDeviceType": "DDR3"
}],
"Processors": [{
"Model": "Intel",
"TotalCores": "4"
}]
}
result = nodes.Node._create_compose_request(name,
description,
requirements)
self.assertEqual(expected, result)
@mock.patch("valence.db.api.Connection.create_composed_node")
@mock.patch("valence.common.utils.generate_uuid")
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
......@@ -119,7 +88,8 @@ class TestAPINodes(unittest.TestCase):
@mock.patch("valence.db.api.Connection.create_composed_node")
@mock.patch("valence.common.utils.generate_uuid")
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.compose_node")
def test_compose_node(self, mock_redfish_compose_node, mock_generate_uuid,
def test_compose_node(self, mock_redfish_compose_node,
mock_generate_uuid,
mock_db_create_composed_node):
"""Test compose node successfully"""
node_hw = node_fakes.get_test_composed_node()
......@@ -129,13 +99,13 @@ class TestAPINodes(unittest.TestCase):
"name": node_hw["name"],
"resource_uri": node_hw["resource_uri"]}
compose_request = {'name': 'fake_name',
'description': 'fake_description'}
mock_redfish_compose_node.return_value = node_hw
uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051'
mock_generate_uuid.return_value = uuid
result = self.node_controller.compose_node(
{"name": node_hw["name"],
"description": node_hw["description"]})
result = self.node_controller.compose_node(compose_request)
expected = nodes.Node._show_node_brief_info(node_hw)
self.assertEqual(expected, result)
......
......@@ -184,7 +184,7 @@ class TestPooledDevices(unittest.TestCase):
mock_device_list,
mock_pod_conn):
mock_device_list.return_value = [fakes.fake_device()]
mock_pod_conn.side_effect = exception.ValenceException('fake_detail')
mock_pod_conn.side_effect = exception.ValenceError('fake_detail')
result = pooled_devices.PooledDevices.update_device_info('podm_id')
expected = {'podm_id': 'podm_id', 'status': 'FAILED'}
self.assertEqual(result, expected)
def fake_eesv_list():
return {"devices": [{"id": "0x1111111111",
"status": "eesv",
"update_time": "1518999910510",
"mac_address": "11:11:11:11:11:11",
"group_id": "1234",
"type": "40g",
"power_status": "on",
"ee_version": "v1.0",
"device_id": "0x00000",
"serial_number": "abcd 01234",
"model": "ExpEther Board (40G)",
"max_eeio_count": "16",
"host_serial_number": "",
"host_model": "",
"notification_status0": ["up", "down"],
"notification_status1": ["down", "down"]},
{"id": "0x2222222222",
"status": "eesv",
"update_time": "1518999910510",
"mac_address": "22:22:22:22:22:22",
"group_id": "5678",
"type": "10g",
"power_status": "on",
"ee_version": "v1.0",
"device_id": "0x00000",
"serial_number": "abcd 01234",
"model": "ExpEther Board (10G)",
"max_eeio_count": "8",
"host_serial_number": "",
"host_model": "",
"notification_status0": ["up", "down"],
"notification_status1": ["down", "down"]}
],
"timestamp": "1521089295162"}
def fake_eesv():
return {"device": fake_eesv_list()['devices'][0],
"timestamp": "1521089295162"}
......@@ -25,6 +25,9 @@ def fake_flavor():
"processor": {
"total_cores": "2",
"model": "Intel"
},
"pci_device": {
"type": ["SSD", "NIC"]
}
}
}
......@@ -47,6 +50,9 @@ def fake_flavor_list():
"processor": {
"total_cores": "10",
"model": "Intel"
},
"pci_device": {
"type": ["NIC"]
}
}
},
......@@ -61,6 +67,9 @@ def fake_flavor_list():
"processor": {
"total_cores": "20",
"model": "Intel"
},
"pci_device": {
"type": ["SSD"]
}
}
},
......@@ -75,6 +84,9 @@ def fake_flavor_list():
"processor": {
"total_cores": "30",
"model": "Intel"
},
"pci_device": {
"type": ["SSD", "NIC"]
}
}
}
......
......@@ -345,7 +345,7 @@ class TestRedfish(TestCase):
fake_node_allocation_conflict]
with self.assertRaises(exception.RedfishException) as context:
redfish.compose_node({"name": "test_node"})
redfish.compose_node('test_node', '', {})
self.assertTrue("There are no computer systems available for this "
"allocation request." in str(context.exception.detail))
......@@ -381,7 +381,7 @@ class TestRedfish(TestCase):
fake_node_assemble_failed]
with self.assertRaises(exception.RedfishException):
redfish.compose_node({"name": "test_node"})
redfish.compose_node('test_node', '', {})
mock_delete_node.assert_called_once()
......@@ -416,7 +416,7 @@ class TestRedfish(TestCase):
fake_node_detail,
fake_node_assemble_failed]
redfish.compose_node({"name": "test_node"})
redfish.compose_node('test_node', '', {})
mock_delete_node.assert_not_called()
mock_get_node_by_id.assert_called_once()
......@@ -666,3 +666,33 @@ class TestRedfish(TestCase):
]
result = redfish.show_rack("2")
self.assertEqual(expected, result)
def test__create_compose_request(self):
name = "test_request"
description = "request for testing purposes"
requirements = {
"memory": {
"capacity_mib": "4000",
"type": "DDR3"
},
"processor": {
"model": "Intel",
"total_cores": "4"
}
}
expected = {
"Name": "test_request",
"Description": "request for testing purposes",
"Memory": [{
"CapacityMiB": "4000",
"DimmDeviceType": "DDR3"
}],
"Processors": [{
"Model": "Intel",
"TotalCores": "4"
}]
}
result = redfish._create_compose_request(name, description,
requirements)
self.assertEqual(expected, result)
......@@ -38,6 +38,13 @@ flavor_schema = {
},
'additionalProperties': False,
},
'pci_device': {
'type': 'object',
'properties': {
'type': {'type': 'array'}
},
'additionalProperties': False,
},
},
'additionalProperties': False,
},
......@@ -122,6 +129,13 @@ compose_node_with_properties = {
},
'additionalProperties': False,
},
'pci_device': {
'type': 'object',
'properties': {
'type': {'type': 'array'}
},
'additionalProperties': False,
},
},
'additionalProperties': False,
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment