summaryrefslogtreecommitdiffstats
path: root/compass-deck/deployment/installers/installer.py
diff options
context:
space:
mode:
Diffstat (limited to 'compass-deck/deployment/installers/installer.py')
-rw-r--r--compass-deck/deployment/installers/installer.py291
1 files changed, 291 insertions, 0 deletions
diff --git a/compass-deck/deployment/installers/installer.py b/compass-deck/deployment/installers/installer.py
new file mode 100644
index 0000000..cfeb9e8
--- /dev/null
+++ b/compass-deck/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