valence/valence/redfish/redfish.py
Seenivasan Gunabalan 830176bbc7 Added Error Response
Added Formating Error response and Confirmation Messages.
When an operation is successfull, its better to send confirmation
message in a standard format. Also when Errors are at Valence-PODM
connection it should be handled and returned as json as per schema
referred in openstack

Change-Id: Ic440caa2a1865ffa5dc95e08ad398e0ad58861a7
Closes-Bug: #1643123
2016-12-09 11:35:27 +05:30

379 lines
13 KiB
Python

#!/usr/bin/env python
# Copyright (c) 2016 Intel, Inc.
#
# 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 json
import logging
import os
import requests
from requests.auth import HTTPBasicAuth
from valence.common import exception
from valence.common import utils
from valence import config as cfg
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):
if cfg.redfish_base_ext in serviceext:
relative_url = serviceext
else:
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)
httpuser = cfg.podm_user
httppwd = cfg.podm_password
resp = None
LOG.debug(url)
try:
resp = requests.request(method, url, verify=False,
auth=HTTPBasicAuth(httpuser, httppwd),
**kwargs)
except requests.exceptions.RequestException as e:
LOG.error(e)
return resp
def filter_chassis(jsonContent, filterCondition):
returnJSONObj = {}
returnMembers = []
members = jsonContent['Members']
for member in members:
resource = member['@odata.id']
resp = send_request(resource)
memberJsonObj = resp.json()
chassisType = memberJsonObj['ChassisType']
if chassisType == filterCondition:
returnMembers.append(member)
returnJSONObj["Members"] = returnMembers
returnJSONObj["Members@odata.count"] = len(returnMembers)
return returnJSONObj
def racks():
chassis_url = get_base_resource_url("Chassis")
jsonContent = send_request(chassis_url)
racks = filter_chassis(jsonContent, "Rack")
return json.dumps(racks)
def pods():
chassis_url = get_base_resource_url("Chassis")
jsonContent = send_request(chassis_url)
pods = filter_chassis(jsonContent, "Pod")
return json.dumps(pods)
def urls2list(url):
# This will extract the url values from @odata.id inside Members
resp = send_request(url)
respdata = resp.json()
if 'Members' in respdata:
return [u['@odata.id'] for u in respdata['Members']]
else:
return []
def node_cpu_details(nodeurl):
cpucnt = 0
cpuarch = ""
cpumodel = ""
cpulist = urls2list(nodeurl + '/Processors')
for lnk in cpulist:
LOG.info("Processing CPU %s" % lnk)
resp = send_request(lnk)
respdata = resp.json()
# Check if CPU data is populated. It also may have NULL values
cpucnt += utils.extract_val(respdata, "TotalCores", 0)
cpuarch = utils.extract_val(respdata, "InstructionSet", "")
cpumodel = utils.extract_val(respdata, "Model", "")
LOG.debug(" Cpu details %s: %d: %s: %s "
% (nodeurl, cpucnt, cpuarch, cpumodel))
return {"cores": str(cpucnt), "arch": cpuarch, "model": cpumodel}
def node_ram_details(nodeurl):
# this extracts the RAM and returns as dictionary
resp = send_request(nodeurl)
respjson = resp.json()
ram = utils.extract_val(respjson,
"MemorySummary/TotalSystemMemoryGiB", "0")
return str(ram)
def node_nw_details(nodeurl):
# this extracts the total nw interfaces and returns as a string
resp = send_request(nodeurl + "/EthernetInterfaces")
respbody = resp.json()
nwi = str(utils.extract_val(respbody, "Members@odata.count", "0"))
LOG.debug(" Total NW for node %s : %s " % (nodeurl, nwi))
return nwi
def node_storage_details(nodeurl):
# this extracts the RAM and returns as dictionary
storagecnt = 0
hddlist = urls2list(nodeurl + "/SimpleStorage")
for lnk in hddlist:
resp = send_request(lnk)
respbody = resp.json()
hdds = utils.extract_val(respbody, "Devices")
if not hdds:
continue
for sd in hdds:
if "CapacityBytes" in sd:
if sd["CapacityBytes"] is not None:
storagecnt += sd["CapacityBytes"]
LOG.debug("Total storage for node %s : %d " % (nodeurl, storagecnt))
# to convert Bytes in to GB. Divide by 1073741824
return str(storagecnt / 1073741824).split(".")[0]
def systems_list(filters={}):
# list of nodes with hardware details needed for flavor creation
lst_systems = []
systems_url = get_base_resource_url("Systems")
systemurllist = urls2list(systems_url)
podmtree = build_hierarchy_tree()
LOG.info(systemurllist)
for lnk in systemurllist:
filterPassed = True
resp = send_request(lnk)
system = resp.json()
if any(filters):
filterPassed = utils.match_conditions(system, filters)
if not filterPassed:
continue
systemid = lnk.split("/")[-1]
systemuuid = system['UUID']
systemlocation = podmtree.getPath(lnk)
cpu = node_cpu_details(lnk)
ram = node_ram_details(lnk)
nw = node_nw_details(lnk)
storage = node_storage_details(lnk)
system = {"id": systemid, "cpu": cpu,
"ram": ram, "storage": storage,
"nw": nw, "location": systemlocation,
"uuid": systemuuid}
# filter based on RAM, CPU, NETWORK..etc
if 'ram' in filters:
filterPassed = (True
if int(ram) >= int(filters['ram'])
else False)
# filter based on RAM, CPU, NETWORK..etc
if 'nw' in filters:
filterPassed = (True
if int(nw) >= int(filters['nw'])
else False)
# filter based on RAM, CPU, NETWORK..etc
if 'storage' in filters:
filterPassed = (True
if int(storage) >= int(filters['storage'])
else False)
if filterPassed:
lst_systems.append(system)
return lst_systems
def get_chassis_list():
chassis_url = get_base_resource_url("Chassis")
chassis_lnk_lst = urls2list(chassis_url)
lst_chassis = []
for clnk in chassis_lnk_lst:
resp = send_request(clnk)
data = resp.json()
LOG.info(data)
if "Links" in data:
contains = []
containedby = {}
computersystems = []
linksdata = data["Links"]
if "Contains" in linksdata and linksdata["Contains"]:
for c in linksdata["Contains"]:
contains.append(c['@odata.id'].split("/")[-1])
if "ContainedBy" in linksdata and linksdata["ContainedBy"]:
odata = linksdata["ContainedBy"]['@odata.id']
containedby = odata.split("/")[-1]
if "ComputerSystems" in linksdata and linksdata["ComputerSystems"]:
for c in linksdata["ComputerSystems"]:
computersystems.append(c['@odata.id'])
name = data["ChassisType"] + ":" + data["Id"]
c = {"name": name,
"ChassisType": data["ChassisType"],
"ChassisID": data["Id"],
"Contains": contains,
"ContainedBy": containedby,
"ComputerSystems": computersystems}
lst_chassis.append(c)
return lst_chassis
def get_systembyid(systemid):
return systems_list({"Id": systemid})
def get_nodebyid(nodeid):
node = nodes_list({"Id": nodeid})
if not node:
raise exception.NotFound()
return node[0]
def build_hierarchy_tree():
# builds the tree sturcture of the PODM data to get the location hierarchy
lst_chassis = get_chassis_list()
podmtree = tree.Tree()
podmtree.add_node("0") # Add root node
for d in lst_chassis:
podmtree.add_node(d["ChassisID"], d)
for d in lst_chassis:
containedby = d["ContainedBy"] if d["ContainedBy"] else "0"
podmtree.add_node(d["ChassisID"], d, containedby)
systems = d["ComputerSystems"]
for system in systems:
sysname = system.split("/")[-2] + ":" + system.split("/")[-1]
podmtree.add_node(system, {"name": sysname}, d["ChassisID"])
return podmtree
def compose_node(data):
nodes_url = get_base_resource_url("Nodes")
compose_url = nodes_url + "/Actions/Allocate"
headers = {'Content-type': 'application/json'}
criteria = data["criteria"]
resp = send_request(compose_url, "POST", json=criteria, headers=headers)
if resp.status_code == 201:
composednode = resp.headers['Location']
return {"node": composednode}
else:
raise exception.RedfishException(resp.json(),
status_code=resp.status_code)
def delete_composednode(nodeid):
nodes_url = get_base_resource_url("Nodes")
delete_url = nodes_url + str(nodeid)
resp = send_request(delete_url, "DELETE")
if resp.status_code == 204:
return exception.confirmation("", "DELETED"), resp.status_code
else:
raise exception.RedfishException(resp.json(),
status_code=resp.status_code)
def nodes_list(filters={}):
# list of nodes with hardware details needed for flavor creation
LOG.debug(filters)
lst_nodes = []
nodes_url = get_base_resource_url("Nodes")
nodeurllist = urls2list(nodes_url)
# podmtree = build_hierarchy_tree()
# podmtree.writeHTML("0","/tmp/a.html")
for lnk in nodeurllist:
filterPassed = True
resp = send_request(lnk)
if resp.status_code != 200:
LOG.info("Error in fetching Node details " + lnk)
else:
node = resp.json()
if any(filters):
filterPassed = utils.match_conditions(node, filters)
LOG.info("FILTER PASSED" + str(filterPassed))
if not filterPassed:
continue
nodeid = lnk.split("/")[-1]
nodeuuid = node['UUID']
nodelocation = node['AssetTag']
# podmtree.getPath(lnk) commented as location should be
# computed using other logic.consult Chester
nodesystemurl = node["Links"]["ComputerSystem"]["@odata.id"]
cpu = {}
ram = 0
nw = 0
storage = node_storage_details(nodesystemurl)
cpu = node_cpu_details(lnk)
if "Memory" in node:
ram = node["Memory"]["TotalSystemMemoryGiB"]
if ("EthernetInterfaces" in node["Links"] and
node["Links"]["EthernetInterfaces"]):
nw = len(node["Links"]["EthernetInterfaces"])
bmcip = "127.0.0.1" # system['Oem']['Dell_G5MC']['BmcIp']
bmcmac = "00:00:00:00:00" # system['Oem']['Dell_G5MC']['BmcMac']
node = {"id": nodeid, "cpu": cpu,
"ram": ram, "storage": storage,
"nw": nw, "location": nodelocation,
"uuid": nodeuuid, "bmcip": bmcip, "bmcmac": bmcmac}
# filter based on RAM, CPU, NETWORK..etc
if 'ram' in filters:
filterPassed = (True
if int(ram) >= int(filters['ram'])
else False)
# filter based on RAM, CPU, NETWORK..etc
if 'nw' in filters:
filterPassed = (True
if int(nw) >= int(filters['nw'])
else False)
# filter based on RAM, CPU, NETWORK..etc
if 'storage' in filters:
filterPassed = (True
if int(storage) >= int(filters['storage'])
else False)
if filterPassed:
lst_nodes.append(node)
return lst_nodes