mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> (cherry picked from commit 43f39a1ec37de08e5b8e97dfd74a853c0aae76c7) Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
		
			
				
	
	
		
			276 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 os
 | |
| import sys
 | |
| import subprocess
 | |
| import glob
 | |
| from random import choice
 | |
| import string
 | |
| from optparse import OptionParser
 | |
| import commands
 | |
| import shutil
 | |
| 
 | |
| # squelch mysqldb spurious warnings
 | |
| import warnings
 | |
| warnings.simplefilter('ignore')
 | |
| # ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
 | |
| # ---- We do this so cloud_utils can be looked up in the following order:
 | |
| # ---- 1) Sources directory
 | |
| # ---- 2) waf configured PYTHONDIR
 | |
| # ---- 3) System Python path
 | |
| for pythonpath in (
 | |
|         "@PYTHONDIR@",
 | |
|         os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
 | |
|     ):
 | |
|         if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
 | |
| # ---- End snippet of code ----
 | |
| 
 | |
| def runCmd(cmds):
 | |
|     process = subprocess.Popen(' '.join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | |
|     stdout, stderr = process.communicate()
 | |
|     if process.returncode != 0:
 | |
|         raise Exception(stderr)
 | |
|     return stdout
 | |
| 
 | |
| class DBDeployer(object):
 | |
|     parser = None
 | |
|     options = None
 | |
|     args = None
 | |
|     isDebug = False
 | |
|     mgmtsecretkey = None
 | |
|     dbsecretkey = None
 | |
|     keyStorePassphrase = "vmops.com"
 | |
|     encryptiontype = None
 | |
|     dbConfPath = r"@MSCONF@"
 | |
|     dbDotProperties = {}
 | |
|     dbDotPropertiesIndex = 0
 | |
|     encryptionKeyFile = '@MSCONF@/key'
 | |
|     encryptionJarPath = '@COMMONLIBDIR@/lib/jasypt-1.9.2.jar'
 | |
|     success = False
 | |
|     magicString = 'This_is_a_magic_string_i_think_no_one_will_duplicate'
 | |
| 
 | |
|     def preRun(self):
 | |
|         def backUpDbDotProperties():
 | |
|             dbpPath = os.path.join(self.dbConfPath, 'db.properties')
 | |
|             copyPath = os.path.join(self.dbConfPath, 'db.properties.origin')
 | |
|             
 | |
|             if os.path.isfile(dbpPath):
 | |
|                 shutil.copy2(dbpPath, copyPath)
 | |
|         
 | |
|         backUpDbDotProperties()
 | |
|     
 | |
|     def postRun(self):
 | |
|         def cleanOrRecoverDbDotProperties():
 | |
|             dbpPath = os.path.join(self.dbConfPath, 'db.properties')
 | |
|             copyPath = os.path.join(self.dbConfPath, 'db.properties.origin')
 | |
|             if os.path.isfile(copyPath):
 | |
|                 if not self.success:
 | |
|                     shutil.copy2(copyPath, dbpPath)
 | |
|                 os.remove(copyPath)
 | |
|         
 | |
|         cleanOrRecoverDbDotProperties()
 | |
|                 
 | |
|         
 | |
|     def info(self, msg, result=None):
 | |
|         output = ""
 | |
|         if msg is not None:
 | |
|             output = "%-80s"%msg
 | |
|             
 | |
|         if result is True:
 | |
|             output += "[ \033[92m%-2s\033[0m ]\n"%"OK"
 | |
|         elif result is False:
 | |
|             output += "[ \033[91m%-6s\033[0m ]\n"%"FAILED"
 | |
|         sys.stdout.write(output)
 | |
|         sys.stdout.flush()
 | |
|     
 | |
|     def debug(self, msg):
 | |
|         msg = "DEBUG:%s"%msg
 | |
|         sys.stdout.write(msg)
 | |
|         sys.stdout.flush()
 | |
| 
 | |
|     def putDbProperty(self, key, value):
 | |
|         if self.dbDotProperties.has_key(key):
 | |
|             (oldValue, index) = self.dbDotProperties[key]
 | |
|             self.dbDotProperties[key] = (value, index)
 | |
|         else:
 | |
|             self.dbDotProperties[key] = (value, self.dbDotPropertiesIndex)
 | |
|             self.dbDotPropertiesIndex += 1
 | |
|     
 | |
|     def getDbProperty(self, key):
 | |
|         if not self.dbDotProperties.has_key(key):
 | |
|             return None
 | |
|         (value, index) = self.dbDotProperties[key]
 | |
|         return value
 | |
|         
 | |
|     def errorAndExit(self, msg):
 | |
|         self.postRun()
 | |
|         err = '''\n\nWe apologize for below error:
 | |
| ***************************************************************      
 | |
| %s
 | |
| *************************************************************** 
 | |
| Please run:
 | |
| 
 | |
|     cloud-setup-encryption -h
 | |
| 
 | |
| for full help
 | |
| ''' % msg
 | |
|         sys.stderr.write(err)
 | |
|         sys.stderr.flush()
 | |
|         sys.exit(1)
 | |
| 
 | |
|     def prepareDBFiles(self):
 | |
|         def prepareDBDotProperties():
 | |
|             dbpPath = os.path.join(self.dbConfPath, 'db.properties')
 | |
|             dbproperties = file(dbpPath).read().splitlines()
 | |
|             newdbp = []
 | |
|             emptyLine = 0
 | |
|             for line in dbproperties:
 | |
|                 passed = False
 | |
|                 line = line.strip()
 | |
|                 if line.startswith("#"): key = line; value = ''; passed = True
 | |
|                 if line == '' or line == '\n': key = self.magicString + str(emptyLine); value = ''; emptyLine += 1; passed = True
 | |
|                 
 | |
|                 try:
 | |
|                     if not passed:
 | |
|                         (key, value) = line.split('=', 1)
 | |
|                 except Exception, e:
 | |
|                     err = '''Wrong format in %s (%s):
 | |
| Besides comments beginning "#" and empty line, all key-value pairs must be in formula of
 | |
|                     key=value
 | |
| for example:
 | |
|                     db.cloud.username = cloud
 | |
|                     ''' % (dbpPath, line)
 | |
|                     self.errorAndExit(err)
 | |
|                 self.putDbProperty(key, value)
 | |
|             self.info("Preparing %s"%dbpPath, True)
 | |
|         
 | |
|         prepareDBDotProperties()    
 | |
|     
 | |
|     def finalize(self):
 | |
|         def finalizeDbProperties():
 | |
|             entries = []
 | |
|             for key in self.dbDotProperties.keys():
 | |
|                 (value, index) = self.dbDotProperties[key]
 | |
|                 if key.startswith("#"):
 | |
|                     entries.insert(index, key)
 | |
|                 elif key.startswith(self.magicString):
 | |
|                     entries.insert(index, '')
 | |
|                 else:
 | |
|                     entries.insert(index, "%s=%s"%(key, value))
 | |
|             file(os.path.join(self.dbConfPath, 'db.properties'), 'w').write('\n'.join(entries))
 | |
|         
 | |
|         self.info("Finalizing setup ...", None)
 | |
|         finalizeDbProperties()
 | |
|         self.info(None, True)
 | |
|         self.success = True # At here, we have done successfully and nothing more after this flag is set
 | |
|                         
 | |
|     def processEncryptionStuff(self):
 | |
|         def encrypt(input):
 | |
|             cmd = ['java','-classpath',self.encryptionJarPath,'org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI', 'encrypt.sh', 'input=\'%s\''%input, 'password=%s'%self.mgmtsecretkey,'verbose=false']
 | |
|             return runCmd(cmd).strip('\n')
 | |
|         
 | |
|         def saveMgmtServerSecretKey():
 | |
|             if self.encryptiontype == 'file':
 | |
|                 file(self.encryptionKeyFile, 'w').write(self.mgmtsecretkey)
 | |
|                 
 | |
|         def formatEncryptResult(value):
 | |
|             return 'ENC(%s)'%value
 | |
|         
 | |
|         def encryptDBSecretKey():
 | |
|             self.putDbProperty('db.cloud.encrypt.secret', formatEncryptResult(encrypt(self.dbsecretkey)))
 | |
| 
 | |
|         def encryptKeyStorePassphrase():
 | |
|             self.putDbProperty('db.cloud.keyStorePassphrase', formatEncryptResult(encrypt(self.keyStorePassphrase)))
 | |
|         
 | |
|         def encryptDBPassword():
 | |
|             dbPassword = self.getDbProperty('db.cloud.password')       
 | |
|             if dbPassword == '': return # Don't encrypt empty password
 | |
|             if dbPassword == None: self.errorAndExit('Cannot find db.cloud.password in %s'%os.path.join(self.dbConfPath, 'db.properties'))
 | |
|             self.putDbProperty('db.cloud.password', formatEncryptResult(encrypt(dbPassword)))
 | |
|             
 | |
|             usagePassword = self.getDbProperty('db.usage.password')
 | |
|             if usagePassword == '': return # Don't encrypt empty password
 | |
|             if usagePassword == None: self.errorAndExit('Cannot find db.usage.password in %s'%os.path.join(self.dbConfPath, 'db.properties'))
 | |
|             self.putDbProperty('db.usage.password', formatEncryptResult(encrypt(usagePassword)))
 | |
|         
 | |
|         self.info("Processing encryption ...", None)
 | |
|         self.putDbProperty("db.cloud.encryption.type", self.encryptiontype)
 | |
|         saveMgmtServerSecretKey()
 | |
|         encryptDBSecretKey()
 | |
| 	encryptKeyStorePassphrase()
 | |
|         encryptDBPassword()
 | |
|         self.info(None, True)
 | |
|         
 | |
|     def parseOptions(self):
 | |
|         def parseOtherOptions():                     
 | |
|             self.encryptiontype = self.options.encryptiontype
 | |
|             self.mgmtsecretkey = self.options.mgmtsecretkey
 | |
|             self.dbsecretkey = self.options.dbsecretkey
 | |
| 	    self.keyStorePassphrase = self.options.keyStorePassphrase
 | |
|             self.isDebug = self.options.debug
 | |
|             
 | |
|             
 | |
|         def validateParameters():
 | |
|             if self.encryptiontype != 'file' and self.encryptiontype != 'web':
 | |
|                 self.errorAndExit('Wrong encryption type %s, --encrypt-type can only be "file" or "web'%self.encryptiontype)
 | |
|             
 | |
|     #---------------------- option parsing and command line checks ------------------------
 | |
|         usage = """%prog [-e ENCRYPTIONTYPE] [-m MGMTSECRETKEY] [-k DBSECRETKEY] [--debug]
 | |
|     
 | |
|     This command sets up the CloudStack Encryption.    
 | |
|     
 | |
|     """
 | |
|         self.parser = OptionParser(usage=usage)
 | |
|         self.parser.add_option("-v", "--debug", action="store_true", dest="debug", default=False,
 | |
|                           help="If enabled, print the commands it will run as they run")
 | |
|         self.parser.add_option("-e", "--encrypt-type", action="store", type="string", dest="encryptiontype", default="file",
 | |
|                           help="Encryption method used for db password encryption. Valid values are file, web. Default is file.")
 | |
|         self.parser.add_option("-m", "--managementserver-secretkey", action="store", type="string", dest="mgmtsecretkey", default="password",
 | |
|                           help="Secret key used to encrypt confidential parameters in db.properties. A string, default is password")
 | |
|         self.parser.add_option("-k", "--database-secretkey", action="store", type="string", dest="dbsecretkey", default="password",
 | |
|                           help="Secret key used to encrypt sensitive database values. A string, default is password")
 | |
|         self.parser.add_option("-p", "--keystore-passphrase", action="store", type="string", dest="keyStorePassphrase", default="vmops.com",
 | |
|                           help="Passphrase used while generating jks file for ssl communication. A string, default is vmops.com")
 | |
|         
 | |
|         (self.options, self.args) = self.parser.parse_args()
 | |
|         parseOtherOptions()
 | |
|         validateParameters()
 | |
|     
 | |
|     def run(self):
 | |
|         try:
 | |
|             self.preRun()
 | |
|             self.parseOptions()
 | |
|             self.prepareDBFiles()            
 | |
|             self.processEncryptionStuff()
 | |
|             self.finalize()
 | |
|         finally:
 | |
|             self.postRun()
 | |
|         
 | |
|         print ''
 | |
|         print "CloudStack has successfully setup Encryption"
 | |
|         print ''
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|    o = DBDeployer()
 | |
|    o.run()
 | |
|         
 |