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