1+ #!/usr/bin/python
2+ # -*- coding: utf-8 -*-
3+ # Licensed to the Apache Software Foundation (ASF) under one
4+ # or more contributor license agreements. See the NOTICE file
5+ # distributed with this work for additional information
6+ # regarding copyright ownership. The ASF licenses this file
7+ # to you under the Apache License, Version 2.0 (the
8+ # "License"); you may not use this file except in compliance
9+ # with the License. You may obtain a copy of the License at
10+ #
11+ # http://www.apache.org/licenses/LICENSE-2.0
12+ #
13+ # Unless required by applicable law or agreed to in writing,
14+ # software distributed under the License is distributed on an
15+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+ # KIND, either express or implied. See the License for the
17+ # specific language governing permissions and limitations
18+ # under the License.
19+
20+ import argparse
21+ import sys
22+ import urllib
23+ import uuid
24+ import subprocess
25+ import os
26+ import shutil
27+ import gzip
28+ import zipfile
29+ import bz2
30+
31+ class InstallSysTemplate (object ):
32+ parser = None
33+ mountpoint = None
34+ args = None
35+ hypervisor = None
36+ systemvmtemplatepath = None
37+ systemvmtemplateurl = None
38+ managementsecretkey = None
39+ forcecleanup = None
40+ databasehostname = None
41+ databaseusername = None
42+ databaseuserpassword = None
43+ templatesuffix = None
44+ template = None
45+ fileextension = None
46+ templateName = None
47+ destDir = None
48+ fileSize = None
49+ dictionary = None
50+
51+ def __init__ (self ):
52+ self .dictionary = dict (xenserver = ('XenServer' , 'vhd' ), kvm = ('KVM' , 'qcow2' ), vmware = ('VMware' , 'ova' ), lxc = ('LXC' , 'qcow2' ), hyperv = ('Hyperv' , 'vhd' ))
53+
54+ def parseOptions (self ):
55+ self .parser = argparse .ArgumentParser (prog = "System Template Installer" )
56+ self .parser .add_argument ("-m" , "--mount-point" , action = "store" , dest = "mountpoint" , help = "Secondary Storage Mount Point where to install the temlate." , required = "true" )
57+ 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' ])
58+ group = self .parser .add_mutually_exclusive_group (required = True )
59+ group .add_argument ("-f" , "--system-vm-template" , action = "store" , dest = "systemvmtemplatepath" , help = "The local system vm template file path" )
60+ group .add_argument ("-u" , "--system-vm-template-url" , action = "store" , dest = "systemvmtemplateurl" , help = "Url to download system vm template" )
61+ 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" )
62+ self .parser .add_argument ("-F" , "--force-clean-up" , action = "store_true" , dest = "forcecleanup" , help = "clean up system templates of specified hypervisor" , default = "false" )
63+ 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" )
64+ self .parser .add_argument ("-r" , "--database-user-name" , action = "store" , dest = "databaseusername" , help = "Database user name, e.g root" , default = "root" , required = "true" )
65+ 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" )
66+ self .parser .add_argument ("-e" , "--template-suffix" , action = "store" , dest = "templatesuffix" , help = "Template suffix, e.g vhd, ova, qcow2" ,default = "vhd" )
67+ self .parser .add_argument ("-t" , "--file-extension" , action = "store" , dest = "fileextension" , help = "The template file extension" , default = "" , required = "true" , choices = ['bz2' ,'gz' ,'zip' ])
68+
69+ self .args = self .parser .parse_args ()
70+
71+ def populateOptions (self ):
72+ self .mountpoint = self .args .mountpoint
73+ self .hypervisor = self .args .hypervisor
74+ self .fileextension = self .args .fileextension
75+ if self .args .systemvmtemplatepath :
76+ self .systemvmtemplatepath = self .args .systemvmtemplatepath
77+ if self .args .systemvmtemplateurl :
78+ self .systemvmtemplateurl = self .args .systemvmtemplateurl
79+ if self .args .managementsecretkey :
80+ self .managementsecretkey = self .args .managementsecretkey
81+ if self .args .forcecleanup :
82+ self .forcecleanup = self .args .forcecleanup
83+ if self .args .databasehostname :
84+ self .databasehostname = self .args .databasehostname
85+ if self .args .databaseusername :
86+ self .databaseusername = self .args .databaseusername
87+ if self .args .databaseuserpassword :
88+ self .databaseuserpassword = self .args .databaseuserpassword
89+ else :
90+ self .databaseuserpassword = ""
91+ if self .args .templatesuffix :
92+ self .templatesuffix = self .args .templatesuffix
93+ print 'Password for DB: %s' % self .databaseuserpassword
94+
95+ def errorAndExit (self , msg ):
96+ err = '''\n \n We apologize for below error:
97+ ***************************************************************
98+ %s
99+ ***************************************************************
100+ Please run:
101+
102+ cloud-install-sys-tmplt -h
103+
104+ for full help
105+ ''' % msg
106+ sys .stderr .write (err )
107+ sys .stderr .flush ()
108+ sys .exit (1 )
109+
110+ def runCmd (self , cmds ):
111+ process = subprocess .Popen (' ' .join (cmds ), shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
112+ stdout , stderr = process .communicate ()
113+ print (stdout )
114+ if process .returncode != 0 :
115+ raise Exception (stderr )
116+ return stdout
117+
118+ def runMysql (self , query ):
119+ try :
120+ print 'Running Query: %s' % query
121+ mysqlCmds = ['mysql' , '--user=%s' % self .databaseusername , '--host=%s' % self .databasehostname , '--password=%s' % self .databaseuserpassword , '--skip-column-names' , '-U' , 'cloud' , '-e "%s"' % query ]
122+ templateId = self .runCmd (mysqlCmds )
123+ print 'TemplateId is : %s' % templateId
124+ except Exception , e :
125+ err = '''Encountering an error when executing mysql script\n %s''' % str (e )
126+ self .errorAndExit (err )
127+ return templateId
128+
129+ def fetchTemplateDetails (self ):
130+ mysqlQuery = "select max(id) from cloud.vm_template where type = 'SYSTEM' and hypervisor_type = '%s' and removed is null"
131+ ht = None
132+ hypervisorInfo = self .dictionary [self .hypervisor ]
133+ ht = hypervisorInfo [0 ]
134+ self .templatesuffix = hypervisorInfo [1 ]
135+
136+ self .template = int (self .runMysql (mysqlQuery % ht ))
137+
138+ def downloadTemplate (self ):
139+ self .systemvmtemplatepath = self .templateName + "." + self .fileextension
140+ print 'Downloading template from %s To %s' % (self .systemvmtemplateurl , self .systemvmtemplatepath )
141+ try :
142+ templateFileDownloadUrl = urllib .urlretrieve (self .systemvmtemplateurl , self .systemvmtemplatepath , reporthook = self .report )
143+ except Exception :
144+ self .errorAndExit ("Failed to download template file from %s" % self .systemvmtemplateurl )
145+
146+ def report (tmp , blocknr , blocksize , size ):
147+ current = blocknr * blocksize
148+ sys .stdout .write ("\r Downloading completed: {0:.2f}%" .format (100.0 * current / size ))
149+
150+ def installTemplate (self ):
151+ destDir = self .mountpoint + os .sep + "template" + os .sep + "tmpl" + os .sep + "1" + os .sep + str (self .template )
152+ self .destDir = destDir
153+ print 'The desination Directory is : %s' % destDir
154+ try :
155+ if self .forcecleanup :
156+ if os .path .exists (destDir ):
157+ shutil .rmtree (destDir )
158+ if not os .path .exists (destDir ):
159+ os .makedirs (destDir )
160+ except Exception , e :
161+ self .errorAndExit ('Failed to create directories on the mounted path.. %s' % str (e ))
162+ print 'Installing Template to : %s' % destDir
163+ tmpFile = self .templateName + "." + "tmp"
164+ self .uncompressFile (tmpFile )
165+ print 'Moving the decompressed file to destination directory %s... which could take a long time, please wait' % destDir
166+ shutil .move (tmpFile , destDir + os .sep + self .templateName )
167+
168+ def uncompressFile (self , fileName ):
169+ print 'Uncompressing the file %s... which could take a long time, please wait' % self .systemvmtemplatepath
170+ if self .fileextension == 'gz' :
171+ compressedFile = gzip .GzipFile (self .systemvmtemplatepath , 'rb' )
172+ decompressedData = compressedFile .read ()
173+ compressedFile .close ()
174+ decompressedFile = file (fileName , 'wb' )
175+ decompressedFile .write (decompressedData )
176+ decompressedFile .close ()
177+ elif self .fileextension == 'bz2' :
178+ compressedFile = bz2 .BZ2File (self .systemvmtemplatepath )
179+ decompressedData = compressedFile .read ()
180+ compressedFile .close ()
181+ decompressedFile = file (fileName , 'wb' )
182+ decompressedFile .write (decompressedData )
183+ decompressedFile .close ()
184+ print ''
185+ elif self .fileextension == 'zip' :
186+ zippedFile = zipfile .ZipFile (self .systemvmtemplatepath , 'r' )
187+ zippedFiles = zippedFile .namelist ()
188+ compressedFile = zippedFiles [0 ]
189+ decompressedData = zippedFile .read (compressedFile )
190+ decompressedFile = file (fileName , 'wb' )
191+ decompressedFile .write (decompressedData )
192+ decompressedFile .close ()
193+ zippedFile .close ()
194+ print ''
195+ else :
196+ self .errorAndExit ('Not supported file type %s to decompress' % self .fileextension )
197+ self .fileSize = os .path .getsize (fileName )
198+
199+ def writeProperties (self ):
200+ propertiesFile = file (self .destDir + os .sep + 'template.properties' , 'wb' )
201+ propertiesFile .write ('filename=%s\n ' % self .templateName )
202+ propertiesFile .write ('description=SystemVM Template\n ' )
203+ propertiesFile .write ('checksum=\n ' )
204+ propertiesFile .write ('hvm=false\n ' )
205+ propertiesFile .write ('size=%s\n ' % str (self .fileSize ))
206+ propertiesFile .write ('%s=true\n ' % self .templatesuffix )
207+ propertiesFile .write ('id=%s\n ' % str (self .template ))
208+ propertiesFile .write ('public=true\n ' )
209+ propertiesFile .write ('%s.filename=%s\n ' % (self .templatesuffix , self .templateName ))
210+ propertiesFile .write ('uniquename=routing-%s\n ' % str (self .template ))
211+ propertiesFile .write ('%s.virtualsize=%s\n ' % (self .templatesuffix , str (self .fileSize )))
212+ propertiesFile .write ('virtualsize=%s\n ' % str (self .fileSize ))
213+ propertiesFile .write ('%s.size=%s' % (self .templatesuffix , str (self .fileSize )))
214+
215+ propertiesFile .close ()
216+
217+ def run (self ):
218+ try :
219+ self .parseOptions ()
220+ self .populateOptions ()
221+ self .fetchTemplateDetails ()
222+ randomUUID = uuid .uuid1 ()
223+ self .templateName = str (randomUUID ) + "." + self .templatesuffix
224+ if self .args .systemvmtemplateurl :
225+ self .downloadTemplate ()
226+ self .installTemplate ()
227+ self .writeProperties ()
228+ finally :
229+ print ''
230+ print ''
231+ print "CloudStack has successfully installed system template"
232+ print ''
233+
234+ if __name__ == "__main__" :
235+ o = InstallSysTemplate ()
236+ o .run ()
0 commit comments