Source code for ironic.drivers.modules.msftocs.msftocsclient

# Copyright 2015 Cloudbase Solutions Srl
# All Rights Reserved.
#
#    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.

"""
MSFT OCS ChassisManager v2.0 REST API client
https://github.com/MSOpenTech/ChassisManager
"""

import posixpath
from xml.etree import ElementTree

from oslo_log import log
import requests
from requests import auth
from requests import exceptions as requests_exceptions

from ironic.common import exception
from ironic.common.i18n import _
from ironic.common.i18n import _LE

LOG = log.getLogger(__name__)

WCSNS = 'http://schemas.datacontract.org/2004/07/Microsoft.GFS.WCS.Contracts'
COMPLETION_CODE_SUCCESS = "Success"

BOOT_TYPE_UNKNOWN = 0
BOOT_TYPE_NO_OVERRIDE = 1
BOOT_TYPE_FORCE_PXE = 2
BOOT_TYPE_FORCE_DEFAULT_HDD = 3
BOOT_TYPE_FORCE_INTO_BIOS_SETUP = 4
BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE = 5

BOOT_TYPE_MAP = {
    'Unknown': BOOT_TYPE_UNKNOWN,
    'NoOverride': BOOT_TYPE_NO_OVERRIDE,
    'ForcePxe': BOOT_TYPE_FORCE_PXE,
    'ForceDefaultHdd': BOOT_TYPE_FORCE_DEFAULT_HDD,
    'ForceIntoBiosSetup': BOOT_TYPE_FORCE_INTO_BIOS_SETUP,
    'ForceFloppyOrRemovable': BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE,
}

POWER_STATUS_ON = "ON"
POWER_STATUS_OFF = "OFF"


[docs]class MSFTOCSClientApi(object): def __init__(self, base_url, username, password): self._base_url = base_url self._username = username self._password = password def _exec_cmd(self, rel_url): """Executes a command by calling the chassis manager API.""" url = posixpath.join(self._base_url, rel_url) try: response = requests.get( url, auth=auth.HTTPBasicAuth(self._username, self._password)) response.raise_for_status() except requests_exceptions.RequestException as ex: msg = _("HTTP call failed: %s") % ex LOG.exception(msg) raise exception.MSFTOCSClientApiException(msg) xml_response = response.text LOG.debug("Call to %(url)s got response: %(xml_response)s", {"url": url, "xml_response": xml_response}) return xml_response def _check_completion_code(self, xml_response): try: et = ElementTree.fromstring(xml_response) except ElementTree.ParseError as ex: LOG.exception(_LE("XML parsing failed: %s"), ex) raise exception.MSFTOCSClientApiException( _("Invalid XML: %s") % xml_response) item = et.find("./n:completionCode", namespaces={'n': WCSNS}) if item is None or item.text != COMPLETION_CODE_SUCCESS: raise exception.MSFTOCSClientApiException( _("Operation failed: %s") % xml_response) return et
[docs] def get_blade_state(self, blade_id): """Returns whether a blade's chipset is receiving power (soft-power). :param blade_id: the blade id :returns: one of: POWER_STATUS_ON, POWER_STATUS_OFF :raises: MSFTOCSClientApiException """ et = self._check_completion_code( self._exec_cmd("GetBladeState?bladeId=%d" % blade_id)) return et.find('./n:bladeState', namespaces={'n': WCSNS}).text
[docs] def set_blade_on(self, blade_id): """Supplies power to a blade chipset (soft-power state). :param blade_id: the blade id :raises: MSFTOCSClientApiException """ self._check_completion_code( self._exec_cmd("SetBladeOn?bladeId=%d" % blade_id))
[docs] def set_blade_off(self, blade_id): """Shuts down a given blade (soft-power state). :param blade_id: the blade id :raises: MSFTOCSClientApiException """ self._check_completion_code( self._exec_cmd("SetBladeOff?bladeId=%d" % blade_id))
[docs] def set_blade_power_cycle(self, blade_id, off_time=0): """Performs a soft reboot of a given blade. :param blade_id: the blade id :param off_time: seconds to wait between shutdown and boot :raises: MSFTOCSClientApiException """ self._check_completion_code( self._exec_cmd("SetBladeActivePowerCycle?bladeId=%(blade_id)d&" "offTime=%(off_time)d" % {"blade_id": blade_id, "off_time": off_time}))
[docs] def get_next_boot(self, blade_id): """Returns the next boot device configured for a given blade. :param blade_id: the blade id :returns: one of: BOOT_TYPE_UNKNOWN, BOOT_TYPE_NO_OVERRIDE, BOOT_TYPE_FORCE_PXE, BOOT_TYPE_FORCE_DEFAULT_HDD, BOOT_TYPE_FORCE_INTO_BIOS_SETUP, BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE :raises: MSFTOCSClientApiException """ et = self._check_completion_code( self._exec_cmd("GetNextBoot?bladeId=%d" % blade_id)) return BOOT_TYPE_MAP[ et.find('./n:nextBoot', namespaces={'n': WCSNS}).text]
[docs] def set_next_boot(self, blade_id, boot_type, persistent=True, uefi=True): """Sets the next boot device for a given blade. :param blade_id: the blade id :param boot_type: possible values: BOOT_TYPE_UNKNOWN, BOOT_TYPE_NO_OVERRIDE, BOOT_TYPE_FORCE_PXE, BOOT_TYPE_FORCE_DEFAULT_HDD, BOOT_TYPE_FORCE_INTO_BIOS_SETUP, BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE :param persistent: whether this setting affects the next boot only or every subsequent boot :param uefi: True if UEFI, False otherwise :raises: MSFTOCSClientApiException """ self._check_completion_code( self._exec_cmd( "SetNextBoot?bladeId=%(blade_id)d&bootType=%(boot_type)d&" "uefi=%(uefi)s&persistent=%(persistent)s" % {"blade_id": blade_id, "boot_type": boot_type, "uefi": str(uefi).lower(), "persistent": str(persistent).lower()}))

Project Source