diff options
Diffstat (limited to 'compass-tasks/deployment/installers/installer.py')
-rw-r--r-- | compass-tasks/deployment/installers/installer.py | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/compass-tasks/deployment/installers/installer.py b/compass-tasks/deployment/installers/installer.py new file mode 100644 index 0000000..cfeb9e8 --- /dev/null +++ b/compass-tasks/deployment/installers/installer.py @@ -0,0 +1,291 @@ +# Copyright 2014 Huawei Technologies Co. Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__author__ = "Grace Yu (grace.yu@huawei.com)" + + +"""Module to provider installer interface. +""" +from Cheetah.Template import Template +from copy import deepcopy +import imp +import logging +import os +import simplejson as json + +from compass.deployment.installers.config_manager import BaseConfigManager +from compass.utils import setting_wrapper as compass_setting +from compass.utils import util + + +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) + + +class BaseInstaller(object): + """Interface for installer.""" + NAME = 'installer' + + def __repr__(self): + return '%r[%r]' % (self.__class__.__name__, self.NAME) + + def deploy(self, **kwargs): + """virtual method to start installing process.""" + raise NotImplementedError + + def clean_progress(self, **kwargs): + raise NotImplementedError + + def delete_hosts(self, **kwargs): + """Delete hosts from installer server.""" + raise NotImplementedError + + def redeploy(self, **kwargs): + raise NotImplementedError + + def ready(self, **kwargs): + pass + + def cluster_ready(self, **kwargs): + pass + + def get_tmpl_vars_from_metadata(self, metadata, config): + """Get variables dictionary for rendering templates from metadata. + + :param dict metadata: The metadata dictionary. + :param dict config: The + """ + template_vars = {} + self._get_tmpl_vars_helper(metadata, config, template_vars) + + return template_vars + + def _get_key_mapping(self, metadata, key, is_regular_key): + """Get the keyword which the input key maps to. + + This keyword will be added to dictionary used to render templates. + + If the key in metadata has a mapping to another keyword which is + used for templates, then return this keyword. If the key is started + with '$', which is a variable in metadata, return the key itself as + the mapping keyword. If the key has no mapping, return None. + + :param dict metadata: metadata/submetadata dictionary. + :param str key: The keyword defined in metadata. + :param bool is_regular_key: False when the key defined in metadata + is a variable(starting with '$'). + """ + mapping_to = key + if is_regular_key: + try: + mapping_to = metadata['_self']['mapping_to'] + except Exception: + mapping_to = None + + return mapping_to + + def _get_submeta_by_key(self, metadata, key): + """Get submetadata dictionary. + + Based on current metadata key. And + determines the input key is a regular string keyword or a variable + keyword defined in metadata, which starts with '$'. + + :param dict metadata: The metadata dictionary. + :param str key: The keyword defined in the metadata. + """ + if key in metadata: + return (True, metadata[key]) + + temp = deepcopy(metadata) + if '_self' in temp: + del temp['_self'] + meta_key = temp.keys()[0] + if meta_key.startswith("$"): + return (False, metadata[meta_key]) + + raise KeyError("'%s' is invalid in metadata '%s'!" % (key, metadata)) + + def _get_tmpl_vars_helper(self, metadata, config, output): + for key, config_value in sorted(config.iteritems()): + is_regular_key, sub_meta = self._get_submeta_by_key(metadata, key) + mapping_to = self._get_key_mapping(sub_meta, key, is_regular_key) + + if isinstance(config_value, dict): + if mapping_to: + new_output = output[mapping_to] = {} + else: + new_output = output + + self._get_tmpl_vars_helper(sub_meta, config_value, new_output) + + elif mapping_to: + output[mapping_to] = config_value + + def get_config_from_template(self, tmpl_path, vars_dict): + logging.debug("template path is %s", tmpl_path) + logging.debug("vars_dict is %s", vars_dict) + + if not os.path.exists(tmpl_path) or not vars_dict: + logging.info("Template dir or vars_dict is None!") + return {} + + searchList = [] + copy_vars_dict = deepcopy(vars_dict) + for key, value in vars_dict.iteritems(): + if isinstance(value, dict): + temp = copy_vars_dict[key] + del copy_vars_dict[key] + searchList.append(temp) + searchList.append(copy_vars_dict) + + # Load base template first if it exists + base_config = {} + base_tmpl_path = os.path.join(os.path.dirname(tmpl_path), 'base.tmpl') + if os.path.isfile(base_tmpl_path) and base_tmpl_path != tmpl_path: + base_tmpl = Template(file=base_tmpl_path, searchList=searchList) + base_config = json.loads(base_tmpl.respond(), encoding='utf-8') + base_config = json.loads(json.dumps(base_config), encoding='utf-8') + + # Load specific template for current adapter + tmpl = Template(file=open(tmpl_path, "r"), searchList=searchList) + config = json.loads(tmpl.respond(), encoding='utf-8') + config = json.loads(json.dumps(config), encoding='utf-8') + + # Merge the two outputs + config = util.merge_dict(base_config, config) + + logging.debug("get_config_from_template resulting %s", config) + return config + + @classmethod + def get_installer(cls, name, path, adapter_info, cluster_info, hosts_info): + try: + mod_file, path, descr = imp.find_module(name, [path]) + if mod_file: + mod = imp.load_module(name, mod_file, path, descr) + config_manager = BaseConfigManager(adapter_info, cluster_info, + hosts_info) + return getattr(mod, mod.NAME)(config_manager) + + except ImportError as exc: + logging.error('No such module found: %s', name) + logging.exception(exc) + + return None + + +class OSInstaller(BaseInstaller): + """Interface for os installer.""" + NAME = 'OSInstaller' + INSTALLER_BASE_DIR = os.path.join(CURRENT_DIR, 'os_installers') + + def get_oses(self): + """virtual method to get supported oses. + + :returns: list of str, each is the supported os version. + """ + return [] + + @classmethod + def get_installer(cls, name, adapter_info, cluster_info, hosts_info): + if name is None: + logging.info("Installer name is None! No OS installer loaded!") + return None + + path = os.path.join(cls.INSTALLER_BASE_DIR, name) + installer = super(OSInstaller, cls).get_installer(name, path, + adapter_info, + cluster_info, + hosts_info) + + if not isinstance(installer, OSInstaller): + logging.info("Installer '%s' is not an OS installer!" % name) + return None + + return installer + + def poweron(self, host_id): + pass + + def poweroff(self, host_id): + pass + + def reset(self, host_id): + pass + + +class PKInstaller(BaseInstaller): + """Interface for package installer.""" + NAME = 'PKInstaller' + INSTALLER_BASE_DIR = os.path.join(CURRENT_DIR, 'pk_installers') + + def generate_installer_config(self): + raise NotImplementedError( + 'generate_installer_config is not defined in %s', + self.__class__.__name__ + ) + + def get_target_systems(self): + """virtual method to get available target_systems for each os. + + :param oses: supported os versions. + :type oses: list of st + + :returns: dict of os_version to target systems as list of str. + """ + return {} + + def get_roles(self, target_system): + """virtual method to get all roles of given target system. + + :param target_system: target distributed system such as OpenStack. + :type target_system: str + + :returns: dict of role to role description as str. + """ + return {} + + def os_ready(self, **kwargs): + pass + + def cluster_os_ready(self, **kwargs): + pass + + def serialize_config(self, config, destination): + with open(destination, "w") as f: + f.write(config) + + @classmethod + def get_installer(cls, name, adapter_info, cluster_info, hosts_info): + if name is None: + logging.info("Install name is None. No package installer loaded!") + return None + + path = os.path.join(cls.INSTALLER_BASE_DIR, name) + if not os.path.exists(path): + path = os.path.join(os.path.join(os.path.join( + compass_setting.PLUGINS_DIR, name), "implementation"), name) + if not os.path.exists(path): + logging.info("Installer '%s' does not exist!" % name) + return None + installer = super(PKInstaller, cls).get_installer(name, path, + adapter_info, + cluster_info, + hosts_info) + + if not isinstance(installer, PKInstaller): + logging.info("Installer '%s' is not a package installer!" % name) + return None + + return installer |