zbaremetalnodeinventory: Add aarch64 support
[ta/cm-plugins.git] / inventoryhandlers / baremetal-node-inventory / zbaremetalnodeinventory.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 import json
16 import platform
17 from jinja2 import Environment
18
19 from cmframework.apis import cmansibleinventoryconfig
20 from cmdatahandlers.api import utils
21
22
23 nics_json_txt = """
24 [ {%- if 'mgmt_mac' in all_vars['hosts'][host] %}
25       {%- for mac_members in all_vars['hosts'][host]['mgmt_mac'] %}
26           {
27           "mac": "{{ mac_members }}"
28           }
29           {%- if not loop.last %},{%- endif %}
30       {%- endfor %}
31   {%- else: %}
32       {
33       "mac": "{{ all_vars['hw_inventory_details'][host]['mgmt_mac'] }}"
34       }
35   {%- endif %}
36 ]
37 """
38
39
40 class zbaremetalnodeinventory(cmansibleinventoryconfig.CMAnsibleInventoryConfigPlugin):
41     def __init__(self, confman, inventory, ownhost):
42         super(zbaremetalnodeinventory, self).__init__(confman, inventory, ownhost)
43
44     def handle_bootstrapping(self):
45         pass
46
47     def handle_provisioning(self):
48         self.handle()
49
50     def handle_postconfig(self):
51         self.handle()
52
53     def handle_setup(self):
54         pass
55
56     @staticmethod
57     def _check_host_single_nic(host_network_profile_value, host_interface_net_mapping):
58         if 'provider_network_interfaces' in host_network_profile_value:
59             host_provider_network_interfaces = host_network_profile_value['provider_network_interfaces']
60             if len(host_interface_net_mapping) == 1 and len(host_provider_network_interfaces) == 1:
61                 if host_interface_net_mapping.keys()[0] == host_provider_network_interfaces.keys()[0]:
62                     return True
63         return False
64
65     @staticmethod
66     def _generate_linux_bonding_options(options):
67         mode_mapping = {'active-backup': 'active-backup', 'lacp': '802.3ad'}
68         default_options = {'active-backup': 'miimon=100',
69                            'lacp': 'lacp_rate=fast miimon=100'}
70         for i in options.split():
71             key, value = i.split('=')
72             if key == 'mode':
73                 if default_options[value]:
74                     return 'mode=' + mode_mapping[value] + ' ' + default_options[value]
75                 return 'mode=' + mode_mapping[value]
76
77     @staticmethod
78     def _generate_ovs_bonding_options(options):
79         mode_mapping = {'active-backup': 'active-backup', 'lacp': 'balance-slb',
80                         'lacp-layer34': 'balance-tcp'}
81         default_options = {'active-backup': '',
82                            'lacp': 'lacp=active other_config:lacp-time=fast other_config:bond-detect-mode=carrier',
83                            'lacp-layer34': 'lacp=active other_config:lacp-time=fast other_config:bond-detect-mode=carrier'}
84         for i in options.split():
85             key, value = i.split('=')
86             if key == 'mode':
87                 if default_options[value]:
88                     return 'bond_mode=' + mode_mapping[value] + ' ' + default_options[value]
89                 return 'bond_mode=' + mode_mapping[value]
90
91     @staticmethod
92     def _add_static_routes(routes):
93         routes_list = []
94         for route in routes:
95             routes_list.append({"ip_netmask": route["to"], "next_hop": route["via"]})
96         return routes_list
97
98     def handle(self):
99         usersconf = self.confman.get_users_config_handler()
100         hostsconf = self.confman.get_hosts_config_handler()
101         admin_user = usersconf.get_admin_user()
102         self.add_global_var("home_dir", "/home/" + admin_user)
103         all_vars = self.inventory['all']['vars']
104         host_locals = self.inventory['_meta']['hostvars']
105         nfs_server_ip = host_locals[all_vars['installation_controller']]['networking']['infra_external']['ip']
106
107         for host, hostvars in host_locals.iteritems():
108             host_hdd_mapping = hostvars['by_path_disks']
109             host_networking = hostvars['networking']
110             host_network_profiles_list = all_vars['hosts'][host]['network_profiles']
111             host_network_profile_value = all_vars['network_profiles'][host_network_profiles_list[0]]
112             host_interface_net_mapping = host_network_profile_value['interface_net_mapping']
113
114             infra_bond = {'in_use': False}
115             host_bonding_interfaces = host_network_profile_value.get('bonding_interfaces', {})
116             default_mtu = all_vars['networking'].get('mtu', 1500)
117
118             sriov_mtus = {}
119             if 'sriov_provider_networks' in host_network_profile_value:
120                 sriov_nets = host_network_profile_value['sriov_provider_networks']
121                 prov_infos = host_networking.get('provider_networks', {})
122                 for net_name, sriov_info in sriov_nets.iteritems():
123                     if prov_infos.get(net_name):
124                         prov_info = prov_infos[net_name]
125                         sriov_mtu = prov_info.get('mtu', default_mtu)
126                         for iface in sriov_info['interfaces']:
127                             sriov_mtus[iface] = sriov_mtu
128
129             mtu = default_mtu
130             if 'mtu' in all_vars['networking']['infra_internal']:
131                 mtu = all_vars['networking']['infra_internal']['mtu']
132
133             phys_iface_mtu = 1500
134             if 'vlan' in host_networking['infra_internal']:
135                 for iface, infras in host_interface_net_mapping.iteritems():
136                     if 'infra_internal' in infras:
137                         for infra in infras:
138                             tmp_mtu = default_mtu
139                             if 'mtu' in all_vars['networking'][infra]:
140                                 tmp_mtu = all_vars['networking'][infra]['mtu']
141                             if infra == 'cloud_tenant':
142                                 tmp_mtu = tmp_mtu + 50
143                             if tmp_mtu > phys_iface_mtu:
144                                 phys_iface_mtu = tmp_mtu
145                         if 'bond' in iface:
146                             if host_bonding_interfaces.get(iface):
147                                 for slave in host_bonding_interfaces[iface]:
148                                     if slave in sriov_mtus and sriov_mtus[slave] > phys_iface_mtu:
149                                         phys_iface_mtu = sriov_mtus[slave]
150                         elif iface in sriov_mtus and sriov_mtus[iface] > phys_iface_mtu:
151                             phys_iface_mtu = sriov_mtus[iface]
152                         break
153
154             properties = {
155                 "capabilities": "boot_option:local",
156                 "cpu_arch": platform.machine(),
157                 "cpus": 8,
158                 "disk_size": 40,
159                 "ram": 16384
160             }
161
162             power = {
163                 "provisioning_server": nfs_server_ip,
164                 "virtmedia_deploy_iso": "file:///opt/images/ironic-deploy.iso",
165             }
166
167             # aarch64 platforms only support EFI bootloaders
168             if platform.machine() == 'aarch64':
169                 properties["capabilities"] += ",boot_mode:uefi"
170
171             if utils.is_virtualized():
172                 driver = "ssh_virtmedia"
173                 properties["root_device"] = {"by_path": host_hdd_mapping['os']}
174                 power["ssh_address"] = all_vars['hosts'][host]['hwmgmt']['address']
175                 power["ssh_username"] = all_vars['hosts'][host]['hwmgmt']['user']
176                 power["ipmi_port"] = all_vars['hosts'][host]['vbmc_port']
177                 power["ipmi_username"] = "admin"
178                 power["ipmi_password"] = "password"
179                 power["ssh_key_contents"] = "{{ lookup('file', '/etc/userconfig/id_rsa') }}"
180                 power["ipmi_address"] = host_locals[all_vars['installation_controller']]['networking']['infra_internal']['ip']
181             else:
182                 driver = "ipmi_virtmedia"
183                 power["ipmi_address"] = all_vars['hosts'][host]['hwmgmt']['address']
184                 power["ipmi_password"] = all_vars['hosts'][host]['hwmgmt']['password']
185                 power["ipmi_username"] = all_vars['hosts'][host]['hwmgmt']['user']
186                 power["ipmi_priv_level"] = hostsconf.get_hwmgmt_priv_level(host)
187                 power["product_family"] = all_vars['hw_inventory_details'][host]['product_family']
188                 power["vendor"] = all_vars['hw_inventory_details'][host]['vendor']
189
190                 if host_hdd_mapping['os'] != "/dev/sda":
191                     properties["root_device"] = {"by_path": host_hdd_mapping['os']}
192                 else:
193                     properties["root_device"] = {"name": host_hdd_mapping['os']}
194
195             nics_text = Environment().from_string(nics_json_txt).render(all_vars=all_vars, host=host)
196             nics_inventory = json.loads(nics_text)
197
198             driver_info = {}
199             driver_info["power"] = power
200             #####################################################
201             network_config = []
202             if 'interface' in host_networking['infra_internal']:
203                 if not self._check_host_single_nic(host_network_profile_value, host_interface_net_mapping):
204                     if 'bonding_interfaces' in host_network_profile_value:
205                         for net_key, net_value in host_interface_net_mapping.iteritems():
206                             bond_contents = {}
207                             if "bond" in net_key and "infra_internal" in net_value:
208                                 members = []
209                                 for member in host_bonding_interfaces[net_key]:
210                                     member_element = {}
211                                     if 'bond' in host_networking['infra_internal']['interface']:
212                                         member_element["mtu"] = mtu
213                                     else:
214                                         member_element["mtu"] = phys_iface_mtu
215                                     member_element["name"] = member
216                                     member_element["type"] = "interface"
217                                     member_element["use_dhcp"] = False
218                                     members.append(member_element)
219
220                                 bond_contents = {
221                                     "type": "linux_bond",
222                                     "use_dhcp": False
223                                 }
224                                 bond_contents["name"] = net_key
225                                 bond_contents["members"] = members
226
227                                 if 'linux_bonding_options' in host_network_profile_value:
228                                     bond_contents["bonding_options"] = self._generate_linux_bonding_options(host_network_profile_value['linux_bonding_options'])
229                                 if 'bond' in host_networking['infra_internal']['interface']:
230                                     bond_contents["addresses"] = [{"ip_netmask": "%s/%s" % (host_networking['infra_internal']['ip'], host_networking['infra_internal']['mask'])}]
231                                     bond_contents["mtu"] = mtu
232                                     if 'routes' in host_networking['infra_internal']:
233                                         routes = host_networking['infra_internal']['routes']
234                                         bond_contents["routes"] = self._add_static_routes(routes)
235                                 else:
236                                     bond_contents["mtu"] = phys_iface_mtu
237
238                                 infra_bond.update({'in_use': True})
239
240                                 network_config.append(bond_contents)
241                     if 'vlan' in host_networking['infra_internal']:
242                         vlan_contents = {
243                             "type": "vlan",
244                             "use_dhcp": False
245                             }
246                         vlan_contents["addresses"] = [{"ip_netmask": "%s/%s" % (host_networking['infra_internal']['ip'], host_networking['infra_internal']['mask'])}]
247                         vlan_contents["vlan_id"] = host_networking['infra_internal']['vlan']
248                         for net_key, net_value in host_interface_net_mapping.iteritems():
249                             if "infra_internal" in net_value:
250                                 vlan_contents["device"] = net_key
251                         vlan_contents["mtu"] = mtu
252                         if 'routes' in host_networking['infra_internal']:
253                             routes = host_networking['infra_internal']['routes']
254                             vlan_contents["routes"] = []
255                             for route in routes:
256                                 vlan_contents["routes"].append({"ip_netmask": route["to"], "next_hop": route["via"]})
257                         if not infra_bond["in_use"]:
258                             vlan_phy_contents = {
259                                 "type": "interface",
260                                 "use_dhcp": False,
261                                 "mtu": phys_iface_mtu
262                                 }
263                             for net_key, net_value in host_interface_net_mapping.iteritems():
264                                 if "infra_internal" in net_value:
265                                     vlan_phy_contents["name"] = net_key
266                             network_config.append(vlan_phy_contents)
267
268                         network_config.append(vlan_contents)
269
270                     elif not infra_bond["in_use"]:
271                         phy_contents = {
272                             "name": host_networking['infra_internal']['interface'],
273                             "type": "interface",
274                             "mtu": mtu,
275                             "use_dhcp": False
276                             }
277                         phy_contents["addresses"] = [{"ip_netmask": "%s/%s" % (host_networking['infra_internal']['ip'], host_networking['infra_internal']['mask'])}]
278                         if 'routes' in host_networking['infra_internal']:
279                             routes = host_networking['infra_internal']['routes']
280                             phy_contents["routes"] = self._add_static_routes(routes)
281
282                         network_config.append(phy_contents)
283
284                 # --> single_nic_setup <-- #
285                 else:
286                     single_nic_contents = {
287                         "name": "br-pro0",
288                         "type": "ovs_bridge",
289                         "members": []
290                         }
291                     member_elements = {"mtu": phys_iface_mtu, "use_dhcp": False}
292                     iface = host_interface_net_mapping.keys()[0]
293                     if 'bond' in iface:
294                         for bond_iface, bond_value in host_bonding_interfaces.iteritems():
295                             if bond_iface == iface:
296                                 if 'ovs_bonding_options' in host_network_profile_value:
297                                     member_elements["ovs_options"] = self._generate_ovs_bonding_options(host_network_profile_value['ovs_bonding_options'])
298                                 member_elements["name"] = iface
299                                 member_elements["type"] = "ovs_bond"
300                                 member_elements["members"] = []
301                                 for member in bond_value:
302                                     ovs_bond_member = {
303                                         "name": member,
304                                         "type": "interface",
305                                         "mtu": phys_iface_mtu,
306                                         "use_dhcp": False
307                                         }
308                                     member_elements["members"].append(ovs_bond_member)
309                             single_nic_contents["members"].append(member_elements)
310                     else:
311                         member_elements["name"] = iface
312                         member_elements["type"] = "interface"
313                         single_nic_contents["members"].append(member_elements)
314
315                     infra_elements = {}
316                     infra = host_networking['infra_internal']
317                     infra_elements["use_dhcp"] = False
318                     infra_elements["type"] = "vlan"
319                     infra_elements["vlan_id"] = infra['vlan']
320                     infra_elements["mtu"] = mtu
321                     infra_elements["addresses"] = [{"ip_netmask": "%s/%s" % (infra['ip'], infra['mask'])}]
322                     if 'routes' in infra:
323                         routes = infra['routes']
324                         infra_elements["routes"] = self._add_static_routes(routes)
325
326                     single_nic_contents["members"].append(infra_elements)
327                     network_config.append(single_nic_contents)
328             #####################################################
329             driver_info["power"]["os_net_config"] = {"network_config": network_config}
330
331             ironic_node_details = {
332                 "name": host,
333                 "driver": driver,
334                 "network_interface": "noop",
335                 "nics": nics_inventory,
336                 "properties": properties,
337                 "driver_info": driver_info
338             }
339             self.add_host_var(host, 'ironic_node_details', ironic_node_details)