Add missing support for HP Gen10
[ta/ironic-virtmedia-driver.git] / src / ironic_virtmedia_driver / vendors / hp / hp.py
1 # Copyright 2019 Nokia
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15
16 import json
17
18 from ironic.common.i18n import _
19
20 from ironic_virtmedia_driver.vendors.ironic_virtmedia_hw import IronicVirtMediaHW
21 from ironic_virtmedia_driver import virtmedia_exception
22
23 import redfish.ris.tpdefs
24 from redfish import AuthMethod, redfish_client
25 from redfish.rest.v1 import ServerDownOrUnreachableError
26
27 class HP(IronicVirtMediaHW):
28     def __init__(self, log):
29         super(HP, self).__init__(log)
30         self.remote_share = '/bootimages/'
31         self.typepath = None
32
33     def _init_connection(self, driver_info):
34         """Get connection info and init rest_object"""
35         host = 'https://' + driver_info['address']
36         user = driver_info['username']
37         password = driver_info['password']
38         redfishclient = None
39         try:
40             redfishclient = redfish_client(base_url=host, \
41                   username=user, password=password, \
42                   default_prefix="/redfish/v1")
43             redfishclient.login(auth=AuthMethod.SESSION)
44             self._init_typepath(redfishclient)
45         except ServerDownOrUnreachableError as error:
46             operation = _("iLO not responding")
47             raise virtmedia_exception.VirtmediaOperationError(
48                 operation=operation, error=error)
49         except Exception as error:
50             operation = _("Failed to login to iLO")
51             raise virtmedia_exception.VirtmediaOperationError(
52                 operation=operation, error=error)
53         return redfishclient
54
55     def _init_typepath(self, connection):
56         typepath = redfish.ris.tpdefs.Typesandpathdefines()
57         typepath.getgen(url=connection.get_base_url())
58         typepath.defs.redfishchange()
59         self.typepath = typepath
60
61     def _search_for_type(self, typename, resources):
62         instances = []
63         nosettings = [item for item in resources["resources"] if "/settings/" not in item["@odata.id"]]
64         for item in nosettings:
65             if "@odata.type" in item and \
66                typename.lower() in item["@odata.type"].lower():
67                 instances.append(item)
68         return instances
69
70     def _get_instances(self, connection):
71         resources = {}
72
73         response = connection.get("/redfish/v1/resourcedirectory/")
74         if response.status == 200:
75             resources["resources"] = response.dict["Instances"]
76         else:
77             return []
78
79         return self._search_for_type("Manager.", resources)
80
81     def _get_error(self, response):
82         message = json.loads(response.text)
83         error = message["error"]["@Message.ExtendedInfo"][0]["MessageId"].split(".")
84         return error
85
86     def _get_eject_media(self, response):
87         try:
88             r_dict = response.dict
89             ops = r_dict.get("Oem", {}).get(self.typepath.defs.oemhp, {}).get("Actions")
90             if ops:
91                 for key in ops.keys():
92                     if "EjectVirtualMedia" in key:
93                         return ops[key]['target']
94         except Exception as err:
95             self.log.error("Failure in finding eject action from response")
96         return None
97
98     def _umount_virtual_cd(self, connection, response):
99         unmount_path = None
100         unmount_body = None
101         if self.typepath.defs.isgen10 or \
102            self.typepath.defs.isgen9:
103             unmount_path = self._get_eject_media(response)
104             unmount_body = {}
105             if not unmount_path:
106                 operation = _("Failed to unmount image")
107                 error = _("Failed to find unmount path")
108                 raise virtmedia_exception.VirtmediaOperationError(
109                     operation=operation, error=error)
110         else:
111             unmount_path = response.dict.get("@odata.id", "")
112             unmount_body = {"Action": "EjectVirtualMedia",
113                             "Target": self.typepath.defs.oempath}
114
115         resp = connection.post(path=unmount_path, body=unmount_body)
116         if resp.status != 200:
117             self.log.error("Unmounting cd failed: %r, cd location: %r", resp, unmount_path)
118             operation = _("Failed to unmount image")
119             error = self._get_error(resp)
120             raise virtmedia_exception.VirtmediaOperationError(
121                 operation=operation, error=error)
122
123
124     def _get_virtual_media_devices(self, connection, instance):
125         rsp = connection.get(instance["@odata.id"])
126         rsp = connection.get(rsp.dict["VirtualMedia"]["@odata.id"])
127         return rsp.dict['Members']
128
129     def _mount_virtual_cd(self, connection, image_location):
130         instances = self._get_instances(connection)
131         for instance in instances:
132             for vmlink in self._get_virtual_media_devices(connection, instance):
133                 response = connection.get(vmlink["@odata.id"])
134
135                 if response.status == 200 and "DVD" in response.dict["MediaTypes"]:
136                     if response.dict['Inserted']:
137                         self._umount_virtual_cd(connection, response)
138
139                     body = {"Image": image_location}
140
141                     if image_location:
142                         body["Oem"] = {self.typepath.defs.oemhp: {"BootOnNextServerReset": \
143                                                         True}}
144
145                         response = connection.patch(path=vmlink["@odata.id"], body=body)
146                 elif response.status != 200:
147                     self.log.error("Failed to mount image")
148                     error = self._get_error(response)
149                     operation = _("Failed to mount image")
150                     raise virtmedia_exception.VirtmediaOperationError(
151                         operation=operation, error=error)
152
153     def attach_virtual_cd(self, image_filename, driver_info, task):
154         connection = self._init_connection(driver_info)
155         image_location = 'http://' + driver_info['provisioning_server'] + ':' + driver_info['provisioning_server_http_port'] + self.remote_share + image_filename
156         self._mount_virtual_cd(connection, image_location)
157         connection.logout()
158         return True
159
160     def detach_virtual_cd(self, driver_info, task):
161         connection = self._init_connection(driver_info)
162         instances = self._get_instances(connection)
163         for instance in instances:
164             for vmlink in self._get_virtual_media_devices(connection, instance):
165                 response = connection.get(vmlink["@odata.id"])
166                 if response.status == 200 and "DVD" in response.dict["MediaTypes"]:
167                     if response.dict['Inserted']:
168                         self._umount_virtual_cd(connection, response)
169         connection.logout()
170         return True
171
172     def set_boot_device(self, task):
173         """ This is done during the mounting"""
174         pass