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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
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
25 class AnsibleInventory(object):
27 def __init__(self, properties, plugin_path):
28 self.pluginloader = AnsibleInventoryPluginLoader(plugin_path)
29 self.props = properties
31 for name, value in properties.iteritems():
33 propsjson[name] = json.loads(value)
34 except Exception: # pylint: disable=broad-except
36 self.confman = configmanager.ConfigManager(propsjson)
38 # pylint: disable=no-self-use
40 if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'setup':
44 def _is_bootstrapping(self):
45 if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'bootstrapping':
49 def _is_provisioning(self):
50 if 'CONFIG_PHASE' in os.environ and os.environ['CONFIG_PHASE'] == 'provisioning':
54 def _is_postconfig(self):
55 if not self._is_bootstrapping() and not self._is_provisioning():
59 def _get_own_host(self):
61 hostsconf = self.confman.get_hosts_config_handler()
63 if utils.is_virtualized():
64 return hostsconf.get_installation_host()
66 hwmgmtip = utils.get_own_hwmgmt_ip()
68 return hostsconf.get_host_having_hwmgmt_address(hwmgmtip)
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
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)
83 host_labels = hostsconf.get_labels(node)
85 hostvars[node]['labels'] = host_labels
87 hostvars[node]['ssl_alt_name'] = {}
89 hostvars[node]['ssl_alt_name']['dns'] = dns
91 ips.append(hostvars[node]['ansible_host'])
92 hostvars[node]['ssl_alt_name']['ip'] = ips
94 def set_caas_master_data(self, hostvars, node, caasconf, hostsconf):
95 dns = hostvars[node]['ssl_alt_name']['dns']
96 dns.append(caasconf.get_kubernetes_domain())
97 dns.append(caasconf.get_apiserver_in_hosts())
98 dns.append(caasconf.get_registry_url())
99 dns.append(caasconf.get_update_registry_url())
100 dns.append(caasconf.get_swift_url())
101 dns.append(caasconf.get_swift_update_url())
102 dns.append(caasconf.get_ldap_master_url())
103 dns.append(caasconf.get_ldap_slave_url())
104 dns.append(caasconf.get_chart_repo_url())
105 dns.append(caasconf.get_caas_parameter('prometheus_url'))
106 dns.append(caasconf.get_tiller_url())
108 hosts = hostsconf.get_hosts()
110 if 'caas_master' in hostsconf.get_service_profiles(host):
113 hostvars[node]['ssl_alt_name']['dns'] = dns
114 ips = hostvars[node]['ssl_alt_name']['ip']
115 ips.append(caasconf.get_apiserver_svc_ip())
116 hostvars[node]['ssl_alt_name']['ip'] = ips
118 def generate_inventory(self):
122 # convert properties to inventory using the following rules:
123 # 1. cloud scoped configuration is mapped to "all" group's "vars" section
124 # 2. The host level domain configuration will be mapped to "_meta" section
125 # under "hostvars" group. Under this there will be a dictionary per host.
126 # 3. The mapping between hosts and profiles is created.
127 # This is used to allow ansible to automatically identify in which hosts
128 # the playbooks are to be run.
129 # This mapping is done as follows:
130 # - A mapping is created for each service profile.
131 # - A mapping is created for the network_profiles type.
132 # - A mapping is created for the storage_profiles type.
133 # - A mapping is created for the performance_profiles type.
135 # Get the host variables and all variables
139 netconf = self.confman.get_networking_config_handler()
140 hostsconf = self.confman.get_hosts_config_handler()
141 infra_internal_name = netconf.get_infra_internal_network_name()
142 hosts = hostsconf.get_hosts()
144 ownhost = self._get_own_host()
145 if self._is_bootstrapping():
148 hostsconf.disable_host(host)
150 hosts = hostsconf.get_enabled_hosts()
151 for name, value in self.props.iteritems():
160 if node not in hostvars:
162 hostip = netconf.get_host_ip(node, infra_internal_name)
163 hostvars[node]['ansible_host'] = hostip
166 hostvars[node][domain] = json.loads(value)
167 except Exception: # pylint: disable=broad-except
168 hostvars[node][domain] = value
170 if 'caas_master' in hostsconf.get_service_profiles(node):
171 self.set_common_caas(hostvars, node, hostsconf)
172 caasconf = self.confman.get_caas_config_handler()
173 self.set_caas_master_data(hostvars, node, caasconf, hostsconf)
174 self.set_default_route(hostvars, node, infra_internal_name)
176 if 'caas_worker' in hostsconf.get_service_profiles(node):
177 self.set_common_caas(hostvars, node, hostsconf)
178 self.set_default_route(hostvars, node, infra_internal_name)
181 allvars[domain] = json.loads(value)
182 except Exception: # pylint: disable=broad-except
183 allvars[domain] = value
185 except Exception: # pylint: disable=broad-except
188 inventory['_meta'] = {}
189 inventory['_meta']['hostvars'] = hostvars
190 inventory['all'] = {'vars': allvars}
192 # add hosts to service profiles mapping
193 serviceprofiles = profiles.Profiles().get_service_profiles()
194 for profile in serviceprofiles:
196 servicehosts = hostsconf.get_service_profile_hosts(profile)
198 for host in servicehosts:
201 inventory[profile] = tmp
202 except Exception: # pylint: disable=broad-except
205 # add mapping between profile types and hosts
206 inventory['network_profiles'] = []
207 inventory['storage_profiles'] = []
208 inventory['performance_profiles'] = []
210 # check for network profiles
212 _ = hostsconf.get_network_profiles(host)
213 inventory['network_profiles'].append(host)
214 except Exception: # pylint: disable=broad-except
217 # check for storage profiles
219 _ = hostsconf.get_storage_profiles(host)
220 inventory['storage_profiles'].append(host)
221 except Exception: # pylint: disable=broad-except
224 # check for perfromance profiles
226 _ = hostsconf.get_performance_profiles(host)
227 inventory['performance_profiles'].append(host)
228 except Exception: # pylint: disable=broad-except
231 self.pluginloader.load()
232 plugins = self.pluginloader.get_plugin_instances(self.confman, inventory, ownhost)
235 for name, plugin in sorted(plugins.iteritems()):
236 if self._is_bootstrapping():
237 plugin.handle_bootstrapping()
238 elif self._is_provisioning():
239 plugin.handle_provisioning()
240 elif self._is_setup():
241 plugin.handle_setup()
243 plugin.handle_postconfig()
247 except Exception as exp: # pylint: disable=broad-except
248 raise cmerror.CMError(str(exp))
251 class AnsibleInventoryPluginLoader(CMPluginLoader):
252 def __init__(self, plugin_location, plugin_filter=None):
253 super(AnsibleInventoryPluginLoader, self).__init__(plugin_location, plugin_filter)
255 def build_filter_dict(self):
258 def get_plugin_instances(self, confman, inventory, ownhost):
260 for plugin, module in self.loaded_plugin.iteritems():
261 class_name = getattr(module, plugin)
262 instance = class_name(confman, inventory, ownhost)
263 plugs[plugin] = instance
272 parser = argparse.ArgumentParser(description='Test ansible inventory handler', prog=sys.argv[0])
274 parser.add_argument('--properties',
277 metavar='PROPERTIES',
278 help='The file containing the properties',
282 parser.add_argument('--plugins',
286 help='The path to ansible inventory plugin(s)',
291 args = parser.parse_args(sys.argv[1:])
293 f = open(args.properties, 'r')
294 lines = f.read().splitlines()
301 properties[d[0]] = d[1]
302 ansible = AnsibleInventory(properties, args.plugins)
303 inventory = ansible.generate_inventory()
305 print(json.dumps(inventory, indent=4, sort_keys=True))
307 except Exception as exp: # pylint: disable=broad-except
309 traceback.print_exc()
314 if __name__ == '__main__':