Example #1
0
class ICPDBootstrap(object):
    ArgsSignature = {
        '--help': 'string',
        '--region': 'string',
        '--stack-name': 'string',
        '--stackid': 'string',
        '--role': 'string',
        '--logfile': 'string',
        '--loglevel': 'string',
        '--trace': 'string'
    }

    def __init__(self):
        """
      Constructor
    """
        object.__init__(self)
        self.home = os.path.expanduser("~")
        self.logsHome = os.path.join(self.home, "logs")

    def makeDir(self, path):
        if not os.path.exists(path):
            os.makedirs(path)
        #endif

    #enddef

    def setupBootNode(self, icpdInstallLogFile):
        methodName = "setupBootNode"
        # copy .docker contents to bootnode from masternode

        dockerpath = "/root/.docker"
        configFile = '/.docker/config.json'
        remoteHost = "root@" + self.bootstrap.getMasterIPAddresses()[0]
        scpCmd = "scp -o StrictHostKeyChecking=no " + remoteHost + ":"
        config_scp = scpCmd + "~" + configFile + " ."

        TR.info(
            methodName, "Copy %s file from  %s using scp command %s" %
            (configFile, remoteHost, config_scp))
        self.makeDir(dockerpath)
        os.chdir(dockerpath)
        call(config_scp, shell=True, stdout=icpdInstallLogFile)

        #copy certs from masternode to bootnode

        etcDockerPath = '/etc/docker/'
        certsPath = etcDockerPath + 'certs.d/'
        registry = self.bootstrap.ClusterDNSName + ':8500'
        registryPath = certsPath + registry
        rootCert = registryPath + '/root-ca.crt'
        caCert = registryPath + '/ca.crt'
        ca_scp = scpCmd + caCert + " ."
        root_scp = scpCmd + rootCert + " ."

        TR.info(methodName,
                "Copy %s and %s file from %s" % (rootCert, caCert, remoteHost))
        TR.info(methodName, "RootCert scp command:  %s" % (root_scp))
        TR.info(methodName, "CACert scp command:  %s" % (ca_scp))

        self.makeDir(registryPath)
        os.chdir(registryPath)
        call(ca_scp, shell=True, stdout=icpdInstallLogFile)
        call(root_scp, shell=True, stdout=icpdInstallLogFile)

    #endDef

    def installICPD(self):
        """
      Install ICPD by executing the script
      
      Script will copy required pre-requiste and extract the installer and run the deploy scripts
      
      It is assumed all the pre-installation configuration steps have been completed.
    """
        methodName = "installICPD"
        TR.info(methodName, "IBM Cloud Pak for Data installation started.")
        # We really do not need this when ICP fixes the bootnode docker and certs issue
        # copy .docker contents to bootnode from masternode

        logFilePath = os.path.join(self.logsHome, "icpd_install.log")

        with open(logFilePath, "a+") as icpdInstallLogFile:
            self.setupBootNode(icpdInstallLogFile)

            # create /ibm folder and extract icp4d.tar contents to it
            TR.info(methodName, "Extract icp4d.tar to /ibm")

            self.makeDir('/ibm')
            call('tar -xvf /tmp/icp4d.tar -C /ibm/',
                 shell=True,
                 stdout=icpdInstallLogFile)
            os.chdir('/ibm')

            # cd to /ibm and give +x to installer file
            installCMD = self.installMap['installCMD']
            TR.info(methodName, "install CMD : %s" % (installCMD))
            os.chmod(installCMD, stat.S_IEXEC)

            # docker login to registry to be on safer side
            registry = self.bootstrap.ClusterDNSName + ':8500'
            dockerCMD = 'docker login ' + registry + '/zen -u ' + self.bootstrap.AdminUser + ' -p ' + self.bootstrap.AdminPassword
            call(dockerCMD, shell=True, stdout=icpdInstallLogFile)

            #create namepsace
            namespaceCMD = "sudo kubectl create -f /root/zen.yaml"
            call(namespaceCMD, shell=True, stdout=icpdInstallLogFile)

            #create docker secret
            secretCMD = "sudo kubectl create secret -n zen docker-registry icp4d-anyuid-docker-pull --docker-server=" + registry + " --docker-username=admin --docker-password="******"2.1.0.1"):
                portValue = '\n31843\nY'
            #endIf
            input = '\nA\nzen\nY' + portValue + '\n' + registry + '/zen\n\nY\nN\n' + str(
                storageclass) + '\nY\nY\nY'
            TR.info(methodName, "Input for installer : %s" % (input))

            runInstaller = 'sudo /ibm/' + installCMD
            process = Popen(runInstaller,
                            shell=True,
                            stdin=PIPE,
                            stdout=icpdInstallLogFile,
                            stderr=icpdInstallLogFile,
                            close_fds=True)
            stdoutdata, stderrdata = process.communicate(input)

            TR.info(methodName, "%s" % (stdoutdata))
            manageUser = "******" + self.bootstrap.AdminPassword
            TR.info(methodName, "Start manageUser")
            call(manageUser, shell=True, stdout=icpdInstallLogFile)
            TR.info(methodName, "End manageUser")

            TR.info(methodName, "Start Activate trial")
            icpdUrl = "https://" + self.bootstrap.ClusterDNSName + ":31843"
            activatetrial = "sudo python /root/activate-trial.py " + icpdUrl + " admin " + self.bootstrap.AdminPassword + " /root/trial.lic"
            call(activatetrial, shell=True, stdout=icpdInstallLogFile)
            TR.info(methodName, "End Activate trial")
            TR.info(methodName,
                    "IBM Cloud Pak for Data installation completed.")
            os.remove("/root/trial.lic")

            source_file = open("/root/activate-license.sh").read()
            source_file = source_file.replace('<CLUSTERDNSNAME>',
                                              self.bootstrap.ClusterDNSName)
            updated_file = open("/root/activate-license.sh", 'w')
            updated_file.write(source_file)
            updated_file.close()

            TR.info(methodName, "Create configmap for addons")
            source_file = open("/root/configmap.yaml").read()
            source_file = source_file.replace('<CLUSTERDNSNAME>',
                                              self.bootstrap.ClusterDNSName)
            updated_file = open("/root/configmap.yaml", 'w')
            updated_file.write(source_file)
            updated_file.close()
            # run kubectl create configmap.yaml
            kubectlCMD = "sudo kubectl create -f /root/configmap.yaml"
            check_call(kubectlCMD, shell=True, stdout=icpdInstallLogFile)
            TR.info(methodName, "Created configmap for addons")

            addonList = self.bootstrap.Addons.split(",")
            if (addonList != ['']):
                for addon in addonList:
                    TR.info(methodName, "Addon to be installed %s" % addon)
                    installAddon = "sudo python /root/deploy_icpd_addons.py " + addon
                    TR.info(
                        methodName,
                        "Intitate addon %s installation with following command %s"
                        % (addon, installAddon))
                    retcode = check_call(installAddon,
                                         shell=True,
                                         stdout=icpdInstallLogFile)
                    TR.info(
                        methodName,
                        "Return code for addon %s installation is %s" %
                        (addon, retcode))
                #endFor
            #endIf
            TR.info(
                methodName,
                "IBM Cloud Pak for Data Addons %s installation completed." %
                addonList)
            url = "wget -q -O - http://169.254.169.254/latest/meta-data/instance-id"
            bootInstanceId = check_output(url, shell=True)
            self.bootstrap.putSSMParameter(
                "/%s/bootInstanceId" % self.bootstrap.rootStackName,
                bootInstanceId,
                description="Boot InstanceId of %s" %
                self.bootstrap.rootStackName)
            TR.info(methodName, "Set flag for stack creation completion")
            self.bootstrap.putSSMParameter(
                "/%s/stackStatus" % self.bootstrap.rootStackName,
                "Created",
                description=" %s is created successfully." %
                self.bootstrap.rootStackName)

            #endFor
        #endwith

    #endDef

    def signalWaitHandle(self, eth, etm, ets):
        """
      Send a status signal to the "install completed" wait condition via the pre-signed URL
      provided to the stack.
      
      If the instance rc is 0, we send a --success true 
      If the instance rc is non-zero we send a --success false
      More detail on the status is provided in the --reason option of the signal.

      NOTE: A failure signal (--success false) causes a rollback of the CloudFormation templates.
      If the deployer does not use --disable-rollback, then the VMs are deleted and doing a post
      mortem can be more difficult. 
    """
        methodName = 'signalWaitHandle'
        try:
            if (self.rc == 0):
                success = 'true'
                status = 'SUCCESS'
            else:
                success = 'false'
                status = 'FAILURE: Check boot node logs in S3 log bucket or on the boot node EC2 instance in /root/logs bootstrap.log and /opt/icp/%s/cluster/logs install.log.*' % self.bootstrap.ICPVersion
            #endIf
            data = "%s: IBM Cloud Pak installation elapsed time: %d:%02d:%02d" % (
                status, eth, etm, ets)
            TR.info(
                methodName, "Signaling: %s with status: %s, data: %s" %
                (self.bootstrap.ICPDInstallationCompletedURL, status, data))
            # Deployer should use --disable-rollback to avoid deleting the stack on failures and allow a post mortem.
            check_call([
                '/usr/local/bin/cfn-signal', '--success', success, '--id',
                self.bootStackId, '--reason', status, '--data', data,
                self.bootstrap.ICPDInstallationCompletedURL
            ])
        except CalledProcessError as e:
            TR.error(
                methodName,
                "ERROR return code: %s, Exception: %s" % (e.returncode, e), e)
            raise e
        #endTry

    #endDef

    def main(self, argv):
        methodName = "main"
        self.rc = 0
        self.bootstrap = Bootstrap()

        try:
            beginTime = Utilities.currentTimeMillis()
            cmdLineArgs = Utilities.getInputArgs(self.ArgsSignature, argv[1:])
            trace, logFile = self.bootstrap._configureTraceAndLogging(
                cmdLineArgs)
            TR.info(
                methodName,
                "BOOT0101I BEGIN Bootstrap AWS ICPD Quickstart version 1.0.0.")
            if (trace):
                TR.info(
                    methodName,
                    "BOOT0102I Tracing with specification: '%s' to log file: '%s'"
                    % (trace, logFile))
            #endIf

            region = cmdLineArgs.get('region')
            self.bootstrap.region = region
            self.bootstrap.role = cmdLineArgs.get('role')
            self.bootstrap.fqdn = socket.getfqdn()

            self.bootStackId = cmdLineArgs.get('stackid')
            self.bootstrap.rootStackName = cmdLineArgs.get('stack-name')

            self.bootstrap._init(self.bootstrap.rootStackName,
                                 self.bootStackId)
            TR.info(methodName,
                    "Addons to be installed %s" % self.bootstrap.Addons)
            self.logExporter = LogExporter(
                region=self.bootstrap.region,
                bucket=self.bootstrap.ICPDeploymentLogsBucketName,
                keyPrefix='logs/%s' % self.bootstrap.rootStackName,
                role=self.bootstrap.role,
                fqdn=self.bootstrap.fqdn)
            self.icpHome = "/opt/icp/%s" % self.bootstrap.ICPVersion
            installMapPath = os.path.join(self.home, "maps",
                                          "icpd-install-artifact-map.yaml")
            self.installMap = self.bootstrap.loadInstallMap(
                mapPath=installMapPath,
                version=self.bootstrap.ICPDVersion,
                region=self.bootstrap.region)
            icpdS3Path = "{version}/{object}".format(
                version=self.installMap['version'],
                object=self.installMap['icpd-base-install-archive'])
            destPath = "/tmp/icp4d.tar"
            storageClassCmd = "kubectl get storageclass | nl | grep aws-efs | awk '{print $1}'"
            TR.info(
                methodName,
                "check_output Get StorageClass value from kubectl %s" %
                (storageClassCmd))
            self.storageclassValue = check_output(
                ['bash', '-c', storageClassCmd])
            TR.info(
                methodName, "check_output  StorageclassValue returned : %s" %
                (self.storageclassValue))
            self.bootstrap.getS3Object(self.bootstrap.ICPDArchiveBucketName,
                                       icpdS3Path, destPath)
            self.stackIds = self.bootstrap._getStackIds(self.bootStackId)
            self.bootstrap._getHosts(self.stackIds)

            self.installICPD()
        except ExitException:
            pass  # ExitException is used as a "goto" end of program after emitting help info

        except Exception, e:
            TR.error(methodName, "ERROR: %s" % e, e)
            self.rc = 1

        except BaseException, e:
            TR.error(methodName, "UNEXPECTED ERROR: %s" % e, e)
            self.rc = 1