Commit a5cc27bf by akhiljain23 Committed by Akhil jain

Add ExpEther Driver in Valence

This commit adds ExpEther driver support inside valence to be able
to manage NEC's ExpEther hardware.
This would fit into valence support of multi-podm architecture.

Change-Id: Ie8eb4d50e88d52aefc3732fd07cd8ed1ad5aee6f
Implements: blueprint add-expether-driver
parent ecd96514
......@@ -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