Plugins for configuration manager
[ta/cm-plugins.git] / activators / src / installationactivator.py
diff --git a/activators/src/installationactivator.py b/activators/src/installationactivator.py
new file mode 100755 (executable)
index 0000000..6a15e73
--- /dev/null
@@ -0,0 +1,197 @@
+#! /usr/bin/python
+# Copyright 2019 Nokia
+#
+# 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.
+
+from cmframework.apis import cmerror
+from cmframework.apis import cmactivator
+from cmdatahandlers.api import configmanager
+from cmdatahandlers.api import configerror
+import os
+import subprocess
+import json
+import pwd
+import logging
+
+class installationactivator(cmactivator.CMGlobalActivator):
+    inventory_cli = '/opt/cmframework/scripts/inventory.sh'
+    playbooks_generate_cli = '/usr/local/bin/cmcli ansible-playbooks-generate'
+    playbooks_path = '/opt/openstack-ansible/playbooks/'
+    setup_playbook = 'setup-playbook.yml'
+    presetup_playbook = 'presetup-playbook.yml'
+    bootstrapping_playbook = 'bootstrapping-playbook.yml'
+    provisioning_playbook = 'provisioning-playbook.yml'
+    postconfig_playbook = 'postconfig-playbook.yml'
+    state_file = '/etc/installation_state'
+
+    def __init__(self):
+        self.plugin_client = None
+
+    def get_subscription_info(self):
+        return '.*'
+
+    def activate_set(self, props):
+        self.activate_full()
+
+    def activate_delete(self, props):
+        self.activate_full()
+
+    def activate_full(self, target=None):
+        try:
+            properties = self.get_plugin_client().get_properties('.*')
+            if not properties:
+                return
+            propsjson = {}
+            for name, value in properties.iteritems():
+                try:
+                    propsjson[name] = json.loads(value)
+                except Exception as exp:
+                    continue
+            configman = configmanager.ConfigManager(propsjson)
+
+            hostsconfig = configman.get_hosts_config_handler()
+            installation_host = hostsconfig.get_installation_host()
+
+            installed = False
+            try:
+                configman.get_cloud_installation_date()
+                installed = True
+            except configerror.ConfigError as exp:
+                pass
+
+            if installed:
+                return
+
+            usersconf = configman.get_users_config_handler()
+            admin = usersconf.get_admin_user()
+
+            #generate high level playbooks
+            if self._run_cmd(self.playbooks_generate_cli, '/etc', 'root', os.environ.copy()):
+                raise cmerror.CMError('Failed to run %s' % self.playbooks_generate_cli)
+
+            caas_data = configman.get_caas_config_handler()
+            phase = self._get_installation_phase()
+            #first we run the setup 
+            if not phase:
+                self._set_installation_phase('setup-started')
+                phase = 'setup-started'
+            env = os.environ.copy()
+            if phase == 'setup-started':
+                env['VNF_EMBEDDED_DEPLOYMENT'] = 'false'
+                env['CONFIG_PHASE'] = 'setup'
+                env['BOOTSTRAP_OPTS'] = 'installation_controller=%s' %(installation_host)
+                self._run_setup_playbook(self.presetup_playbook, env)
+                env['BOOTSTRAP_OPTS'] = ''
+                if caas_data.get_vnf_flag():
+                    env['VNF_EMBEDDED_DEPLOYMENT'] = 'true'
+                self._run_setup_playbook(self.setup_playbook, env)
+                self._set_installation_phase('setup-ended')
+                phase = 'setup-ended'
+
+            #second we run the aio
+            if phase == 'setup-ended':
+                self._set_installation_phase('bootstrapping-started')
+                phase = 'bootstrapping-started'
+            if phase == 'bootstrapping-started':
+                env['CONFIG_PHASE'] = 'bootstrapping'
+                self._run_playbook(self.bootstrapping_playbook, admin, env)
+                self._set_installation_phase('bootstrapping-ended')
+                phase = 'bootstrapping-ended'
+
+            #3rd we run the provisioning
+            if phase == 'bootstrapping-ended':
+                self._set_installation_phase('provisioning-started')
+                phase = 'provisioning-started'
+            if phase == 'provisioning-started':
+                env['CONFIG_PHASE'] = 'provisioning'
+                self._run_playbook(self.provisioning_playbook, admin, env)
+                self._set_installation_phase('provisioning-ended')
+                phase = 'provisioning-ended'
+
+            #4th we run the postconfig
+            if phase == 'provisioning-ended':
+                self._set_installation_phase('postconfig-started')
+                phase = 'postconfig-started'
+            if phase == 'postconfig-started':
+                env['CONFIG_PHASE'] = 'postconfig'
+                env['CAAS_ONLY_DEPLOYMENT'] = 'false'
+                if caas_data.get_caas_only():
+                    env['CAAS_ONLY_DEPLOYMENT'] = 'true'
+                self._run_playbook(self.postconfig_playbook, admin, env)
+                self._set_installation_phase('postconfig-ended')
+                phase = 'postconfig-ended'
+            
+            self._set_installation_date()
+
+            self._set_state('success')
+
+        except Exception as exp:
+            self._set_state('failure')
+            raise cmerror.CMError(str(exp))
+
+    def _set_installation_phase(self, phase):
+        self.get_plugin_client().set_property('cloud.installation_phase', json.dumps(phase))
+
+    def _get_installation_phase(self):
+        phase = None
+        try:
+            phase = json.loads(self.get_plugin_client().get_property('cloud.installation_phase'))
+            logging.debug('Current installation phase cloud.installation_phase="%s"'%phase)
+        except Exception as exp:
+            pass
+        return phase
+
+    def _set_installation_date(self):
+        from time import gmtime, strftime
+        # Use ISO 8601 date format
+        times = strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())
+        self.get_plugin_client().set_property('cloud.installation_date', json.dumps(times))
+
+    def _run_playbook(self, playbook, user, env):
+        cmd = '/usr/local/bin/openstack-ansible -b -u ' + user + ' ' + playbook
+        result = self._run_cmd(cmd, self.playbooks_path, user, env)
+        if result != 0:
+            raise cmerror.CMError('Playbook %s failed' % playbook)
+        
+    def _run_setup_playbook(self, playbook, env):
+        cmd = '/usr/local/bin/setup-controller.sh ' + playbook
+        result = self._run_cmd(cmd, self.playbooks_path, 'root', env)
+        if result != 0:
+            raise cmerror.CMError('Playbook %s failed' % playbook)
+        
+    def _run_cmd(self, cmd, cwd, user, env):
+        args = cmd.split()
+        pw_record = pwd.getpwnam(user)
+        user_name = pw_record.pw_name
+        user_home_dir = pw_record.pw_dir
+        user_uid = pw_record.pw_uid
+        user_gid = pw_record.pw_gid
+        env['HOME'] = user_home_dir
+        env['LOGNAME'] = user_name
+        env['HOME'] = user_home_dir
+        env['PWD'] = cwd
+        env['USER'] = user_name
+        process = subprocess.Popen(args, preexec_fn=self._demote(user_uid, user_gid), cwd=cwd, env=env)
+        result = process.wait()
+        return result
+
+
+    def _demote(self, user_uid, user_gid):
+        def result():
+            os.setgid(user_gid)
+            os.setuid(user_uid)
+        return result
+
+    def _set_state(self, state):
+        with open(self.state_file, 'w') as f:
+            f.write(state)