a940f78aa5d39f14d0c23f3c5eb9bb32e81293a1
[ta/config-manager.git] / cmframework / src / cmframework / utils / cmansibleinventory.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 from __future__ import print_function
15 import json
16 import os
17
18 from cmframework.apis import cmerror
19 from cmframework.utils.cmpluginloader import CMPluginLoader
20 from cmdatahandlers.api import configmanager
21 from cmdatahandlers.api import utils
22 from serviceprofiles import profiles
23
24
25 class AnsibleInventory(object):
26
27     def __init__(self, properties, plugin_path):
28         self.pluginloader = AnsibleInventoryPluginLoader(plugin_path)
29         self.props = properties
30         propsjson = {}
31         for name, value in properties.iteritems():
32             try:
33                 propsjson[name] = json.loads(value)
34             except Exception:  # pylint: disable=broad-except
35                 continue
36         self.confman = configmanager.ConfigManager(propsjson)
37
38     # pylint: disable=no-self-use
39     def _is_setup(self):
40         if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'setup':
41             return True
42         return False
43
44     def _is_bootstrapping(self):
45         if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'bootstrapping':
46             return True
47         return False
48
49     def _is_provisioning(self):
50         if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'provisioning':
51             return True
52         return False
53
54     def _is_postconfig(self):
55         if not self._is_bootstrapping() and not self._is_provisioning():
56             return True
57         return False
58
59     def _get_own_host(self):
60
61         hostsconf = self.confman.get_hosts_config_handler()
62
63         if utils.is_virtualized():
64             return hostsconf.get_installation_host()
65
66         hwmgmtip = utils.get_own_hwmgmt_ip()
67
68         return hostsconf.get_host_having_hwmgmt_address(hwmgmtip)
69
70     def set_default_route(self, hostvars, node, infra_internal_name):
71         routes = hostvars[node]['networking'][infra_internal_name].get('routes', [])
72         infra_int_ip = hostvars[node]['networking'][infra_internal_name]['ip']
73         caasconf = self.confman.get_caas_config_handler()
74         cidr_to_set = caasconf.get_caas_parameter("service_cluster_ip_cidr")
75         routes.append({"to": cidr_to_set, "via": infra_int_ip})
76         hostvars[node]['networking'][infra_internal_name]['routes'] = routes
77
78     def set_common_caas(self, hostvars, node, hostsconf):
79         hostvars[node]['nodetype'] = hostsconf.get_nodetype(node)
80         hostvars[node]['nodeindex'] = hostsconf.get_nodeindex(node)
81         hostvars[node]['nodename'] = hostsconf.get_nodename(node)
82         if hostsconf.get_nodetype(node) == "caas-master":
83             hostvars[node]['noderole'] = "master"
84         elif hostsconf.get_nodetype(node) == "caas-worker":
85             hostvars[node]['noderole'] = "worker"
86
87         host_labels = hostsconf.get_labels(node)
88         if host_labels:
89             hostvars[node]['labels'] = host_labels
90
91         hostvars[node]['ssl_alt_name'] = {}
92         dns = [node]
93         hostvars[node]['ssl_alt_name']['dns'] = dns
94         ips = ['127.0.0.1']
95         ips.append(hostvars[node]['ansible_host'])
96         hostvars[node]['ssl_alt_name']['ip'] = ips
97
98         caasconf = self.confman.get_caas_config_handler()
99         hostvars[node]['system_reserved_memory'] = hostsconf.get_system_reserved_memory(node)
100         hostvars[node]['caas_soft_eviction_threshold'] = caasconf.get_caas_soft_eviction_threshold()
101         hostvars[node]['caas_hard_eviction_threshold'] = caasconf.get_caas_hard_eviction_threshold()
102
103     def set_caas_master_data(self, hostvars, node, caasconf, hostsconf):
104         dns = hostvars[node]['ssl_alt_name']['dns']
105         dns.append(caasconf.get_kubernetes_domain())
106         dns.append(caasconf.get_apiserver_in_hosts())
107         dns.append(caasconf.get_registry_url())
108         dns.append(caasconf.get_update_registry_url())
109         dns.append(caasconf.get_swift_url())
110         dns.append(caasconf.get_swift_update_url())
111         dns.append(caasconf.get_ldap_master_url())
112         dns.append(caasconf.get_ldap_slave_url())
113         dns.append(caasconf.get_chart_repo_url())
114         dns.append(caasconf.get_caas_parameter('prometheus_url'))
115         dns.append(caasconf.get_tiller_url())
116
117         hosts = hostsconf.get_hosts()
118         for host in hosts:
119             if 'caas_master' in hostsconf.get_service_profiles(host):
120                 dns.append(host)
121
122         hostvars[node]['ssl_alt_name']['dns'] = dns
123         ips = hostvars[node]['ssl_alt_name']['ip']
124         ips.append(caasconf.get_apiserver_svc_ip())
125         hostvars[node]['ssl_alt_name']['ip'] = ips
126
127     def generate_inventory(self):
128         try:
129             inventory = {}
130
131             # convert properties to inventory using the following rules:
132             # 1. cloud scoped configuration is mapped to "all" group's "vars" section
133             # 2. The host level domain configuration will be mapped to "_meta" section
134             #    under "hostvars" group. Under this there will be a dictionary per host.
135             # 3. The mapping between hosts and profiles is created.
136             #    This is used to allow ansible to automatically identify in which hosts
137             #    the playbooks are to be run.
138             #    This mapping is done as follows:
139             #      - A mapping is created for each service profile.
140             #      - A mapping is created for the network_profiles type.
141             #      - A mapping is created for the storage_profiles type.
142             #      - A mapping is created for the performance_profiles type.
143
144             # Get the host variables and all variables
145             hostvars = {}
146             allvars = {}
147
148             netconf = self.confman.get_networking_config_handler()
149             hostsconf = self.confman.get_hosts_config_handler()
150             infra_internal_name = netconf.get_infra_internal_network_name()
151             hosts = hostsconf.get_hosts()
152
153             ownhost = self._get_own_host()
154             if self._is_bootstrapping():
155                 for host in hosts:
156                     if host != ownhost:
157                         hostsconf.disable_host(host)
158
159             hosts = hostsconf.get_enabled_hosts()
160             for name, value in self.props.iteritems():
161                 try:
162                     d = name.split('.')
163                     if len(d) != 2:
164                         continue
165                     node = d[0]
166                     domain = d[1]
167                     if node != 'cloud':
168                         if node in hosts:
169                             if node not in hostvars:
170                                 hostvars[node] = {}
171                             hostip = netconf.get_host_ip(node, infra_internal_name)
172                             hostvars[node]['ansible_host'] = hostip
173
174                             try:
175                                 hostvars[node][domain] = json.loads(value)
176                             except Exception:  # pylint: disable=broad-except
177                                 hostvars[node][domain] = value
178
179                             if 'caas_master' in hostsconf.get_service_profiles(node):
180                                 self.set_common_caas(hostvars, node, hostsconf)
181                                 caasconf = self.confman.get_caas_config_handler()
182                                 self.set_caas_master_data(hostvars, node, caasconf, hostsconf)
183                                 self.set_default_route(hostvars, node, infra_internal_name)
184
185                             if 'caas_worker' in hostsconf.get_service_profiles(node):
186                                 self.set_common_caas(hostvars, node, hostsconf)
187                                 self.set_default_route(hostvars, node, infra_internal_name)
188                     else:
189                         try:
190                             allvars[domain] = json.loads(value)
191                         except Exception:  # pylint: disable=broad-except
192                             allvars[domain] = value
193
194                 except Exception:  # pylint: disable=broad-except
195                     pass
196
197             inventory['_meta'] = {}
198             inventory['_meta']['hostvars'] = hostvars
199             inventory['all'] = {'vars': allvars}
200
201             # add hosts to service profiles mapping
202             serviceprofiles = profiles.Profiles().get_service_profiles()
203             for profile in serviceprofiles:
204                 try:
205                     servicehosts = hostsconf.get_service_profile_hosts(profile)
206                     tmp = []
207                     for host in servicehosts:
208                         if host in hosts:
209                             tmp.append(host)
210                     inventory[profile] = tmp
211                 except Exception:  # pylint: disable=broad-except
212                     continue
213
214             # add mapping between profile types and hosts
215             inventory['network_profiles'] = []
216             inventory['storage_profiles'] = []
217             inventory['performance_profiles'] = []
218             for host in hosts:
219                 # check for network profiles
220                 try:
221                     _ = hostsconf.get_network_profiles(host)
222                     inventory['network_profiles'].append(host)
223                 except Exception:  # pylint: disable=broad-except
224                     pass
225
226                 # check for storage profiles
227                 try:
228                     _ = hostsconf.get_storage_profiles(host)
229                     inventory['storage_profiles'].append(host)
230                 except Exception:  # pylint: disable=broad-except
231                     pass
232
233                 # check for perfromance profiles
234                 try:
235                     _ = hostsconf.get_performance_profiles(host)
236                     inventory['performance_profiles'].append(host)
237                 except Exception:  # pylint: disable=broad-except
238                     pass
239
240             self.pluginloader.load()
241             plugins = self.pluginloader.get_plugin_instances(self.confman, inventory, ownhost)
242             if self._is_setup():
243                 inventory.clear()
244             for name, plugin in sorted(plugins.iteritems()):
245                 if self._is_bootstrapping():
246                     plugin.handle_bootstrapping()
247                 elif self._is_provisioning():
248                     plugin.handle_provisioning()
249                 elif self._is_setup():
250                     plugin.handle_setup()
251                 else:
252                     plugin.handle_postconfig()
253
254             return inventory
255
256         except Exception as exp:  # pylint: disable=broad-except
257             raise cmerror.CMError(str(exp))
258
259
260 class AnsibleInventoryPluginLoader(CMPluginLoader):
261     def __init__(self, plugin_location, plugin_filter=None):
262         super(AnsibleInventoryPluginLoader, self).__init__(plugin_location, plugin_filter)
263
264     def build_filter_dict(self):
265         pass
266
267     def get_plugin_instances(self, confman, inventory, ownhost):
268         plugs = {}
269         for plugin, module in self.loaded_plugin.iteritems():
270             class_name = getattr(module, plugin)
271             instance = class_name(confman, inventory, ownhost)
272             plugs[plugin] = instance
273         return plugs
274
275
276 def main():
277     import argparse
278     import sys
279     import traceback
280
281     parser = argparse.ArgumentParser(description='Test ansible inventory handler', prog=sys.argv[0])
282
283     parser.add_argument('--properties',
284                         required=True,
285                         dest='properties',
286                         metavar='PROPERTIES',
287                         help='The file containing the properties',
288                         type=str,
289                         action='store')
290
291     parser.add_argument('--plugins',
292                         required=True,
293                         dest='plugins',
294                         metavar='PLUGINS',
295                         help='The path to ansible inventory plugin(s)',
296                         type=str,
297                         action='store')
298
299     try:
300         args = parser.parse_args(sys.argv[1:])
301
302         f = open(args.properties, 'r')
303         lines = f.read().splitlines()
304         f.close()
305         properties = {}
306         for line in lines:
307             d = line.split('=')
308             if len(d) != 2:
309                 continue
310             properties[d[0]] = d[1]
311         ansible = AnsibleInventory(properties, args.plugins)
312         inventory = ansible.generate_inventory()
313
314         print(json.dumps(inventory, indent=4, sort_keys=True))
315
316     except Exception as exp:  # pylint: disable=broad-except
317         print(str(exp))
318         traceback.print_exc()
319         sys.exit(1)
320     sys.exit(0)
321
322
323 if __name__ == '__main__':
324     main()