# Copyright 2019 Nokia # # 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 from ironic.common.i18n import _ from ironic_virtmedia_driver.vendors.ironic_virtmedia_hw import IronicVirtMediaHW from ironic_virtmedia_driver import virtmedia_exception import redfish.ris.tpdefs from redfish import AuthMethod, redfish_client from redfish.rest.v1 import ServerDownOrUnreachableError class HP(IronicVirtMediaHW): def __init__(self, log): super(HP, self).__init__(log) self.remote_share = '/bootimages/' self.typepath = None def _init_connection(self, driver_info): """Get connection info and init rest_object""" host = 'https://' + driver_info['address'] user = driver_info['username'] password = driver_info['password'] redfishclient = None try: redfishclient = redfish_client(base_url=host, \ username=user, password=password, \ default_prefix="/redfish/v1") redfishclient.login(auth=AuthMethod.SESSION) self._init_typepath(redfishclient) except ServerDownOrUnreachableError as error: operation = _("iLO not responding") raise virtmedia_exception.VirtmediaOperationError( operation=operation, error=error) except Exception as error: operation = _("Failed to login to iLO") raise virtmedia_exception.VirtmediaOperationError( operation=operation, error=error) return redfishclient def _init_typepath(self, connection): typepath = redfish.ris.tpdefs.Typesandpathdefines() typepath.getgen(url=connection.get_base_url()) typepath.defs.redfishchange() self.typepath = typepath def _search_for_type(self, typename, resources): instances = [] nosettings = [item for item in resources["resources"] if "/settings/" not in item["@odata.id"]] for item in nosettings: if "@odata.type" in item and \ typename.lower() in item["@odata.type"].lower(): instances.append(item) return instances def _get_instances(self, connection): resources = {} response = connection.get("/redfish/v1/resourcedirectory/") if response.status == 200: resources["resources"] = response.dict["Instances"] else: return [] return self._search_for_type("Manager.", resources) def _get_error(self, response): message = json.loads(response.text) error = message["error"]["@Message.ExtendedInfo"][0]["MessageId"].split(".") return error def _get_eject_media(self, response): try: r_dict = response.dict ops = r_dict.get("Oem", {}).get(self.typepath.defs.oemhp, {}).get("Actions") if ops: for key in ops.keys(): if "EjectVirtualMedia" in key: return ops[key]['target'] except Exception as err: self.log.error("Failure in finding eject action from response") return None def _umount_virtual_cd(self, connection, response): unmount_path = None unmount_body = None if self.typepath.defs.isgen10 or \ self.typepath.defs.isgen9: unmount_path = self._get_eject_media(response) unmount_body = {} if not unmount_path: operation = _("Failed to unmount image") error = _("Failed to find unmount path") raise virtmedia_exception.VirtmediaOperationError( operation=operation, error=error) else: unmount_path = response.dict.get("@odata.id", "") unmount_body = {"Action": "EjectVirtualMedia", "Target": self.typepath.defs.oempath} resp = connection.post(path=unmount_path, body=unmount_body) if resp.status != 200: self.log.error("Unmounting cd failed: %r, cd location: %r", resp, unmount_path) operation = _("Failed to unmount image") error = self._get_error(resp) raise virtmedia_exception.VirtmediaOperationError( operation=operation, error=error) def _get_virtual_media_devices(self, connection, instance): rsp = connection.get(instance["@odata.id"]) rsp = connection.get(rsp.dict["VirtualMedia"]["@odata.id"]) return rsp.dict['Members'] def _mount_virtual_cd(self, connection, image_location): instances = self._get_instances(connection) for instance in instances: for vmlink in self._get_virtual_media_devices(connection, instance): response = connection.get(vmlink["@odata.id"]) if response.status == 200 and "DVD" in response.dict["MediaTypes"]: if response.dict['Inserted']: self._umount_virtual_cd(connection, response) body = {"Image": image_location} if image_location: body["Oem"] = {self.typepath.defs.oemhp: {"BootOnNextServerReset": \ True}} response = connection.patch(path=vmlink["@odata.id"], body=body) elif response.status != 200: self.log.error("Failed to mount image") error = self._get_error(response) operation = _("Failed to mount image") raise virtmedia_exception.VirtmediaOperationError( operation=operation, error=error) def attach_virtual_cd(self, image_filename, driver_info, task): connection = self._init_connection(driver_info) image_location = 'http://' + driver_info['provisioning_server'] + ':' + driver_info['provisioning_server_http_port'] + self.remote_share + image_filename self._mount_virtual_cd(connection, image_location) connection.logout() return True def detach_virtual_cd(self, driver_info, task): connection = self._init_connection(driver_info) instances = self._get_instances(connection) for instance in instances: for vmlink in self._get_virtual_media_devices(connection, instance): response = connection.get(vmlink["@odata.id"]) if response.status == 200 and "DVD" in response.dict["MediaTypes"]: if response.dict['Inserted']: self._umount_virtual_cd(connection, response) connection.logout() return True def set_boot_device(self, task): """ This is done during the mounting""" pass