Пример #1
0
def compose_node(name, description, requirements):
    """Compose new node through podm api.

    :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')
    if resp.status_code != http_client.OK:
        LOG.error('Unable to query ' + nodes_url)
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)
    respdata = resp.json()
    allocate_url = respdata['Actions']['#ComposedNodeCollection.Allocate'][
        'target']

    # Allocate resource to this node
    LOG.debug('Allocating Node: {0}'.format(request_body))
    allocate_resp = send_request(allocate_url,
                                 'POST',
                                 headers={'Content-type': 'application/json'},
                                 json=request_body)
    if allocate_resp.status_code != http_client.CREATED:
        # Raise exception if allocation failed
        raise exception.RedfishException(allocate_resp.json(),
                                         status_code=allocate_resp.status_code)

    # Allocated node successfully
    # node_url -- relative redfish url e.g redfish/v1/Nodes/1
    node_url = allocate_resp.headers['Location'].lstrip(CONF.podm.url)
    # node_index -- numeric index of new node e.g 1
    node_index = node_url.split('/')[-1]
    LOG.debug('Successfully allocated node:' + node_url)

    # Get url of assembling node
    resp = send_request(node_url, "GET")
    respdata = resp.json()
    assemble_url = respdata['Actions']['#ComposedNode.Assemble']['target']

    # Assemble node
    LOG.debug('Assembling Node: {0}'.format(assemble_url))
    assemble_resp = send_request(assemble_url, "POST")

    if assemble_resp.status_code != http_client.NO_CONTENT:
        # Delete node if assemble failed
        delete_composed_node(node_index)
        raise exception.RedfishException(assemble_resp.json(),
                                         status_code=resp.status_code)
    else:
        # Assemble successfully
        LOG.debug('Successfully assembled node: ' + node_url)

    # Return new composed node index
    return get_node_by_id(node_index)
Пример #2
0
def set_boot_source(nodeid, request):
    nodes_url = get_base_resource_url("Nodes")
    node_url = os.path.normpath("/".join([nodes_url, nodeid]))
    resp = send_request(node_url)

    if resp.status_code != http_client.OK:
        # Raise exception if don't find node
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)

    node = resp.json()

    boot_enabled = request.get("Boot", {}).get("Enabled")
    boot_target = request.get("Boot", {}).get("Target")
    allowable_boot_target = \
        node["Boot"]["*****@*****.**"]

    if not boot_enabled or not boot_target:
        raise exception.BadRequest(
            detail="The content of set boot source request is malformed. "
            "Please refer to Valence api specification to correct it.")
    if boot_enabled not in ["Disabled", "Once", "Continuous"]:
        raise exception.BadRequest(
            detail="The parameter Enabled '{0}' is not in allowable list "
            "['Disabled', 'Once', 'Continuous'].".format(boot_enabled))
    if allowable_boot_target and \
            boot_target not in allowable_boot_target:
        raise exception.BadRequest(
            detail="The parameter Target '{0}' is not in allowable list "
            "{1}.".format(boot_target, allowable_boot_target))

    action_resp = send_request(node_url,
                               'PATCH',
                               headers={'Content-type': 'application/json'},
                               json={
                                   "Boot": {
                                       "BootSourceOverrideEnabled":
                                       boot_enabled,
                                       "BootSourceOverrideTarget": boot_target
                                   }
                               })

    if action_resp.status_code != http_client.NO_CONTENT:
        raise exception.RedfishException(action_resp.json(),
                                         status_code=action_resp.status_code)
    else:
        # Set boot source successfully
        LOG.debug("Set boot source of composed node {0} to '{1}' with enabled "
                  "state '{2}' successfully.".format(nodes_url, boot_target,
                                                     boot_enabled))
        return exception.confirmation(
            confirm_code="Set Boot Source of Composed Node",
            confirm_detail="The boot source of composed node has been set to "
            "'{0}' with enabled state '{1}' successfully.".format(
                boot_target, boot_enabled))
Пример #3
0
def reset_node(nodeid, request):
    nodes_url = get_base_resource_url("Nodes")
    node_url = os.path.normpath("/".join([nodes_url, nodeid]))
    resp = send_request(node_url)

    if resp.status_code != http_client.OK:
        # Raise exception if don't find node
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)

    node = resp.json()

    action_type = request.get("Reset", {}).get("Type")
    allowable_actions = node["Actions"]["#ComposedNode.Reset"][
        "*****@*****.**"]

    if not action_type:
        raise exception.BadRequest(
            detail="The content of node action request is malformed. Please "
            "refer to Valence api specification to correct it.")
    if allowable_actions and action_type not in allowable_actions:
        raise exception.BadRequest(
            detail="Action type '{0}' is not in allowable action list "
            "{1}.".format(action_type, allowable_actions))

    target_url = node["Actions"]["#ComposedNode.Reset"]["target"]

    action_resp = send_request(target_url,
                               'POST',
                               headers={'Content-type': 'application/json'},
                               json={"ResetType": action_type})

    if action_resp.status_code != http_client.NO_CONTENT:
        raise exception.RedfishException(action_resp.json(),
                                         status_code=action_resp.status_code)
    else:
        # Reset node successfully
        LOG.debug("Post action '{0}' to node {1} successfully.".format(
            action_type, target_url))
        return exception.confirmation(
            confirm_code="Reset Composed Node",
            confirm_detail="This composed node has been set to '{0}' "
            "successfully.".format(action_type))
Пример #4
0
def delete_composed_node(nodeid):
    nodes_url = get_base_resource_url("Nodes")
    delete_url = nodes_url + '/' + str(nodeid)
    resp = send_request(delete_url, "DELETE")
    if resp.status_code == http_client.NO_CONTENT:
        # we should return 200 status code instead of 204, because 204 means
        # 'No Content', the message in resp_dict will be ignored in that way
        return exception.confirmation(
            confirm_code="DELETED",
            confirm_detail="This composed node has been deleted successfully.")
    else:
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)
Пример #5
0
def show_ram_details(ram_url):
    """Get memory details .

    :param ram_url: relative redfish url to memory,
                    e.g /redfish/v1/Systems/1/Memory/1.
    :returns: dict of memory detail.
    """
    resp = send_request(ram_url)
    if resp.status_code != http_client.OK:
        # Raise exception if don't find memory
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)
    respdata = resp.json()
    ram_details = {
        "data_width_bit": respdata.get("DataWidthBits"),
        "speed_mhz": respdata.get("OperatingSpeedMhz"),
        "total_memory_mb": respdata.get("CapacityMiB")
    }

    return ram_details
Пример #6
0
def show_network_details(network_url):
    """Get network interface details .

    :param ram_url: relative redfish url to network interface,
                    e.g /redfish/v1/Systems/1/EthernetInterfaces/1.
    :returns: dict of network interface detail.
    """
    resp = send_request(network_url)
    if resp.status_code != http_client.OK:
        # Raise exception if don't find network interface
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)
    respdata = resp.json()
    network_details = {
        "speed_mbps":
        respdata.get("SpeedMbps"),
        "mac":
        respdata.get("MACAddress"),
        "status":
        respdata.get("Status", {}).get("State"),
        "ipv4": [{
            "address": ipv4.get("Address"),
            "subnet_mask": ipv4.get("SubnetMask"),
            "gateway": ipv4.get("Gateway")
        } for ipv4 in respdata.get("IPv4Addresses", [])]
    }

    if respdata.get("VLANs"):
        # Get vlan info
        vlan_url_list = urls2list(respdata.get("VLANs", {}).get("@odata.id"))
        network_details["vlans"] = []
        for url in vlan_url_list:
            vlan_info = send_request(url).json()
            network_details["vlans"].append({
                "vlanid":
                vlan_info.get("VLANId"),
                "status":
                vlan_info.get("Status", {}).get("State")
            })

    return network_details
Пример #7
0
def show_cpu_details(cpu_url):
    """Get processor details .

    :param cpu_url: relative redfish url to processor,
                    e.g /redfish/v1/Systems/1/Processors/1.
    :returns: dict of processor detail.
    """
    resp = send_request(cpu_url)
    if resp.status_code != http_client.OK:
        # Raise exception if don't find processor
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)
    respdata = resp.json()
    cpu_details = {
        "instruction_set": respdata.get("InstructionSet"),
        "model": respdata.get("Model"),
        "speed_mhz": respdata.get("MaxSpeedMHz"),
        "total_core": respdata.get("TotalCores")
    }

    return cpu_details
Пример #8
0
def get_node_by_id(node_index, show_detail=True):
    """Get composed node details of specific index.

    :param node_index: numeric index of new composed node.
    :param show_detail: show more node detail when set to True.
    :returns: node detail info.
    """
    nodes_base_url = get_base_resource_url('Nodes')
    node_url = os.path.normpath('/'.join([nodes_base_url, node_index]))
    resp = send_request(node_url)

    LOG.debug(resp.status_code)
    if resp.status_code != http_client.OK:
        # Raise exception if don't find node
        raise exception.RedfishException(resp.json(),
                                         status_code=resp.status_code)

    respdata = resp.json()

    node_detail = {
        "name":
        respdata.get("Name"),
        "node_power_state":
        respdata.get("PowerState"),
        "links": [
            link.Link.make_link('self', flask.request.url_root,
                                'nodes/' + respdata.get("UUID"), '').as_dict(),
            link.Link.make_link('bookmark',
                                flask.request.url_root,
                                'nodes/' + respdata.get("UUID"),
                                '',
                                bookmark=True).as_dict()
        ]
    }

    if show_detail:
        node_detail.update({
            "index":
            node_index,
            "description":
            respdata.get("Description"),
            "node_state":
            respdata.get("ComposedNodeState"),
            "boot_source":
            respdata.get("Boot", {}).get("BootSourceOverrideTarget"),
            "target_boot_source":
            respdata.get("Boot", {}).get("BootSourceOverrideTarget"),
            "health_status":
            respdata.get("Status", {}).get("Health"),
            # TODO(lin.yang): "pooled_group_id" is used to check whether
            # resource can be assigned to composed node, which should be
            # supported after PODM API v2.1 released.
            "pooled_group_id":
            None,
            "metadata": {
                "processor": [
                    show_cpu_details(i.get("@odata.id"))
                    for i in respdata.get("Links", {}).get("Processors", [])
                ],
                "memory": [
                    show_ram_details(i.get("@odata.id"))
                    for i in respdata.get("Links", {}).get("Memory", [])
                ],
                "network": [
                    show_network_details(i.get("@odata.id"))
                    for i in respdata.get("Links", {}).get(
                        "EthernetInterfaces", [])
                ]
            },
            "computer_system":
            respdata.get("Links").get("ComputerSystem")
        })

    return node_detail