Skip to content

Commit 603eab7

Browse files
Damodar ReddyAbhinandan Prateek
authored andcommitted
CLOUDSTACK-6701, CLOUDSTACK-6702:
1. Integrate System Seed Template into MSI Installer 2. Added progress bar status messages for custom actions at needed places. Signed-off-by: Abhinandan Prateek <aprateek@apache.org>
1 parent c312aa9 commit 603eab7

5 files changed

Lines changed: 275 additions & 13 deletions

File tree

client/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@
693693
<include name="**/*" />
694694
</fileset>
695695
</copy>
696+
<copy file="../scripts/storage/secondary/cloud-install-sys-tmplt.py" tofile="target/scripts/cloud-install-sys-tmplt.py" />
696697
<copy todir="./target/scripts">
697698
<fileset dir="./target/utilities/bin">
698699
<include name="**/*" />

scripts/installer/windows/WixInstallerDialog.wxs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@
5757
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
5858
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
5959
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
60+
61+
<!-- Progress Text for Custom Actions STARTs here -->
62+
<ProgressText Action="GenerateSSLKey">!(loc.GenerateSSLKey)</ProgressText>
63+
<ProgressText Action="DeployDB">!(loc.DeployDB)</ProgressText>
64+
<ProgressText Action="SetupDatabases">!(loc.SetupDatabases)</ProgressText>
65+
<ProgressText Action="DeleteDirectory">!(loc.DeleteDirectory)</ProgressText>
66+
<ProgressText Action="CopySitePackages">!(loc.CopySitePackages)</ProgressText>
67+
<ProgressText Action="SetuptoolsInstallation">!(loc.SetuptoolsInstallation)</ProgressText>
68+
<ProgressText Action="DeleteFiles">!(loc.DeleteFiles)</ProgressText>
69+
<ProgressText Action="UpdateTomcatCatalinaBase">!(loc.UpdateTomcatCatalinaBase)</ProgressText>
70+
<ProgressText Action="UpdateTomcatClassPath">!(loc.UpdateTomcatClassPath)</ProgressText>
71+
72+
<!-- Progress Text for Custom Actions ENDs here -->
6073
</UI>
6174
<UIRef Id="WixUI_Common" />
6275
</Fragment>

scripts/installer/windows/acs.wxs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@
8181
Name="CATALINA_OPTS" Permanent="no" System="yes"
8282
Value="-XX:MaxPermSize=512m -Xmx1024m -Xms256m" />
8383
<Environment Id="Path" Action="set" Name="Path"
84-
Permanent="yes" System="yes" Part="last"
85-
Value="[JAVA_HOME]\bin;[PYTHON_HOME];[SEVENZ_HOME];[ProgramFilesFolder]\cdrtools;[MYSQL]\bin" />
84+
Permanent="no" System="yes" Part="last"
85+
Value="[JAVA_HOME]bin;[PYTHON_HOME];[SEVENZ_HOME];[ProgramFilesFolder]cdrtools;[MYSQL]bin;[INSTALLDIR]scripts" />
86+
<Environment Id="PATHEXT" Action="set" Name="PATHEXT"
87+
Permanent="no" System="yes" Part="last"
88+
Value=".py" />
8689
</Component>
8790
<Component Id='setuptools' Guid='019a51dd-7fc4-4d6c-9277-13cc7b600789'>
8891
<File Id="ez_setup" Source="ez_setup.py" />
@@ -235,31 +238,31 @@
235238
</CustomAction>
236239
<CustomAction Id="UpdateTomcatCatalinaBase"
237240
ExeCommand='//US//Tomcat6 --JvmOptions=-Dcatalina.base=[CSMANAGEMENT];-Djava.io.tmpdir=[CSMANAGEMENT]\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Dcatalina.home="[TOMCATDIRECTORY]";-Xms512m;-Xmx1024m;-XX:MaxPermSize=512m;-Duser.name=cloud'
238-
Property="TOMCATDIRECTORY1" Execute="commit" Return="check" />
241+
Property="TOMCATDIRECTORY1" Execute="deferred" Return="check" />
239242
<CustomAction Id="UpdateTomcatClassPath"
240243
ExeCommand='//US//Tomcat6 --Classpath="[TOMCATDIRECTORY]\bin\bootstrap.jar";"[TOMCATDIRECTORY]\bin\tomcat-juli.jar";[CSMANAGEMENT]\conf;[CSMANAGEMENT]\lib;[CSMANAGEMENT]\setup'
241-
Property="TOMCATDIRECTORY1" Execute="commit" Return="check" />
244+
Property="TOMCATDIRECTORY1" Execute="deferred" Return="check" />
242245
<CustomAction Id="CopySitePackages" Directory='INSTALLDIR'
243246
ExeCommand='[SystemFolder]cmd.exe /c xcopy /S "[INSTALLDIR]\python-site-packages" [PYTHON_HOME]\Lib\site-packages'
244-
Execute="commit" Return="check"/>
247+
Execute="deferred" Return="check"/>
245248
<CustomAction Id="DeleteDirectory" Directory='INSTALLDIR'
246249
ExeCommand='[SystemFolder]cmd.exe /c RD /S /Q "[INSTALLDIR]\python-site-packages"'
247-
Execute="commit" Return="check" />
250+
Execute="deferred" Return="check" />
248251
<CustomAction Id="DeleteFiles" Directory='CSMANAGEMENT'
249252
ExeCommand='[SystemFolder]cmd.exe /c del "[CSMANAGEMENT]\webapps\client\WEB-INF\classes\db.properties" "[CSMANAGEMENT]\webapps\client\WEB-INF\classes\log4j*.xml"'
250-
Execute="commit" Return="check" />
253+
Execute="deferred" Return="check" />
251254
<CustomAction Id="SetuptoolsInstallation" Directory='INSTALLDIR'
252255
ExeCommand='[PYTHON_HOME]\python "[INSTALLDIR]\ez_setup.py"'
253-
Execute="commit" Return="check" />
256+
Execute="deferred" Return="check" />
254257
<CustomAction Id="GenerateSSLKey" Directory='CSMANAGEMENT'
255258
ExeCommand='[PYTHON_HOME]\python "[CSMANAGEMENT]\webapps\client\WEB-INF\classes\scripts\common\keys\ssl-keys.py" "[CSMANAGEMENT]\lib"'
256-
Execute="commit" Return="check" />
259+
Execute="deferred" Return="check" />
257260
<CustomAction Id="DeployDB" Directory='CSMANAGEMENT'
258261
ExeCommand='[PYTHON_HOME]\python "[INSTALLDIR]\scripts\cloud-setup-databases" [DB_USERNAME]:[DB_PASSWORD]@[DB_HOSTNAME] --deploy-as=root:[DB_ROOT_PASSWORD] -c "[CSMANAGEMENT]\lib" -f "[CSMANAGEMENT]\setup" -j "[CSMANAGEMENT]\webapps\client\WEB-INF\lib\jasypt-1.9.0.jar" -n "[CSMANAGEMENT]\lib\key"'
259-
Execute="commit" Return="check" />
262+
Execute="deferred" Return="check" Impersonate="no"/>
260263
<CustomAction Id="SetupDatabases" Directory='CSMANAGEMENT'
261264
ExeCommand='[PYTHON_HOME]\python "[INSTALLDIR]\scripts\cloud-setup-databases" [DB_USERNAME]:[DB_PASSWORD]@[DB_HOSTNAME] -c "[CSMANAGEMENT]\lib" -f "[CSMANAGEMENT]\setup" -j "[CSMANAGEMENT]\webapps\client\WEB-INF\lib\jasypt-1.9.0.jar" -n "[CSMANAGEMENT]\lib\key"'
262-
Execute="commit" Return="check" />
265+
Execute="deferred" Return="check" Impersonate="no"/>
263266

264267
<InstallExecuteSequence>
265268
<InstallServices Sequence="4990"></InstallServices>
@@ -270,9 +273,9 @@
270273
Installed</Custom>
271274
<Custom Action="DeleteFiles" Before="SetuptoolsInstallation">NOT Installed</Custom>
272275
<Custom Action="SetuptoolsInstallation" Before="GenerateSSLKey">NOT Installed</Custom>
273-
<Custom Action="GenerateSSLKey" Before="InstallFinalize">NOT Installed
276+
<Custom Action="GenerateSSLKey" After="PublishProduct">NOT Installed
274277
</Custom>
275-
<Custom Action="CopySitePackages" Before="InstallFinalize">NOT Installed</Custom>
278+
<Custom Action="CopySitePackages" After="GenerateSSLKey">NOT Installed</Custom>
276279
<Custom Action="DeleteDirectory" After="CopySitePackages">NOT Installed</Custom>
277280
<Custom Action="DeployDB" After="DeleteDirectory">(NOT Installed) AND (CREATE_DATABASE = "1")
278281
</Custom>

scripts/installer/windows/en-us.wxl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@
2828
<String Id="DbPasswordLabel">Password</String>
2929
<String Id="DbRootPasswordLabel">Root Password</String>
3030
<String Id="CreateDBCheckBoxLabel">Create Cloud Stack Database</String>
31+
<String Id="GenerateSSLKey">Generating SSL Keys which is needed to talk to Management Server.</String>
32+
<String Id="DeployDB">Creating Database and needed Schemas for management server.</String>
33+
<String Id="SetupDatabases">Updating db.properties with the given database configuration.</String>
34+
<String Id="DeleteDirectory">Deleting un-necessary directories from installation directory.</String>
35+
<String Id="CopySitePackages">Copying Management Server related python libraries.</String>
36+
<String Id="SetuptoolsInstallation">Installing Setup Tools for Python.</String>
37+
<String Id="DeleteFiles">Deleting un-necessary files from installation directory.</String>
38+
<String Id="UpdateTomcatCatalinaBase">Updating Tomcat Catalina Base with Management Server Home.</String>
39+
<String Id="UpdateTomcatClassPath">Updating Tomcat Classpath with necessary paths.</String>
3140

3241
<String Id="InstallVersion">Version 4.0.0.0</String>
3342
</WixLocalization>
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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\nWe 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("\rDownloading 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

Comments
 (0)