Get base resource urls through @odata.id

Currently the redfish driver is assuming that base
resources like systems and nodes exist at '/redfish/v1/Systems'
and '/redfish/v1/Nodes', but this isn't always the case. This
patch gets the urls of these resources through their @odata.id
entries from the service root to ensure they're correct.

Change-Id: I795d007f6dc3e029be17390e33ebe49232d83e13
Closes-bug: #1645957
This commit is contained in:
Nate Potter 2016-12-02 14:25:12 -08:00
parent 05e38cf039
commit 1339877bda
5 changed files with 76 additions and 20 deletions

View File

@ -17,4 +17,4 @@ bind_port = 8181
url=http://<ip address>
user=<podm user>
password=<podm admin>
redfish_base_ext=/redfish/v1/

View File

@ -66,3 +66,4 @@ debug = get_option("DEFAULT", "debug", False, bool)
podm_url = get_option("podm", "url", "http://127.0.0.1")
podm_user = get_option("podm", "user", "admin")
podm_password = get_option("podm", "password", "admin")
redfish_base_ext = get_option("podm", "redfish_base_ext", "/redfish/v1/")

View File

@ -25,17 +25,31 @@ from valence.redfish import tree
LOG = logging.getLogger(__name__)
SERVICE_ROOT = None
def update_service_root():
global SERVICE_ROOT
resp = send_request(cfg.redfish_base_ext)
SERVICE_ROOT = resp.json()
def get_rfs_url(serviceext):
REDFISH_BASE_EXT = "/redfish/v1/"
if REDFISH_BASE_EXT in serviceext:
if cfg.redfish_base_ext in serviceext:
relative_url = serviceext
else:
relative_url = os.path.join(REDFISH_BASE_EXT, serviceext)
relative_url = os.path.join(cfg.redfish_base_ext, serviceext)
return requests.compat.urljoin(cfg.podm_url, relative_url)
def get_base_resource_url(resource, update_services=False):
if update_services or not SERVICE_ROOT:
LOG.debug("Updating service root...")
update_service_root()
resource_url = SERVICE_ROOT[resource]["@odata.id"]
return resource_url
def send_request(resource, method="GET", **kwargs):
# The verify=false param in the request should be removed eventually
url = get_rfs_url(resource)
@ -95,16 +109,16 @@ def generic_filter(jsonContent, filterConditions):
def racks():
resp = send_request('Chassis')
jsonContent = resp.json()
racks = filter_chassis(jsonContent, 'Rack')
chassis_url = get_base_resource_url("Chassis")
jsonContent = send_request(chassis_url)
racks = filter_chassis(jsonContent, "Rack")
return json.dumps(racks)
def pods():
resp = send_request('Chassis')
jsonContent = resp.json()
pods = filter_chassis(jsonContent, 'Pod')
chassis_url = get_base_resource_url("Chassis")
jsonContent = send_request(chassis_url)
pods = filter_chassis(jsonContent, "Pod")
return json.dumps(pods)
@ -184,7 +198,8 @@ def node_storage_details(nodeurl):
def systems_list(filters={}):
# list of nodes with hardware details needed for flavor creation
lst_systems = []
systemurllist = urls2list("Systems")
systems_url = get_base_resource_url("Systems")
systemurllist = urls2list(systems_url)
podmtree = build_hierarchy_tree()
LOG.info(systemurllist)
for lnk in systemurllist:
@ -233,7 +248,8 @@ def systems_list(filters={}):
def get_chassis_list():
chassis_lnk_lst = urls2list("Chassis")
chassis_url = get_base_resource_url("Chassis")
chassis_lnk_lst = urls2list(chassis_url)
lst_chassis = []
for clnk in chassis_lnk_lst:
@ -295,21 +311,24 @@ def build_hierarchy_tree():
def compose_node(data):
composeurl = "Nodes/Actions/Allocate"
nodes_url = get_base_resource_url("Nodes")
compose_url = nodes_url + "/Actions/Allocate"
headers = {'Content-type': 'application/json'}
criteria = data["criteria"]
if not criteria:
resp = send_request(composeurl, "POST", headers=headers)
resp = send_request(compose_url, "POST", headers=headers)
else:
resp = send_request(composeurl, "POST", json=criteria, headers=headers)
resp = send_request(compose_url, "POST", json=criteria,
headers=headers)
composednode = resp.headers['Location']
return {"node": composednode}
composed_node = resp.headers['Location']
return {"node": composed_node}
def delete_composednode(nodeid):
deleteurl = "Nodes/" + str(nodeid)
resp = send_request(deleteurl, "DELETE")
nodes_url = get_base_resource_url("Nodes")
delete_url = nodes_url + str(nodeid)
resp = send_request(delete_url, "DELETE")
return resp
@ -317,7 +336,8 @@ def nodes_list(filters={}):
# list of nodes with hardware details needed for flavor creation
LOG.debug(filters)
lst_nodes = []
nodeurllist = urls2list("Nodes")
nodes_url = get_base_resource_url("Nodes")
nodeurllist = urls2list(nodes_url)
# podmtree = build_hierarchy_tree()
# podmtree.writeHTML("0","/tmp/a.html")

View File

@ -24,6 +24,33 @@ def mock_request_get(json_data, status_code):
return MockResponse(json_data, status_code)
def fake_service_root():
return {
"@odata.id": "/redfish/v1",
"Id": "ServiceRoot",
"Name": "Service root",
"RedfishVersion": "1.0.0",
"Chassis": {
"@odata.id": "/redfish/v1/Chassis"
},
"Services": {
"@odata.id": "/redfish/v1/Services"
},
"Systems": {
"@odata.id": "/redfish/v1/Systems"
},
"Managers": {
"@odata.id": "/redfish/v1/Managers"
},
"Nodes": {
"@odata.id": "/redfish/v1/Nodes"
},
"EthernetSwitches": {
"@odata.id": "/redfish/v1/EthernetSwitches"
}
}
def fake_chassis_list():
return [
{

View File

@ -30,6 +30,14 @@ class TestRedfish(TestCase):
result = redfish.get_rfs_url("/redfish/v1/Systems/1")
self.assertEqual(expected, result)
@mock.patch('valence.redfish.redfish.send_request')
def test_get_base_resource_url_chassis(self, mock_request):
fake_resp = fakes.mock_request_get(fakes.fake_service_root(), "200")
mock_request.return_value = fake_resp
expected = "/redfish/v1/Chassis"
result = redfish.get_base_resource_url("Chassis")
self.assertEqual(expected, result)
@mock.patch('requests.request')
def test_send_request(self, mock_request):
mock_request.return_value = "fake_node"