| #!/usr/bin/python | |
| # -*- coding: utf-8 -*- | |
| # Licensed to the Apache Software Foundation (ASF) under one | |
| # or more contributor license agreements. See the NOTICE file | |
| # distributed with this work for additional information | |
| # regarding copyright ownership. The ASF licenses this file | |
| # to you 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. | |
| import argparse | |
| import sys | |
| import urllib | |
| import uuid | |
| import subprocess | |
| import os | |
| import shutil | |
| import gzip | |
| import zipfile | |
| import bz2 | |
| class InstallSysTemplate(object): | |
| parser = None | |
| mountpoint = None | |
| args = None | |
| hypervisor = None | |
| systemvmtemplatepath = None | |
| systemvmtemplateurl = None | |
| managementsecretkey = None | |
| forcecleanup = None | |
| databasehostname = None | |
| databaseusername = None | |
| databaseuserpassword = None | |
| templatesuffix = None | |
| template = None | |
| fileextension = None | |
| templateName = None | |
| destDir = None | |
| fileSize = None | |
| dictionary = None | |
| def __init__(self): | |
| self.dictionary = dict(xenserver=('XenServer', 'vhd'), kvm=('KVM', 'qcow2'), vmware=('VMware', 'ova'), lxc=('LXC', 'qcow2'), hyperv=('Hyperv', 'vhd')) | |
| def parseOptions(self): | |
| self.parser = argparse.ArgumentParser(prog="System Template Installer") | |
| self.parser.add_argument("-m", "--mount-point", action="store", dest="mountpoint", help="Secondary Storage Mount Point where to install the template.", required="true") | |
| self.parser.add_argument("-H", "--hypervisor", action="store", dest="hypervisor", help="The Hypervisor name for which template need to be installed", required="true", choices=['kvm','xenserver','vmware','lxc','hyperv']) | |
| group = self.parser.add_mutually_exclusive_group(required=True) | |
| group.add_argument("-f", "--system-vm-template", action="store", dest="systemvmtemplatepath", help="The local system vm template file path") | |
| group.add_argument("-u", "--system-vm-template-url", action="store", dest="systemvmtemplateurl", help="Url to download system vm template") | |
| self.parser.add_argument("-s", "--management-secret-key", action="store", dest="managementsecretkey", help="mgmt server secret key, if you specified any when running cloudstack-setup-database, default is password", default="password") | |
| self.parser.add_argument("-F", "--force-clean-up", action="store_true", dest="forcecleanup", help="clean up system templates of specified hypervisor", default="false") | |
| self.parser.add_argument("-d", "--database-host-name", action="store", dest="databasehostname", help="Database server hostname or ip, e.g localhost", default="localhost", required="true") | |
| self.parser.add_argument("-r", "--database-user-name", action="store", dest="databaseusername", help="Database user name, e.g root", default="root", required="true") | |
| self.parser.add_argument("-p", "--database-user-password", nargs='?', action="store", dest="databaseuserpassword", help="Database password. Followed by nothing if the password is empty", default="", required="true") | |
| self.parser.add_argument("-e", "--template-suffix", action="store", dest="templatesuffix", help="Template suffix, e.g vhd, ova, qcow2",default="vhd") | |
| self.parser.add_argument("-t", "--file-extension", action="store", dest="fileextension", help="The template file extension", default="", required="true", choices=['bz2','gz','zip']) | |
| self.args = self.parser.parse_args() | |
| def populateOptions(self): | |
| self.mountpoint = self.args.mountpoint | |
| self.hypervisor = self.args.hypervisor | |
| self.fileextension = self.args.fileextension | |
| if self.args.systemvmtemplatepath: | |
| self.systemvmtemplatepath = self.args.systemvmtemplatepath | |
| if self.args.systemvmtemplateurl: | |
| self.systemvmtemplateurl = self.args.systemvmtemplateurl | |
| if self.args.managementsecretkey: | |
| self.managementsecretkey = self.args.managementsecretkey | |
| if self.args.forcecleanup: | |
| self.forcecleanup = self.args.forcecleanup | |
| if self.args.databasehostname: | |
| self.databasehostname = self.args.databasehostname | |
| if self.args.databaseusername: | |
| self.databaseusername = self.args.databaseusername | |
| if self.args.databaseuserpassword: | |
| self.databaseuserpassword = self.args.databaseuserpassword | |
| else: | |
| self.databaseuserpassword = "" | |
| if self.args.templatesuffix: | |
| self.templatesuffix = self.args.templatesuffix | |
| print 'Password for DB: %s'%self.databaseuserpassword | |
| def errorAndExit(self, msg): | |
| err = '''\n\nWe apologize for below error: | |
| *************************************************************** | |
| %s | |
| *************************************************************** | |
| Please run: | |
| cloud-install-sys-tmplt -h | |
| for full help | |
| ''' % msg | |
| sys.stderr.write(err) | |
| sys.stderr.flush() | |
| sys.exit(1) | |
| def runCmd(self, cmds): | |
| process = subprocess.Popen(' '.join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| stdout, stderr = process.communicate() | |
| print(stdout) | |
| if process.returncode != 0: | |
| raise Exception(stderr) | |
| return stdout | |
| def runMysql(self, query): | |
| try: | |
| print 'Running Query: %s' % query | |
| mysqlCmds = ['mysql', '--user=%s'%self.databaseusername, '--host=%s'%self.databasehostname, '--password=%s'%self.databaseuserpassword, '--skip-column-names', '-U', 'cloud', '-e "%s"'%query] | |
| templateId = self.runCmd(mysqlCmds) | |
| print 'TemplateId is : %s' % templateId | |
| except Exception, e: | |
| err = '''Encountering an error when executing mysql script\n%s''' % str(e) | |
| self.errorAndExit(err) | |
| return templateId | |
| def fetchTemplateDetails(self): | |
| mysqlQuery = "select max(id) from cloud.vm_template where type = 'SYSTEM' and hypervisor_type = '%s' and removed is null" | |
| ht = None | |
| hypervisorInfo = self.dictionary[self.hypervisor] | |
| ht = hypervisorInfo[0] | |
| self.templatesuffix = hypervisorInfo[1] | |
| self.template = int(self.runMysql(mysqlQuery%ht)) | |
| def downloadTemplate(self): | |
| self.systemvmtemplatepath = self.templateName + "." + self.fileextension | |
| print 'Downloading template from %s To %s' % (self.systemvmtemplateurl, self.systemvmtemplatepath) | |
| try: | |
| templateFileDownloadUrl = urllib.urlretrieve(self.systemvmtemplateurl, self.systemvmtemplatepath, reporthook=self.report) | |
| except Exception: | |
| self.errorAndExit("Failed to download template file from %s" % self.systemvmtemplateurl) | |
| def report(tmp, blocknr, blocksize, size): | |
| current = blocknr*blocksize | |
| sys.stdout.write("\rDownloading completed: {0:.2f}%".format(100.0*current/size)) | |
| def installTemplate(self): | |
| destDir = self.mountpoint + os.sep + "template" + os.sep + "tmpl" + os.sep + "1" + os.sep + str(self.template) | |
| self.destDir = destDir | |
| print 'The desination Directory is : %s' % destDir | |
| try: | |
| if self.forcecleanup: | |
| if os.path.exists(destDir): | |
| shutil.rmtree(destDir) | |
| if not os.path.exists(destDir): | |
| os.makedirs(destDir) | |
| except Exception, e: | |
| self.errorAndExit('Failed to create directories on the mounted path.. %s' % str (e)) | |
| print 'Installing Template to : %s' % destDir | |
| tmpFile = self.templateName + "." + "tmp" | |
| self.uncompressFile(tmpFile) | |
| print 'Moving the decompressed file to destination directory %s... which could take a long time, please wait' % destDir | |
| shutil.move(tmpFile, destDir + os.sep + self.templateName) | |
| def uncompressFile(self, fileName): | |
| print 'Uncompressing the file %s... which could take a long time, please wait' % self.systemvmtemplatepath | |
| if self.fileextension == 'gz': | |
| compressedFile = gzip.GzipFile(self.systemvmtemplatepath, 'rb') | |
| decompressedData = compressedFile.read() | |
| compressedFile.close() | |
| decompressedFile = file(fileName, 'wb') | |
| decompressedFile.write(decompressedData) | |
| decompressedFile.close() | |
| elif self.fileextension == 'bz2': | |
| compressedFile = bz2.BZ2File(self.systemvmtemplatepath) | |
| decompressedData = compressedFile.read() | |
| compressedFile.close() | |
| decompressedFile = file(fileName, 'wb') | |
| decompressedFile.write(decompressedData) | |
| decompressedFile.close() | |
| print '' | |
| elif self.fileextension == 'zip': | |
| zippedFile = zipfile.ZipFile(self.systemvmtemplatepath, 'r') | |
| zippedFiles = zippedFile.namelist() | |
| compressedFile = zippedFiles[0] | |
| decompressedData = zippedFile.read(compressedFile) | |
| decompressedFile = file(fileName, 'wb') | |
| decompressedFile.write(decompressedData) | |
| decompressedFile.close() | |
| zippedFile.close() | |
| print '' | |
| else: | |
| self.errorAndExit('Not supported file type %s to decompress' % self.fileextension) | |
| self.fileSize = os.path.getsize(fileName) | |
| def writeProperties(self): | |
| propertiesFile = file(self.destDir + os.sep + 'template.properties', 'wb') | |
| propertiesFile.write('filename=%s\n'%self.templateName) | |
| propertiesFile.write('description=SystemVM Template\n') | |
| propertiesFile.write('checksum=\n') | |
| propertiesFile.write('hvm=false\n') | |
| propertiesFile.write('size=%s\n'%str(self.fileSize)) | |
| propertiesFile.write('%s=true\n'%self.templatesuffix) | |
| propertiesFile.write('id=%s\n'%str(self.template)) | |
| propertiesFile.write('public=true\n') | |
| propertiesFile.write('%s.filename=%s\n'%(self.templatesuffix, self.templateName)) | |
| propertiesFile.write('uniquename=routing-%s\n'%str(self.template)) | |
| propertiesFile.write('%s.virtualsize=%s\n'%(self.templatesuffix, str(self.fileSize))) | |
| propertiesFile.write('virtualsize=%s\n'%str(self.fileSize)) | |
| propertiesFile.write('%s.size=%s'%(self.templatesuffix, str(self.fileSize))) | |
| propertiesFile.close() | |
| def run(self): | |
| try: | |
| self.parseOptions() | |
| self.populateOptions() | |
| self.fetchTemplateDetails() | |
| randomUUID = uuid.uuid1() | |
| self.templateName = str(randomUUID) + "." + self.templatesuffix | |
| if self.args.systemvmtemplateurl: | |
| self.downloadTemplate() | |
| self.installTemplate() | |
| self.writeProperties() | |
| finally: | |
| print '' | |
| print '' | |
| print "CloudStack has successfully installed system template" | |
| print '' | |
| if __name__ == "__main__": | |
| o = InstallSysTemplate() | |
| o.run() |