mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1615 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1615 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
 | |
|   #
 | |
|   #  Copyright (C) 2011 Citrix Systems, Inc.  All rights reserved
 | |
|   #
 | |
|   #
 | |
|   # This software is licensed under the GNU General Public License v3 or later.
 | |
|   #
 | |
|   # It is free software: you can redistribute it and/or modify
 | |
|   # it under the terms of the GNU General Public License as published by
 | |
|   # the Free Software Foundation, either version 3 of the License, or any later version.
 | |
|   # This program is distributed in the hope that it will be useful,
 | |
|   # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|   # GNU General Public License for more details.
 | |
|   #
 | |
|   # You should have received a copy of the GNU General Public License
 | |
|   # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|   #
 | |
|   #
 | |
|  
 | |
| 
 | |
| from xml.dom.minidom import *
 | |
| import urllib2
 | |
| import MySQLdb
 | |
| from XenAPI  import *
 | |
| import time
 | |
| import datetime
 | |
| import os
 | |
| import sys
 | |
| import paramiko
 | |
| import traceback
 | |
| 
 | |
| ### Logging functions
 | |
| 
 | |
| def getTimestamp():
 | |
|     return datetime.datetime.now().strftime("%m/%d/%Y | %I:%M:%S %p")
 | |
| 
 | |
| def nonExitingLogDecorator(entryMessage):
 | |
|     return genDecoratorFn(entryMessage, False, False)
 | |
| 
 | |
| def basicLogDecorator(entryMessage):
 | |
|     return genDecoratorFn(entryMessage, True, True)
 | |
| 
 | |
| def verboseLogDecorator(entryMessage):
 | |
|     return genDecoratorFn(entryMessage, False, True)
 | |
| 
 | |
| def genDecoratorFn(entryMessage, printToScreen, exitOnError):
 | |
|     def wrap(f):
 | |
|         def g(*args):
 | |
|             writeToLog("", printToScreen)
 | |
|             writeToLog(getTimestamp() + " | " + entryMessage, printToScreen)
 | |
|             
 | |
|             if (len(args) > 0):
 | |
|                 argString = ""
 | |
|                 for i in range(len(args)):
 | |
|                     arg = args[i]
 | |
|                     argString += str(arg)
 | |
|                     if (i != len(args) - 1):
 | |
|                         argString += ", "
 | |
|                 writeToLog("args: " + argString, False)
 | |
|             
 | |
|             returnValue = None
 | |
|             try:
 | |
|                 returnValue = f(*args)
 | |
|             except SystemExit:
 | |
|                 sys.exit(1)
 | |
|             except Exception, e:                
 | |
|                 if (exitOnError):
 | |
|                     handleError(str(e), True)
 | |
|                     traceback.print_exc(file = GLOBALS["LOG_FILE"])
 | |
|                     sys.exit(1)
 | |
|                 else:
 | |
|                     return False
 | |
| 
 | |
|             if (returnValue in (None, False)):                
 | |
|                 if (exitOnError):
 | |
|                     writeToLog(str(f) + " returned " + str(returnValue), False)
 | |
|                     handleError(None, True)
 | |
|                     sys.exit(1)
 | |
|                 else:
 | |
|                     return False
 | |
|             else:
 | |
|                 return returnValue
 | |
|         return g
 | |
|     return wrap
 | |
| 
 | |
| def handleError(msg, printToScreen):
 | |
|     writeToLog(getTimestamp() + " | " + "Failed to complete this step.", printToScreen)
 | |
|     if (msg != None):
 | |
|         writeToLog("Details: " + msg, printToScreen)
 | |
|     
 | |
| def writeToLog(message, printToScreen):
 | |
|     logFile = GLOBALS.get("LOG_FILE")
 | |
|     if (logFile != None):
 | |
|         logFile.write(message)
 | |
|         logFile.write("\n")
 | |
|     if (printToScreen):
 | |
|         print message
 | |
| 
 | |
| ### Util classes
 | |
| 
 | |
| class System:
 | |
|     def __init__(self, managementServerIp, asyncApi, xenServerIp, xenServerPassword, xenServerPasswordMap, dbName, dbLogin, dbPassword, zoneId, templateId, isoId, defaultServiceOfferingId, defaultDiskOfferingId):
 | |
|         self.zoneId = zoneId
 | |
|         self.templateId = templateId
 | |
|         self.isoId = isoId
 | |
|         self.defaultServiceOfferingId = defaultServiceOfferingId
 | |
|         self.defaultDiskOfferingId = defaultDiskOfferingId
 | |
|         self.api = System.API(managementServerIp, asyncApi)
 | |
|         if (dbPassword == None):
 | |
|             dbPassword = ""
 | |
|         self.db = System.DB(managementServerIp, dbName, dbLogin, dbPassword)
 | |
| 
 | |
|         self.xenServerIp = None
 | |
|         self.xenapi = None
 | |
|         if (xenServerIp != None or xenServerPasswordMap != None):
 | |
|             self.findXenApi(xenServerIp, xenServerPassword, xenServerPasswordMap)
 | |
|             self.controlDomainRef = self.findControlDomainRef()
 | |
|             self.sshConn = paramiko.SSHClient()
 | |
|             self.sshConn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 | |
|             self.sshConn.connect(self.xenServerIp, username = "root", password = self.xenServerPassword)
 | |
| 
 | |
|     @verboseLogDecorator("Finding the XenAPI connection...")
 | |
|     def findXenApi(self, xenServerIp, xenServerPassword, xenServerPasswordMap):
 | |
|         if (xenServerPasswordMap != None):
 | |
|             xenServerIp = xenServerPasswordMap.keys()[0]
 | |
|             xenServerPassword = xenServerPasswordMap[xenServerIp]
 | |
| 
 | |
|         masterXenServerIp = xenServerIp
 | |
|         masterXenServerPassword = xenServerPassword
 | |
|         session = None
 | |
|         try:
 | |
|             session = Session("http://" + masterXenServerIp + "/var/run/xend/xen-api.sock")
 | |
|             session.login_with_password("root", masterXenServerPassword)
 | |
|         except Exception, e:
 | |
|             if (e.details != None and len(e.details) == 2 and e.details[0] == "HOST_IS_SLAVE"):
 | |
|                 masterXenServerIp = e.details[1]
 | |
|                 if (xenServerPasswordMap != None):
 | |
|                     masterXenServerPassword = xenServerPasswordMap[masterXenServerIp]
 | |
|                 else:
 | |
|                     masterXenServerPassword = xenServerPassword
 | |
|                 session = Session("http://" + masterXenServerIp + "/var/run/xend/xen-api.sock")
 | |
|                 session.login_with_password("root", masterXenServerPassword)
 | |
|             else:
 | |
|                 raise
 | |
| 
 | |
|         self.xenServerIp = masterXenServerIp
 | |
|         self.xenServerPassword = masterXenServerPassword
 | |
|         self.xenapi = session.xenapi
 | |
|         return True
 | |
|         
 | |
|     @verboseLogDecorator("Finding the control domain for the dest system...")
 | |
|     def findControlDomainRef(self):
 | |
|         # Find the host ref for this system
 | |
|         hostRefs = self.xenapi.host.get_all()
 | |
|         systemHostRef = None
 | |
|         for hostRef in hostRefs:
 | |
|             address = self.xenapi.host.get_address(hostRef)
 | |
|             if (address == self.xenServerIp):
 | |
|                 systemHostRef = hostRef
 | |
|                 break
 | |
|             
 | |
|         if (systemHostRef == None):
 | |
|             raise Exception("Failed to find the XenServer host ref for " + str(self))
 | |
| 
 | |
|         # Find the control domain ref that corresponds to this host                
 | |
|         vmRefs = self.xenapi.VM.get_all()
 | |
|         for vmRef in vmRefs:
 | |
|             if (self.xenapi.VM.get_is_control_domain(vmRef)):
 | |
|                 controlDomainHostRef = self.xenapi.VM.get_resident_on(vmRef)
 | |
|                 if (controlDomainHostRef == systemHostRef):
 | |
|                     return vmRef
 | |
|         return None
 | |
| 
 | |
|     @nonExitingLogDecorator("Running ssh command...")
 | |
|     def runSshCommand(self, command):
 | |
|         stdin, stdout, stderr = self.sshConn.exec_command(command)
 | |
|         return (stdin.channel.recv_exit_status() == 0)
 | |
| 
 | |
|     def updateIsoPermissions(self):
 | |
|         setParams = {"public":"1"}
 | |
|         whereParams = {"id":self.isoId}
 | |
|         return self.db.updateDbValues("vm_template", setParams, whereParams)
 | |
| 
 | |
|     def __str__(self):
 | |
|         description = "Management Server: %s" % (self.api.ip)
 | |
|         if (self.xenServerIp != None):
 | |
|             description += " | XenServer: %s" %(self.xenServerIp)
 | |
|         return description
 | |
|     
 | |
|     class API:
 | |
|         # Vars: ip
 | |
|         def __init__(self, ip, asyncApi):
 | |
|             self.ip = ip
 | |
|             self.asyncApi = asyncApi
 | |
|                     
 | |
|         # Runs a synchronous API command and returns the ID of the created object, or success/failure
 | |
|         def runSyncApiCommand(self, command, params, objectName):
 | |
|             requestURL = self.buildRequestUrl(command, params)
 | |
|             xmlText = urllib2.urlopen(requestURL).read()
 | |
|             if (objectName == None):
 | |
|                 responseName = (command + "response").lower()
 | |
|                 return (System.API.getTagValue(xmlText, responseName, "success") == "true")
 | |
|             else:
 | |
|                 return System.API.getTagValue(xmlText, objectName, "id")
 | |
|                 
 | |
|         # Runs a asynchronous API command and returns the ID of the created object, or success/failure
 | |
|         def runAsyncApiCommand(self, command, params, objectName):
 | |
|             requestURL = self.buildRequestUrl(command, params)
 | |
|             xmlText = urllib2.urlopen(requestURL).read()
 | |
|             responseName = (command + "response").lower()
 | |
|             jobId = System.API.getTagValue(xmlText, responseName, "jobid")
 | |
|             objectId = System.API.getTagValue(xmlText, responseName, objectName + "id")
 | |
|             params = dict()
 | |
|             params["jobId"] = jobId
 | |
|             requestURL = self.buildRequestUrl("queryAsyncJobResult", params)                
 | |
|             retries = int(GLOBALS["ASYNC_RETRIES"])
 | |
|             jobResult = None
 | |
|             while (retries > 0):
 | |
|                 time.sleep(float(GLOBALS["ASYNC_SLEEP_TIME"]))
 | |
|                 xmlText = urllib2.urlopen(requestURL).read()
 | |
|                 if (System.API.getTagValue(xmlText, "queryasyncjobresultresponse", "jobstatus") == "1"):
 | |
|                     if (objectId != None):
 | |
|                         return objectId
 | |
|                     else:
 | |
|                         return True
 | |
|                 jobResult = System.API.getTagValue(xmlText, "queryasyncjobresultresponse", "jobresult")
 | |
|                 retries -= 1
 | |
|             raise Exception(jobResult)            
 | |
| 
 | |
|         def buildRequestUrl(self, command, params):
 | |
|             requestURL = "http://" + self.ip + ":8096/client/api/?command=" + command
 | |
|             for paramKey in params.keys():
 | |
|                 paramVal = params.get(paramKey)
 | |
|                 requestURL += "&" + str(paramKey) + "=" + str(paramVal)
 | |
|             return requestURL
 | |
|     
 | |
|         @staticmethod
 | |
|         def getTagValue(xmlText, objectName, tagName):
 | |
|             xmlDoc = parseString(xmlText)
 | |
|             response = xmlDoc.getElementsByTagName(objectName)[0]
 | |
|             for x in response.childNodes:
 | |
|                 if (x.tagName == tagName):
 | |
|                     return " ".join(z.wholeText for z in x.childNodes)
 | |
|             return None        
 | |
| 
 | |
|         @staticmethod
 | |
|         def printApiValues(listOfAPIObjects):
 | |
|             for apiObject in listOfAPIObjects:
 | |
|                 for key in apiObject.keys():
 | |
|                     print key + ":" + apiObject.get(key)
 | |
|                 print " "
 | |
| 
 | |
|     class DB:
 | |
|         # Vars: conn
 | |
|         def __init__(self, ip, dbName, dbLogin, dbPassword):
 | |
|             self.conn = MySQLdb.connect(host = ip, user = dbLogin, passwd = dbPassword, db = dbName)
 | |
|             self.conn.autocommit(True)
 | |
| 
 | |
|         def getTable(self, table):
 | |
|             cursor = self.conn.cursor()
 | |
|             sql  = "SELECT * from " + table
 | |
|             cursor.execute(sql)
 | |
|             for row in cursor.fetchall():
 | |
|                 print row
 | |
|             cursor.close()
 | |
|         
 | |
|         # Returns a list of hashtables, each one representing a row and using column names as keys. 
 | |
|         def getDbValues(self, table, columns, whereParams):
 | |
|             values = []
 | |
|             cursor = self.conn.cursor()
 | |
|             columnsText = ",".join(columns)
 | |
|             sql = "SELECT " + columnsText + " FROM " + table
 | |
|             if (len(whereParams) > 0):
 | |
|                 sql += System.DB.buildSqlWhereClause(whereParams)
 | |
|             cursor.execute(sql)
 | |
|             rows = cursor.fetchall()
 | |
|             cursor.close()
 | |
|             for row in rows:
 | |
|                 value = dict()
 | |
|                 for i in range(len(columns)):
 | |
|                     val = str(row[i])
 | |
|                     value[columns[i]] = val
 | |
|                 values.append(value)
 | |
|             return values
 | |
| 
 | |
|         def updateDbValues(self, table, setParams, whereParams):
 | |
|             setClause = System.DB.buildSqlSetClause(setParams)
 | |
|             sql = "UPDATE " + table + setClause
 | |
|             if (len(whereParams) > 0):
 | |
|                 sql += System.DB.buildSqlWhereClause(whereParams)
 | |
|         
 | |
|             cursor = self.conn.cursor()
 | |
|             cursor.execute(sql)
 | |
|             self.conn.commit()
 | |
|             cursor.close()
 | |
|             return True
 | |
| 
 | |
|         def insertIntoDb(self, table, setParams):
 | |
|             existingRecords = self.getDbValues(table, ["id"], setParams)
 | |
|             if (len(existingRecords) > 0):
 | |
|                 return existingRecords[0]["id"]
 | |
|             columns = setParams.keys()
 | |
|             values = []
 | |
|             for column in columns:
 | |
|                 values.append(setParams[column])
 | |
|             sql = "INSERT INTO " + table + System.DB.buildSqlInsertClause(columns, values)
 | |
|             cursor = self.conn.cursor()
 | |
|             cursor.execute(sql)
 | |
|             insertId = self.conn.insert_id()
 | |
|             self.conn.commit()
 | |
|             cursor.close()
 | |
|             return insertId
 | |
| 
 | |
|         @staticmethod
 | |
|         def buildSqlInsertClause(columns, values):
 | |
|             columnsSql = " ("
 | |
|             valuesSql = " VALUES ("
 | |
|             for i in range(len(columns)):
 | |
|                 if (str(values[i]) == "null"):
 | |
|                     continue
 | |
| 
 | |
|                 columnsSql += columns[i]
 | |
|                 valuesSql += "'" + str(values[i]) + "'"
 | |
|                 if (i != (len(columns) - 1)):
 | |
|                     columnsSql += ", "
 | |
|                     valuesSql += ", "
 | |
|                 else:
 | |
|                     columnsSql += ")"
 | |
|                     valuesSql += ")"
 | |
|             return columnsSql + valuesSql
 | |
| 
 | |
|         @staticmethod
 | |
|         def buildSqlWhereClause(params):
 | |
|             sql = " WHERE "
 | |
|             keys = params.keys()
 | |
|             for i in range(len(keys)):
 | |
|                 key = str(keys[i])
 | |
|                 val = str(params[key])
 | |
|                 if ("like" in val):
 | |
|                     val = val.split(":")[1]
 | |
|                     sql += key + " like '" + val + "'"
 | |
|                 elif ("neq" in val):
 | |
|                     val  = val.split(":")[1]
 | |
|                     sql += key + " != '" + val + "'"
 | |
|                 elif (val == "null" or val == "not null"):
 | |
|                     sql += key + " IS " + val
 | |
|                 else:
 | |
|                     sql += key + " = '" + val + "'"
 | |
|                 if (i != (len(keys) - 1)):
 | |
|                     sql += " AND "
 | |
|             return sql
 | |
| 
 | |
|         @staticmethod
 | |
|         def buildSqlSetClause(params):
 | |
|             sql = " SET "
 | |
|             keys = params.keys()
 | |
|             for i in range(len(keys)):
 | |
|                 key = keys[i]
 | |
|                 val = params[key]
 | |
|                 sql += key + " = "
 | |
|                 if (val == "null"):
 | |
|                     sql += "null"
 | |
|                 else:
 | |
|                     sql += "'" + val + "'"
 | |
|                 if (i != (len(keys) - 1)):
 | |
|                     sql += ", "        
 | |
|             return sql    
 | |
| 
 | |
| ### Data classes
 | |
| 
 | |
| class User:
 | |
|     # Vars: system, id, username, password, accountId, firstname, lastname, email, accountType, accountName, domainId
 | |
| 
 | |
|     def __init__(self, system, userId, username, password, accountId, firstname, lastname, email, accountType, accountName, domainId):
 | |
|         self.system = system
 | |
|         self.id = userId
 | |
|         self.username = username
 | |
|         self.password = password
 | |
|         self.accountId = accountId
 | |
|         self.firstname = firstname
 | |
|         self.lastname = lastname
 | |
|         self.email = email
 | |
|         self.accountType = accountType
 | |
|         self.accountName = accountName
 | |
|         self.domainId = domainId
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "(User: %s | %s)" % (self.username, self.system)
 | |
| 
 | |
|     def alreadyMigrated(self):
 | |
|         f = open(GLOBALS["MIGRATED_ACCOUNTS_FILE"], "a+")
 | |
|         migratedUsersCsv = f.read()
 | |
|         f.close()
 | |
|         migratedUsersEntries = migratedUsersCsv.split(",")
 | |
|         for migratedUsersEntry in migratedUsersEntries:
 | |
|             if (migratedUsersEntry.strip() == self.accountId):
 | |
|                 return True
 | |
|         return False
 | |
| 
 | |
|     def tagAsMigrated(self):
 | |
|         if (not self.alreadyMigrated()):
 | |
|             f = open(GLOBALS["MIGRATED_ACCOUNTS_FILE"], "a")
 | |
|             f.write(self.accountId + ",")
 | |
|             f.close()
 | |
|         return True        
 | |
| 
 | |
|     @staticmethod
 | |
|     def getByName(system, username):
 | |
|         columns = ["id"]
 | |
|         users = system.db.getDbValues("user", columns, {"username":username, "removed":"null"})
 | |
|         if (len(users) > 0):
 | |
|             return User.get(system, users[0]["id"])
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def get(system, userId):
 | |
|         columns = ["id", "username", "password", "account_id", "firstname", "lastname", "email"]
 | |
|         users = system.db.getDbValues("user", columns, {"id":userId})
 | |
|         if (len(users) == 0):
 | |
|             return None
 | |
|         user = users[0]
 | |
|         columns = ["type", "account_name", "domain_id"]
 | |
|         account = system.db.getDbValues("account", columns, {"id":user["account_id"]})[0]
 | |
|         return User(system, userId, user["username"], user["password"], user["account_id"], user["firstname"], user["lastname"], user["email"], account["type"], account["account_name"], account["domain_id"])
 | |
| 
 | |
|     @staticmethod
 | |
|     def getDomain(system, domainId):
 | |
|         columns = ["id", "parent", "name", "owner"]
 | |
|         return system.db.getDbValues("domain", columns, {"id":domainId})[0]
 | |
| 
 | |
|     @staticmethod
 | |
|     def getDomainByName(system, domainName):
 | |
|         columns = ["id"]
 | |
|         domains = system.db.getDbValues("domain", columns, {"name":domainName})
 | |
|         if (len(domains) > 0):
 | |
|             return domains[0]
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def createDomain(srcSystem, destSystem, srcDomainId):
 | |
|         # Get the source domain
 | |
|         srcDomain = User.getDomain(srcSystem, srcDomainId)
 | |
| 
 | |
|         # If a domain with the same name exists in the dest system, return its ID
 | |
|         destDomain = User.getDomainByName(destSystem, srcDomain["name"])
 | |
|         if (destDomain != None):
 | |
|             return destDomain["id"]
 | |
|         else:
 | |
|             # Otherwise, create a new domain in the dest system with the same name, and return its ID
 | |
|             # If the src domain has parent domains, we need to create these first
 | |
|             parentId = None
 | |
|             if (srcDomain["parent"] != "null"):
 | |
|                 parentId = User.createDomain(srcSystem, destSystem, srcDomain["parent"])
 | |
|             params = dict()
 | |
|             params["name"] = srcDomain["name"]
 | |
|             if (parentId != None):
 | |
|                 params["parent"] = parentId
 | |
|             newDomainId = destSystem.api.runSyncApiCommand("createDomain", params, "domain")
 | |
|             if (newDomainId == None):
 | |
|                 raise Exception("Failed to create domain " + srcDomain["name"])
 | |
|             else:
 | |
|                 # Set the owner for the new domain
 | |
|                 srcOwner = User.get(srcSystem, srcDomain["owner"])
 | |
|                 destOwner = User.create(destSystem, srcOwner)
 | |
|                 if (not destSystem.db.updateDbValues("domain", {"owner":destOwner.id}, {"id":newDomainId})):
 | |
|                     raise Exception("Failed to update the owner for domain " + srcDomain["name"])
 | |
|                 return newDomainId
 | |
|                 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Creating new user...")
 | |
|     def create(system, srcUser):
 | |
|         user = User.getByName(system, srcUser.username)
 | |
|         if (user != None):
 | |
|             return user
 | |
|         else:
 | |
|             # If the user's domain doesn't exist in the system, create it
 | |
|             domainId = User.createDomain(srcUser.system, system, srcUser.domainId)                                                    
 | |
| 
 | |
|             params = dict()
 | |
|             params["username"] = srcUser.username
 | |
|             params["password"] = "temp"
 | |
|             params["firstname"] = srcUser.firstname
 | |
|             params["lastname"] = srcUser.lastname
 | |
|             params["email"] = srcUser.email
 | |
|             accountType = srcUser.accountType
 | |
|             if (accountType == "2"):
 | |
|                 accountType = "0"
 | |
|             params["accounttype"] = accountType
 | |
|             params["account"] = srcUser.accountName
 | |
|             params["domainid"] = domainId
 | |
|             newUserId = system.api.runSyncApiCommand("createUser", params, "user")
 | |
|             if (newUserId != None):
 | |
|                 if (system.db.updateDbValues("user", {"password":srcUser.password}, {"id":newUserId})):
 | |
|                     return User.get(system, newUserId)
 | |
|                 else:
 | |
|                     return None
 | |
|             else:
 | |
|                 return None
 | |
| 
 | |
| class ServiceOffering:
 | |
|     # Vars: system, id, numCpus, speed, memory, disk
 | |
| 
 | |
|     def __init__(self, system, offeringId, numCpus, speed, memory, disk):
 | |
|         self.system = system
 | |
|         self.id = offeringId
 | |
|         self.numCpus = numCpus
 | |
|         self.speed = speed
 | |
|         self.memory = memory
 | |
|         self.disk = disk
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "ServiceOffering: %s | id: %s | numCpus: %s | speed: %s | memory: %s | disk: %s" % (self.system, self.id, self.numCpus, self.speed, self.memory, self.disk)
 | |
| 
 | |
|     @staticmethod
 | |
|     def getCorrespondingServiceOffering(srcServiceOfferingId):
 | |
|         srcServiceOffering = ServiceOffering.getSrcSystemServiceOfferingById(srcServiceOfferingId)
 | |
|         destServiceOffering = ServiceOffering.getDestSystemServiceOffering(srcServiceOffering.numCpus, srcServiceOffering.speed, srcServiceOffering.memory)
 | |
|         return destServiceOffering
 | |
| 
 | |
|     @staticmethod
 | |
|     def getDestSystemServiceOffering(numCpus, speed, memory):
 | |
|         serviceOfferings = GLOBALS["DEST_SYSTEM"].db.getDbValues("service_offering", ["id"], {"cpu":numCpus, "speed":speed, "ram_size":memory, "guest_ip_type":"Virtualized"})
 | |
|         if (len(serviceOfferings) > 0):
 | |
|             return ServiceOffering(GLOBALS["DEST_SYSTEM"], serviceOfferings[0]["id"], numCpus, speed, memory, None)
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSrcSystemServiceOfferingByVmId(vmId):
 | |
|         serviceOfferingId = GLOBALS["SRC_SYSTEM"].db.getDbValues("user_vm", ["service_offering_id"], {"id":vmId})[0]["service_offering_id"]
 | |
|         return getSrcSystemServiceOfferingById(serviceOfferingId)
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSrcSystemServiceOfferingById(serviceOfferingId):
 | |
|         columns = ["id", "cpu", "speed", "ram_size", "disk"]
 | |
|         serviceOfferings = GLOBALS["SRC_SYSTEM"].db.getDbValues("service_offering", columns, {"id":serviceOfferingId})
 | |
|         if (len(serviceOfferings) > 0):
 | |
|             offering = serviceOfferings[0]
 | |
|             return ServiceOffering(GLOBALS["SRC_SYSTEM"], offering["id"], offering["cpu"], offering["speed"], offering["ram_size"], offering["disk"])
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSrcSystemServiceOfferings():
 | |
|         serviceOfferings = []
 | |
|         columns = ["id", "cpu", "speed", "ram_size", "disk"]
 | |
|         srcServiceOfferings = GLOBALS["SRC_SYSTEM"].db.getDbValues("service_offering", columns, {})
 | |
|         for offering in srcServiceOfferings:
 | |
|             serviceOfferings.append(ServiceOffering(GLOBALS["SRC_SYSTEM"], offering["id"], offering["cpu"], offering["speed"], offering["ram_size"], offering["disk"]))
 | |
|         return serviceOfferings
 | |
| 
 | |
| class DiskOffering:
 | |
|     # Vars: id, size
 | |
| 
 | |
|     def __init__(self, diskOfferingId, size):
 | |
|         self.id = diskOfferingId
 | |
|         self.size = size
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "Disk Offering: size = %s" % (self.size)
 | |
|         
 | |
|     @staticmethod
 | |
|     def getDestDiskOffering(size):
 | |
|         columns = ["id"]
 | |
|         diskOfferingRows = GLOBALS["DEST_SYSTEM"].db.getDbValues("disk_offering", ["id"], {"disk_size":size, "type":"Disk"})
 | |
|         if (len(diskOfferingRows) > 0):
 | |
|             return DiskOffering(diskOfferingRows[0]["id"], size)
 | |
|         else:
 | |
|             size = ((int(size) / 1024) + 1) * 1024
 | |
|             diskOfferingRows = GLOBALS["DEST_SYSTEM"].db.getDbValues("disk_offering", ["id"], {"disk_size":size, "type":"Disk"})
 | |
|             if (len(diskOfferingRows) > 0):
 | |
|                 return DiskOffering(diskOfferingRows[0]["id"], size)
 | |
|             else:
 | |
|                 return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getCorrespondingDiskOffering(srcServiceOfferingId):
 | |
|         srcServiceOffering = ServiceOffering.getSrcSystemServiceOfferingById(srcServiceOfferingId)
 | |
|         diskOffering = DiskOffering.getDestDiskOffering(srcServiceOffering.disk)
 | |
|         return diskOffering
 | |
| 
 | |
| 
 | |
| class VM:
 | |
|     # Vars: id, system, user, serviceOfferingId, name, templateId, guestOsId
 | |
|     
 | |
|     def __init__(self, vmId, user, serviceOfferingId, guestOsId, guestOsCategoryId):
 | |
|         self.id = vmId
 | |
|         self.system = user.system
 | |
|         self.user = user
 | |
|         self.serviceOfferingId = serviceOfferingId
 | |
|         self.guestOsId = guestOsId
 | |
|         self.guestOsCategoryId = guestOsCategoryId
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "UserVM: id = %s | username = %s | system = %s" % (self.id, self.user.username, self.system)
 | |
| 
 | |
|     def getName(self):
 | |
|         columns = ["name"]
 | |
|         return self.system.db.getDbValues("vm_instance", columns, {"id":self.id})[0]["name"]
 | |
|     
 | |
|     @basicLogDecorator("Deploying a temporary VM...")
 | |
|     def deployTemp(self):
 | |
|         params = {"account":self.user.accountName, "domainid":self.user.domainId, "zoneId":self.system.zoneId, "serviceofferingid":self.system.defaultServiceOfferingId, "templateid":self.system.templateId}
 | |
|         vmId = self.system.api.runAsyncApiCommand("deployVirtualMachine", params, "virtualmachine")
 | |
|         if (vmId in (None, False)):
 | |
|             return False
 | |
|         self.id = vmId
 | |
|         self.name = self.getName() + "-temp-vm"
 | |
|         success = self.system.db.updateDbValues("vm_instance", {"name":self.name}, {"id":self.id})
 | |
|         return success
 | |
| 
 | |
|     @basicLogDecorator("Deploying a new VM for the user ...")    
 | |
|     def deploy(self, srcVm):
 | |
|         params = dict()
 | |
|         params["account"] = self.user.accountName
 | |
|         params["domainid"] = self.user.domainId
 | |
|         params["zoneid"] = self.system.zoneId        
 | |
|         params["serviceofferingid"] = self.serviceOfferingId
 | |
|         params["templateid"] = self.system.isoId
 | |
|         params["diskofferingid"] = self.system.defaultDiskOfferingId
 | |
|         vmId = self.system.api.runAsyncApiCommand("deployVirtualMachine", params, "virtualmachine")
 | |
|         if (vmId in (None, False)):
 | |
|             return None
 | |
|         self.id = vmId        
 | |
|         self.name = self.getName() + "-" + str(srcVm.id) + " (" + VM.getGuestOsName(GLOBALS["DEST_SYSTEM"], self.guestOsId) + ")"
 | |
|         success = self.system.db.updateDbValues("vm_instance", {"name":self.name}, {"id":self.id})
 | |
|         return success
 | |
|         
 | |
|     @verboseLogDecorator("Updating the guest OS ID for the VM...")
 | |
|     def updateGuestOsId(self):
 | |
|         setParams = {"guest_os_id":self.guestOsId}
 | |
|         whereParams = {"id":self.id}
 | |
|         return self.system.db.updateDbValues("vm_instance", setParams, whereParams)
 | |
|         
 | |
|     @basicLogDecorator("Starting VM...")
 | |
|     def start(self):
 | |
|         params = {"id":self.id}
 | |
|         return self.system.api.runAsyncApiCommand("startVirtualMachine", params, "virtualmachine")
 | |
| 
 | |
|     @basicLogDecorator("Stopping VM...")
 | |
|     def stop(self):
 | |
|         params = {"id":self.id}
 | |
|         if (self.system.api.asyncApi):
 | |
|             return self.system.api.runAsyncApiCommand("stopVirtualMachine", params, "virtualmachine")
 | |
|         else:
 | |
|             return self.system.api.runSyncApiCommand("stopVirtualMachine", params, None)
 | |
| 
 | |
|     @basicLogDecorator("Destroying temporary VM...")
 | |
|     def destroy(self):
 | |
|         params = {"id":self.id}
 | |
|         return self.system.api.runAsyncApiCommand("destroyVirtualMachine", params, "virtualmachine")
 | |
| 
 | |
|     @verboseLogDecorator("Detaching ISO from VM...")
 | |
|     def detachIso(self):
 | |
|         isoId = self.system.db.getDbValues("vm_instance", ["iso_id"], {"id":self.id})[0]["iso_id"]
 | |
|         if (isoId == "None"):
 | |
|             return True
 | |
|         
 | |
|         params = {"virtualmachineid":self.id}
 | |
|         return self.system.api.runAsyncApiCommand("detachIso", params, "virtualmachine")
 | |
| 
 | |
|     def isLinuxVm(self):
 | |
|         return (self.guestOsCategoryId != str(GLOBALS["DEST_WINDOWS_GUEST_OS_CATEGORY_ID"]))
 | |
| 
 | |
|     @staticmethod
 | |
|     def getGuestOsName(system, guestOsId):
 | |
|         columns = ["id", "display_name"]
 | |
|         guestOsList = system.db.getDbValues("guest_os", columns, {"id":guestOsId})
 | |
|         if (len(guestOsList) > 0):
 | |
|             return guestOsList[0]["display_name"]
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getGuestOsCategoryId(system, guestOsId):
 | |
|         columns = ["category_id"]
 | |
|         return system.db.getDbValues("guest_os", columns, {"id":guestOsId})[0]["category_id"]
 | |
|     
 | |
|     @staticmethod
 | |
|     def getVmId(system, accountId, guestIpAddress):
 | |
|         userVms = system.db.getDbValues("user_vm", ["id"], {"account_id":accountId, "guest_ip_address":guestIpAddress})
 | |
|         for userVm in userVms:
 | |
|             vmInstances = system.db.getDbValues("vm_instance", ["id"], {"id":userVm["id"], "removed":"null", "state":"neq:Destroyed"})
 | |
|             if (len(vmInstances) > 0):
 | |
|                 return vmInstances[0]["id"]
 | |
|         return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getVms(user):
 | |
|         system = user.system
 | |
|         vms = []
 | |
|         columns = ["id", "service_offering_id"]
 | |
|         userVmRows = system.db.getDbValues("user_vm", columns, {"account_id":user.accountId})
 | |
|         for userVmRow in userVmRows:
 | |
|             vmInstanceRow = system.db.getDbValues("vm_instance", ["vm_template_id", "removed"], {"id":userVmRow["id"]})[0]
 | |
|             if (vmInstanceRow["removed"] != "None"):
 | |
|                 continue
 | |
| 
 | |
|             # Determine the service offering ID
 | |
|             serviceOfferingId = userVmRow["service_offering_id"]
 | |
|             
 | |
|             # Determine the new guest OS id and category id
 | |
|             templateId = vmInstanceRow["vm_template_id"]
 | |
|             guestOsId = GLOBALS["GUEST_OS_MAP"][templateId]
 | |
|             guestOsCategoryId = VM.getGuestOsCategoryId(GLOBALS["DEST_SYSTEM"], guestOsId)
 | |
|             
 | |
|             vms.append(VM(userVmRow["id"], user, serviceOfferingId, guestOsId, guestOsCategoryId))
 | |
|         return vms
 | |
| 
 | |
|     @staticmethod
 | |
|     def getCorrespondingVm(destUser, srcVm):
 | |
|         system = destUser.system
 | |
|         columns = ["id", "guest_os_id"]
 | |
|         correspondingVms = system.db.getDbValues("vm_instance", columns, {"name":"like:%-" + srcVm.id + " (%"})
 | |
|         if (len(correspondingVms) > 0):
 | |
|             correspondingVm = correspondingVms[0]
 | |
|             newServiceOffering = ServiceOffering.getCorrespondingServiceOffering(srcVm.serviceOfferingId)
 | |
|             vmId = correspondingVm["id"]
 | |
|             guestOsId = srcVm.guestOsId
 | |
|             guestOsCategoryId = VM.getGuestOsCategoryId(GLOBALS["DEST_SYSTEM"], guestOsId)
 | |
|             return VM(vmId, destUser, newServiceOffering.id, guestOsId, guestOsCategoryId)
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getTempVm(user):
 | |
|         system = user.system
 | |
|         columns = ["id"]
 | |
|         tempVms = system.db.getDbValues("vm_instance", columns, {"removed":"null", "state":"neq:Destroyed", "name":"like:%-temp-vm"})
 | |
|         if (len(tempVms) > 0):
 | |
|             tempVm = tempVms[0]
 | |
|             return VM(tempVm["id"], user, None, None, None)
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getTemplate(system, templateId):
 | |
|         columns = ["id", "name", "format"]
 | |
|         templates = system.db.getDbValues("vm_template", columns, {"id":templateId})
 | |
|         if (len(templates) > 0):
 | |
|             return templates[0]
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     @staticmethod
 | |
|     def getTemplateIds(system):
 | |
|         templateIds = []
 | |
|         columns = ["id", "unique_name"]
 | |
|         templates = system.db.getDbValues("vm_template", columns, {})
 | |
|         for template in templates:
 | |
|             if (template["unique_name"] == "routing"):
 | |
|                 continue
 | |
|             templateIds.append(template["id"])
 | |
|         return templateIds
 | |
| 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Migrating the user's VMs...")    
 | |
|     def migrateVirtualMachines(srcUser, destUser):
 | |
|         # Maintain a map of src system VM IDs to dest system VM ids
 | |
|         vmIdMap = dict()
 | |
|         
 | |
|         # Get a list of user VMs for the source user
 | |
|         srcVms = VM.getVms(srcUser)
 | |
|     
 | |
|         for srcVm in srcVms:
 | |
|             # Try to find an existing VM in the dest system that corresponds to the VM in the src system
 | |
|             destVm = VM.getCorrespondingVm(destUser, srcVm)
 | |
|             
 | |
|             # If there is no corresponding VM, deploy a new VM in the dest system
 | |
|             if (destVm == None):
 | |
|                 destVm = VM(None, destUser, srcVm.serviceOfferingId, srcVm.guestOsId, srcVm.guestOsCategoryId)
 | |
|                 destVm.deploy(srcVm)
 | |
| 
 | |
|             # Add a mapping between the src VM and the dest VM
 | |
|             vmIdMap[srcVm.id] = destVm.id                    
 | |
| 
 | |
|             # Get a list of volumes for the source VM
 | |
|             srcVolumes = Volume.getSrcVolumes(srcUser, srcVm)
 | |
| 
 | |
|             # If these volumes have already been copied to the dest system, skip migration for this VM
 | |
|             vmAlreadyMigrated = True
 | |
|             for srcVolume in srcVolumes:
 | |
|                 destVolume = Volume.getDestVolume(None, destVm, srcVolume.type)
 | |
|                 if (destVolume == None):
 | |
|                     vmAlreadyMigrated = False
 | |
|                     break
 | |
|                 elif (srcVolume.id != destVolume.name.split("-")[-1]):
 | |
|                     vmAlreadyMigrated = False
 | |
|                     break
 | |
| 
 | |
|             if (vmAlreadyMigrated):
 | |
|                 writeToLog("\n" + str(srcVm) + " has already been migrated.", True)
 | |
|                 continue
 | |
|             else:
 | |
|                 writeToLog("\nMigrating volumes for source VM: " + str(srcVm), True)
 | |
|             
 | |
|             # Stop the dest VM
 | |
|             destVm.stop()        
 | |
| 
 | |
|             # Stop the source VM
 | |
|             srcVm.stop()
 | |
| 
 | |
|             for srcVolume in srcVolumes:
 | |
|                 destVolume = None
 | |
|                 if (srcVolume.type == "DATADISK"):
 | |
|                     destVolume = Volume.getDestVolume(None, destVm, "DATADISK")
 | |
|                     if (destVolume == None):
 | |
|                         diskOffering = DiskOffering.getCorrespondingDiskOffering(srcVm.serviceOfferingId)
 | |
|                         destVolume = Volume(GLOBALS["DEST_SYSTEM"], None, str(destVm.id) + "-DATADISK", None, None, None, "DATA", diskOffering.id)
 | |
|                         destVolume.createAndAttach(destVm)
 | |
|                 else:
 | |
|                     destVolume = Volume.getDestVolume(None, destVm, "ROOT")
 | |
| 
 | |
|                 # If the dest volume is already tagged with the source volume's ID, we don't need to do a copy
 | |
|                 if (srcVolume.id == destVolume.name.split("-")[-1]):
 | |
|                     writeToLog(str(srcVolume) + " has already been migrated.")
 | |
|                     continue
 | |
| 
 | |
|                 # If the srcVolume's iSCSI SR isn't created on the XenServer, create it
 | |
|                 srcHost = Host.getHost(GLOBALS["SRC_SYSTEM"], srcVolume.hostId)
 | |
|                 srcSR = SR.getExistingSrcSr(srcHost.ip, srcHost.iqn)
 | |
|                 if (srcSR == None):
 | |
|                     srcSR = SR(GLOBALS["DEST_SYSTEM"], srcHost.ip, srcHost.iqn, None)
 | |
|                     srcSR.create()
 | |
|                 else:
 | |
|                     writeToLog("Found existing SR: " + str(srcSR), False)
 | |
|                     
 | |
|                 # Find the VDI corresponding to the src volume
 | |
|                 srcVdi = VDI(srcSR, srcVolume, None)
 | |
| 
 | |
|                 # Find the SR corresponding to the dest storage pool
 | |
|                 destStoragePool = StoragePool.getStoragePool(GLOBALS["DEST_SYSTEM"], destVolume.poolId)
 | |
|                 destSR = SR(GLOBALS["DEST_SYSTEM"], None, None, destStoragePool.uuid)
 | |
|                 destSR.find()
 | |
|                 
 | |
|                 # Copy the src VDI to the dest SR
 | |
|                 copiedVdiUuid = srcVdi.copy(destSR)
 | |
| 
 | |
|                 # If this is the rootdisk of a Linux VM, change the disk name
 | |
|                 destVdi = VDI(destSR, destVolume, copiedVdiUuid)
 | |
|                 if (destVolume.type == "ROOT" and srcVm.isLinuxVm()):
 | |
|                     destVdi.changeBootableDeviceName()                    
 | |
|                                             
 | |
|                 # Destroy the VM's old VDI
 | |
|                 oldDestVdi = VDI(destSR, destVolume, destVolume.path)
 | |
|                 oldDestVdi.destroy()
 | |
|     
 | |
|                 # Update the destVolume's database record to have the UUID of the copied VDI, the virtual size of the copied VDI, and the ID of the source volume
 | |
|                 destVolume.update(copiedVdiUuid, destVdi.getVirtualSize(), destVolume.name + "-" + srcVolume.id)
 | |
| 
 | |
|             # Detach the dest VM's ISO
 | |
|             destVm.detachIso()         
 | |
| 
 | |
|             # Update the guest OS ID for the VM
 | |
|             destVm.updateGuestOsId()
 | |
|         
 | |
|             # Start the dest VM
 | |
|             destVm.start()                
 | |
|             
 | |
|         return vmIdMap
 | |
| 
 | |
| class Volume:
 | |
|     # vars: system, id, hostId, poolId, path, zoneId, iscsiName, type, diskOfferingId
 | |
| 
 | |
|     def __init__(self, system, volumeId, name, poolOrHostId, path, iscsiName, volumeType, diskOfferingId):
 | |
|         self.system = system
 | |
|         self.id = volumeId
 | |
|         self.name = name
 | |
|         if (iscsiName == None):
 | |
|             self.poolId = poolOrHostId
 | |
|             self.iscsiName = None
 | |
|         else:
 | |
|             self.hostId = poolOrHostId
 | |
|             self.iscsiName = iscsiName
 | |
|         self.path = path
 | |
|         self.type = volumeType
 | |
|         self.diskOfferingId = diskOfferingId
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "Volume: %s | type: %s | path: %s" % (self.system, self.type, self.path)
 | |
| 
 | |
|     @basicLogDecorator("Creating a new volume and attaching it to the user's VM...")
 | |
|     def createAndAttach(self, destVm):
 | |
|         params = dict()
 | |
|         params["account"] = destVm.user.accountName
 | |
|         params["domainid"] = destVm.user.domainId
 | |
|         params["name"] = self.name
 | |
|         params["zoneid"] = self.system.zoneId
 | |
|         params["diskofferingid"] = self.diskOfferingId
 | |
|         volumeId = self.system.api.runAsyncApiCommand("createVolume", params, "volume")        
 | |
|         if (volumeId in (None, False)):
 | |
|             return False        
 | |
|         self.id = volumeId
 | |
|         params = dict()
 | |
|         params["id"] = volumeId
 | |
|         params["virtualmachineid"] = destVm.id
 | |
|         success = self.system.api.runAsyncApiCommand("attachVolume", params, "volume")        
 | |
|         if (success in (None, False)):
 | |
|             return False        
 | |
|         newVolume = Volume.getDestVolume(volumeId, None, None)
 | |
|         self.poolId = newVolume.poolId
 | |
|         self.path = newVolume.path
 | |
|         return True
 | |
| 
 | |
|     def update(self, volumeUuid, volumeSize, name):
 | |
|         setParams = {"path":volumeUuid, "size":volumeSize, "name":name}
 | |
|         whereParams = {"id":self.id}
 | |
|         return self.system.db.updateDbValues("volumes", setParams, whereParams)
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSrcVolumes(user, vm):
 | |
|         volumes = []
 | |
|         columns = ["id", "name", "host_id", "path", "iscsi_name", "volume_type", "offering_id"]
 | |
|         volumeRows = GLOBALS["SRC_SYSTEM"].db.getDbValues("volumes", columns, {"account_id":user.accountId, "instance_id":vm.id, "removed":"null"})
 | |
|         for volumeRow in volumeRows:
 | |
|             volumes.append(Volume(GLOBALS["SRC_SYSTEM"], volumeRow["id"], volumeRow["name"], volumeRow["host_id"], volumeRow["path"], volumeRow["iscsi_name"], volumeRow["volume_type"], volumeRow["offering_id"]))
 | |
|         return volumes
 | |
| 
 | |
|     @staticmethod
 | |
|     def getDestVolume(volumeId, vm, volumeType):
 | |
|         columns = ["id", "name", "pool_id", "path", "disk_offering_id"]
 | |
|         whereParams = None
 | |
|         if (volumeId != None):
 | |
|             whereParams = {"id":volumeId}
 | |
|         else:
 | |
|             whereParams = {"instance_id":vm.id, "volume_type":volumeType}
 | |
|         volumeRows = GLOBALS["DEST_SYSTEM"].db.getDbValues("volumes", columns, whereParams)
 | |
|         if (len(volumeRows) > 0):
 | |
|             volumeRow = volumeRows[0]
 | |
|             return Volume(GLOBALS["DEST_SYSTEM"], volumeRow["id"], volumeRow["name"], volumeRow["pool_id"], volumeRow["path"], None, volumeType, volumeRow["disk_offering_id"])
 | |
|         else:
 | |
|             return None
 | |
|         
 | |
| class DomainRouter:
 | |
|     # Vars: id, system, user
 | |
| 
 | |
|     def __init__(self, user):
 | |
|         self.system = user.system
 | |
|         self.user = user
 | |
|         self.id = self.getId()
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "DomainRouter: %s" % (self.user)
 | |
| 
 | |
|     @basicLogDecorator("Stopping user's router...")
 | |
|     def stop(self):
 | |
|         if (self.id == None):
 | |
|             raise Exception("Could not find router for " + str(self.user))
 | |
|         params  = {"id":self.id}
 | |
|         if (self.system.api.asyncApi):
 | |
|             return self.system.api.runAsyncApiCommand("stopRouter", params, "router")
 | |
|         else:
 | |
|             return self.system.api.runSyncApiCommand("stopRouter", params, None)
 | |
| 
 | |
|     @basicLogDecorator("Starting user's router...")
 | |
|     def start(self):
 | |
|         if (self.id == None):
 | |
|             return False
 | |
|         params = {"id":self.id}
 | |
|         if (self.system.api.asyncApi):
 | |
|             return self.system.api.runAsyncApiCommand("startRouter", params, "router")
 | |
|         else:
 | |
|             return self.system.api.runSyncApiCommand("startRouter", params, None)
 | |
| 
 | |
|     @basicLogDecorator("Rebooting user's router...")
 | |
|     def reboot(self):
 | |
|         if (self.id == None):
 | |
|             return False
 | |
|         params = {"id":self.id}
 | |
|         return self.system.api.runAsyncApiCommand("rebootRouter", params, "router")
 | |
| 
 | |
|     def getId(self):
 | |
|         routers = self.system.db.getDbValues("domain_router", ["id"], {"account_id":self.user.accountId})
 | |
|         if (len(routers) > 0):
 | |
|             return routers[0]["id"]
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
| class PublicIp:
 | |
|     # Vars: system, user, address, zoneId, sourceNat, allocated
 | |
| 
 | |
|     def __init__(self, system, user, address, zoneId, sourceNat, allocated):
 | |
|         self.system = system
 | |
|         self.user = user
 | |
|         self.address = address
 | |
|         self.zoneId = zoneId
 | |
|         self.sourceNat = sourceNat
 | |
|         self.allocated = allocated
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.address
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return self.address
 | |
| 
 | |
|     def allocate(self):
 | |
|         setParams = {"account_id":self.user.accountId,
 | |
|                      "domain_id":self.user.domainId,
 | |
|                      "source_nat":self.sourceNat,
 | |
|                      "allocated":self.allocated}
 | |
|         whereParams = {"public_ip_address":self.address,
 | |
|                        "data_center_id":self.zoneId}
 | |
|         return self.system.db.updateDbValues("user_ip_address", setParams, whereParams)
 | |
| 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Clearing existing public IPs...")    
 | |
|     def clearPublicIps(user):
 | |
|         system = user.system
 | |
|         setParams = {"account_id":"null", "domain_id":"null", "source_nat":"0", "allocated":"null"}
 | |
|         whereParams = {"account_id":user.accountId}
 | |
|         return system.db.updateDbValues("user_ip_address", setParams, whereParams)
 | |
| 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Migrating allocated public IPs...")
 | |
|     def migrateAllocatedPublicIps(srcUser, destUser):
 | |
|         # Get a list of public IPs allocated to the source user
 | |
|         ips = PublicIp.getAllocatedPublicIps(srcUser)
 | |
|         
 | |
|         # Allocate each one of these IPs in the dest system
 | |
|         for ip in ips:
 | |
|             ip.system = GLOBALS["DEST_SYSTEM"]
 | |
|             ip.user = destUser
 | |
|             if (not ip.allocate()):
 | |
|                 return None
 | |
|             
 | |
|         return ips
 | |
| 
 | |
|     @staticmethod
 | |
|     def getAllocatedPublicIps(user):
 | |
|         system = user.system
 | |
|         ips = []
 | |
|         columns = ["public_ip_address", "data_center_id", "source_nat", "allocated"]
 | |
|         ipRows = system.db.getDbValues("user_ip_address", columns, {"account_id":user.accountId})
 | |
|         for ipRow in ipRows:
 | |
|             ips.append(PublicIp(system, user, ipRow["public_ip_address"], ipRow["data_center_id"], ipRow["source_nat"], ipRow["allocated"]))
 | |
|         return ips
 | |
| 
 | |
|     @staticmethod
 | |
|     def getGuestIpAddress(system, vmId):
 | |
|         columns = ["guest_ip_address"]
 | |
|         guestIp = system.db.getDbValues("user_vm", columns, {"id":vmId})[0]
 | |
|         return guestIp["guest_ip_address"]
 | |
| 
 | |
| class ForwardingRule:
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Migrating port forwarding and load balancer rules...")    
 | |
|     def migrateForwardingRules(srcUser, destUser, publicIps, vmIdMap):
 | |
|         for publicIp in publicIps:
 | |
|             forwardingRules = ForwardingRule.getSrcForwardingRules(srcUser, destUser, publicIp.address, vmIdMap)
 | |
|             for forwardingRule in forwardingRules:
 | |
|                 newRuleId = forwardingRule.createInDestSystem()
 | |
|                 if (newRuleId == None):
 | |
|                     return False
 | |
|         return True
 | |
|         
 | |
|     @staticmethod
 | |
|     def getSrcForwardingRules(srcUser, destUser, address, vmIdMap):
 | |
|         # vmIdMap maps UserVM database IDs in the src system to UserVM database IDs in the dest system
 | |
|         columns = ["id", "public_port", "private_ip_address", "private_port", "enabled", "protocol", "forwarding", "algorithm"]
 | |
|         ruleRows = GLOBALS["SRC_SYSTEM"].db.getDbValues("ip_forwarding", columns, {"public_ip_address":address})
 | |
|         activeRules = []
 | |
|         for ruleRow in ruleRows:
 | |
|             srcVmId = VM.getVmId(GLOBALS["SRC_SYSTEM"], srcUser.accountId, ruleRow["private_ip_address"])
 | |
|             destVmId = vmIdMap.get(srcVmId)
 | |
|             if (destVmId == None):
 | |
|                 continue
 | |
| 
 | |
|             if (ruleRow["forwarding"] == "1"):
 | |
|                 activeRules.append(ForwardingRule.PortForwardingRule(address, ruleRow["public_port"], PublicIp.getGuestIpAddress(GLOBALS["DEST_SYSTEM"], destVmId), ruleRow["private_port"], ruleRow["enabled"], ruleRow["protocol"]))
 | |
|             else:
 | |
|                 activeRules.append(ForwardingRule.LoadBalancerRule(destUser.accountId, address, ruleRow["public_port"], ruleRow["private_port"], destVmId, ruleRow["algorithm"]))
 | |
|                            
 | |
|         return activeRules 
 | |
|     
 | |
|     class PortForwardingRule:
 | |
|         def __init__(self, publicIp, publicPort, privateIp, privatePort, enabled, protocol):
 | |
|             self.publicIp = publicIp
 | |
|             self.publicPort = publicPort
 | |
|             self.privateIp = privateIp
 | |
|             self.privatePort = privatePort
 | |
|             self.enabled = enabled
 | |
|             self.protocol = protocol
 | |
| 
 | |
|         def createInDestSystem(self):
 | |
|             setParams = dict()
 | |
|             setParams["public_ip_address"] = self.publicIp
 | |
|             setParams["public_port"] = self.publicPort
 | |
|             setParams["private_ip_address"] = self.privateIp
 | |
|             setParams["private_port"] = self.privatePort
 | |
|             setParams["enabled"] = self.enabled
 | |
|             setParams["protocol"] = self.protocol
 | |
|             setParams["forwarding"] = "1"
 | |
|             setParams["algorithm"] = "null"
 | |
|             setParams["group_id"] = "null"
 | |
|             return GLOBALS["DEST_SYSTEM"].db.insertIntoDb("ip_forwarding", setParams)
 | |
|         
 | |
|     class LoadBalancerRule:
 | |
|         def __init__(self, accountId, ip, publicPort, privatePort, vmId, algorithm):
 | |
|             self.accountId = accountId
 | |
|             self.ip = ip
 | |
|             self.publicPort = publicPort
 | |
|             self.privatePort = privatePort
 | |
|             self.vmId = vmId
 | |
|             self.algorithm = algorithm
 | |
| 
 | |
|         def createInDestSystem(self):
 | |
|             setParams = dict()
 | |
|             setParams["name"] = str(self.publicPort) + "-" + str(self.privatePort)
 | |
|             setParams["account_id"] = self.accountId
 | |
|             setParams["ip_address"] = self.ip
 | |
|             setParams["public_port"] = self.publicPort
 | |
|             setParams["private_port"] = self.privatePort
 | |
|             setParams["algorithm"] = self.algorithm
 | |
|             newLoadBalancerRuleId = GLOBALS["DEST_SYSTEM"].db.insertIntoDb("load_balancer", setParams)
 | |
|             if (newLoadBalancerRuleId == None or newLoadBalancerRuleId == "0"):
 | |
|                 return None
 | |
|             setParams = dict()
 | |
|             setParams["load_balancer_id"] = newLoadBalancerRuleId
 | |
|             setParams["instance_id"] = self.vmId
 | |
|             return GLOBALS["DEST_SYSTEM"].db.insertIntoDb("load_balancer_vm_map", setParams)               
 | |
| 
 | |
| class SR:
 | |
|     def __init__(self, system, ip, iqn, uuid):
 | |
|         self.system = system
 | |
|         self.ip = ip
 | |
|         self.iqn = iqn
 | |
|         self.uuid = uuid
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "SR: %s | ip: %s | iqn: %s | uuid: %s" % (self.system, self.ip, self.iqn, self.uuid)
 | |
|     
 | |
|     @verboseLogDecorator("Finding SR...")
 | |
|     def find(self):
 | |
|         xenapi = self.system.xenapi
 | |
|         self.ref = xenapi.SR.get_by_name_label(self.uuid)[0]
 | |
|         return True
 | |
|         
 | |
|     @verboseLogDecorator("Finding source system's iSCSI SR...")        
 | |
|     def create(self):
 | |
|         xenapi = self.system.xenapi
 | |
|         host = xenapi.host.get_all()[0]
 | |
|         deviceConfig = {'targetIQN': self.iqn, 'target': self.ip}
 | |
|         srRef = None
 | |
|         name = "1.0 iSCSI pool: " + self.ip + "-" + self.iqn
 | |
|         srRef = xenapi.SR.create(host, deviceConfig, "0", name, name, "iscsi", "user", True)
 | |
|         if (srRef != None):
 | |
|             self.ref = srRef
 | |
|             return True
 | |
|         else:
 | |
|             return False            
 | |
|         
 | |
|     @staticmethod
 | |
|     def getExistingSrcSr(ip, iqn):
 | |
|         xenapi = GLOBALS["DEST_SYSTEM"].xenapi
 | |
|         srRefs = xenapi.SR.get_all()
 | |
|         for srRef in srRefs:
 | |
|             srNameLabel = xenapi.SR.get_name_label(srRef)
 | |
|             if (srNameLabel == "1.0 iSCSI pool: " + ip + "-" + iqn):
 | |
|                 sr = SR(GLOBALS["DEST_SYSTEM"], ip, iqn, xenapi.SR.get_uuid(srRef))
 | |
|                 sr.ref = srRef
 | |
|                 return sr
 | |
|         return None
 | |
| 
 | |
|     @staticmethod
 | |
|     @verboseLogDecorator("Forgetting all src iSCSI SRs...")    
 | |
|     def forgetAllSrcSrs():
 | |
|         xenapi = GLOBALS["DEST_SYSTEM"].xenapi
 | |
|         srRefs = xenapi.SR.get_all()
 | |
|         for srRef in srRefs:
 | |
|             srNameLabel = xenapi.SR.get_name_label(srRef)
 | |
|             if ("1.0 iSCSI pool" in srNameLabel):
 | |
|                 # Unplug and destroy the SR's PBDs
 | |
|                 pbdRefs = xenapi.SR.get_PBDs(srRef)
 | |
|                 for pbdRef in pbdRefs:
 | |
|                     xenapi.PBD.unplug(pbdRef)
 | |
|                     xenapi.PBD.destroy(pbdRef)
 | |
| 
 | |
|                 # Forget the SR
 | |
|                 xenapi.SR.forget(srRef)                    
 | |
| 
 | |
|         return True
 | |
| 
 | |
| class VDI:
 | |
|     def __init__(self, sr, volume, uuid):
 | |
|         self.sr = sr
 | |
|         self.volume = volume
 | |
|         self.uuid = uuid
 | |
|         self.find()
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "VDI: %s | uuid: %s" % (self.volume, self.uuid)
 | |
| 
 | |
|     @verboseLogDecorator("Getting virtual size for VDI...")
 | |
|     def getVirtualSize(self):
 | |
|         xenapi = self.sr.system.xenapi
 | |
|         return xenapi.VDI.get_virtual_size(self.ref)
 | |
|         
 | |
|     @basicLogDecorator("Copying source system volume to dest system...")
 | |
|     def copy(self, destSR):
 | |
|         xenapi = self.sr.system.xenapi
 | |
|         newVdiRef = xenapi.VDI.copy(self.ref, destSR.ref)
 | |
|         return xenapi.VDI.get_uuid(newVdiRef)
 | |
| 
 | |
|     @basicLogDecorator("Destroying old volume...")
 | |
|     def destroy(self):
 | |
|         xenapi = self.sr.system.xenapi
 | |
|         xenapi.VDI.destroy(self.ref)
 | |
|         return True
 | |
|     
 | |
|     @verboseLogDecorator("Finding VDI in SR...")
 | |
|     def find(self):
 | |
|         xenapi = self.sr.system.xenapi        
 | |
|         if (self.uuid == None):
 | |
|             # Run an sr-scan
 | |
|             xenapi.SR.scan(self.sr.ref)
 | |
|             
 | |
|             # Get a list of VDIs in the SR
 | |
|             vdiRefs = xenapi.SR.get_VDIs(self.sr.ref)
 | |
|                 
 | |
|             # Find the VDI that has the same SCSI ID as the specified volume
 | |
|             volumeScsiId = self.volume.iscsiName.split(":")[-1].strip()
 | |
|             for vdiRef in vdiRefs:
 | |
|                 smConfig = xenapi.VDI.get_sm_config(vdiRef)
 | |
|                 vdiScsiId = smConfig["SCSIid"].strip()[1:]
 | |
|                 if (vdiScsiId == volumeScsiId):
 | |
|                     self.ref = vdiRef
 | |
|                     self.uuid = xenapi.VDI.get_uuid(vdiRef)
 | |
|                     return True
 | |
|                 
 | |
|             return False
 | |
|         else:
 | |
|             self.ref = xenapi.VDI.get_by_uuid(self.uuid)
 | |
|             return True
 | |
|         
 | |
|     @basicLogDecorator("Changing disk name for VDI...")
 | |
|     def changeBootableDeviceName(self):
 | |
|         system = self.volume.system
 | |
|         xenapi = system.xenapi
 | |
|         controlDomainRef = system.controlDomainRef        
 | |
| 
 | |
|         vbdRef = None
 | |
|         try:
 | |
|             # Create a VBD for the VDI
 | |
|             vbd = {'bootable': True, 'userdevice': '0', 'VDI': self.ref,
 | |
|                    'other_config': {}, 'VM': controlDomainRef,
 | |
|                    'mode': 'rw', 'qos_algorithm_type': '', 'qos_algorithm_params': {},
 | |
|                    'type': 'Disk', 'empty': False, 'unpluggable': True}
 | |
|             vbdRef = xenapi.VBD.create(vbd)
 | |
|             
 | |
|             # Plug the VBD
 | |
|             xenapi.VBD.plug(vbdRef)
 | |
|     
 | |
|             # Create a temporary directory
 | |
|             if (not system.runSshCommand("mkdir -p /root/temp")):
 | |
|                 raise Exception ("Failed to create directory /root/temp")
 | |
|     
 | |
|             # Check if /dev/xvda1 exists
 | |
|             xvda1Exists = system.runSshCommand("ls /dev/xvda1")
 | |
|     
 | |
|             # If /dev/xvda1 doesn't exist, work with /dev/xvda
 | |
|             if (not xvda1Exists):
 | |
|                 # Mount /dev/xvda to /root/temp
 | |
|                 if (not system.runSshCommand("mount /dev/xvda /root/temp")):
 | |
|                     raise Exception("Failed to mount /dev/xvda to /root/temp")
 | |
|                 writeToLog("Using /dev/xvda to change bootable device name.", False)
 | |
|             else:
 | |
|                 # Mount /dev/xvda1 to /root/temp
 | |
|                 if (not system.runSshCommand("mount /dev/xvda1 /root/temp")):
 | |
|                     raise Exception("Failed to mount /dev/xvda1 to /root/temp")
 | |
| 
 | |
|                 # If the boot directory exists under /root/temp, we can work with xvda1 
 | |
|                 if (system.runSshCommand("ls /root/temp/boot")):
 | |
|                     writeToLog("Using /dev/xvda1 to change bootable device name.", False)
 | |
|                 else:
 | |
|                     # If the boot directory doesn't exist under /root/temp, we need to work with /dev/xvda2
 | |
|                     
 | |
|                     # Check that /dev/xvda2 exists
 | |
|                     if (not system.runSshCommand("ls /dev/xvda2")):
 | |
|                         raise Exception("/dev/xvda1 exists but /dev/xvda2 doesn't exist")
 | |
| 
 | |
|                     # Unmount /dev/xvda1
 | |
|                     if (not system.runSshCommand("umount /root/temp")):
 | |
|                         raise Exception("Failed to unmount /dev/xvda1")
 | |
| 
 | |
|                     # Mount /dev/xvda2
 | |
|                     if (not system.runSshCommand("mount /dev/xvda2 /root/temp")):
 | |
|                         raise Exception("Failed to mount /dev/xvda2")
 | |
| 
 | |
|                     writeToLog("Using /dev/xvda2 to change bootable device name.", False)
 | |
|     
 | |
|             # Modify fstab, grub.conf, and device.map, if they exist
 | |
|             for fileToModify in ["/root/temp/etc/fstab", "/root/temp/boot/grub/grub.conf", "/root/temp/boot/grub/device.map"]:
 | |
|                 if (system.runSshCommand("ls " + fileToModify)):
 | |
|                     if (not system.runSshCommand("sed -i 's_/dev/sda_/dev/xvda_' " + fileToModify)):
 | |
|                         raise Exception("Failed to modify " + fileToModify)
 | |
|         finally:
 | |
|             # Unmount /root/temp if necessary
 | |
|             if (system.runSshCommand("mount | grep '/root/temp'")):
 | |
|                 if (not system.runSshCommand("umount /root/temp")):
 | |
|                     raise Exception("Failed to unmount /root/temp")
 | |
| 
 | |
|             # Delete /root/temp 
 | |
|             system.runSshCommand("rm -rf /root/temp")
 | |
|             
 | |
|             if (vbdRef != None):
 | |
|                 # Unplug the VBD
 | |
|                 xenapi.VBD.unplug(vbdRef)
 | |
|         
 | |
|                 # Destroy the VBD
 | |
|                 xenapi.VBD.destroy(vbdRef)
 | |
| 
 | |
|         return True
 | |
|         
 | |
| class StoragePool:
 | |
|     # Vars: id, uuid
 | |
| 
 | |
|     def __init__(self, storagePoolId, uuid):
 | |
|         self.id = storagePoolId
 | |
|         self.uuid = uuid
 | |
| 
 | |
|     @staticmethod
 | |
|     def getStoragePool(system, storagePoolId):
 | |
|         columns = ["id", "uuid"]
 | |
|         storagePoolRow = system.db.getDbValues("storage_pool", columns, {"id":storagePoolId})[0]
 | |
|         return StoragePool(storagePoolRow["id"], storagePoolRow["uuid"])
 | |
| 
 | |
| class Host:
 | |
|     # Vars: id, ip, iqn
 | |
| 
 | |
|     def __init__(self, hostId, ip, iqn):
 | |
|         self.id = hostId
 | |
|         self.ip = ip
 | |
|         self.iqn = iqn
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "Host: id: %s | ip %s" % (self.id, self.ip)
 | |
| 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Sharing LUs...")
 | |
|     def shareAllLus():
 | |
|         return Host.shareOrUnshareAllLus(True)
 | |
| 
 | |
|     @staticmethod
 | |
|     @basicLogDecorator("Unsharing LUs...")
 | |
|     def unshareAllLus():
 | |
|         return Host.shareOrUnshareAllLus(False)    
 | |
| 
 | |
|     @staticmethod
 | |
|     def shareOrUnshareAllLus(share):
 | |
|         # Get a map of XenServer IPs -> IQNs
 | |
|         xenServerIqns = Host.getXenServerIqns()
 | |
|         
 | |
|         # Get a map of storage host IPs in the source system -> passwords
 | |
|         storageHostPasswords = GLOBALS["STORAGE_HOST_PASSWORDS"]
 | |
|         
 | |
|         # Copy share_all_lus.sh to each storage host and run with each XenServer IQN
 | |
|         for ip in storageHostPasswords.keys():
 | |
|             password = storageHostPasswords[ip]
 | |
|             sshConn = paramiko.SSHClient()
 | |
|             sshConn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 | |
|             sshConn.connect(ip, username = "root", password = password)            
 | |
|             sftpConn = sshConn.open_sftp()
 | |
|             sftpConn.put("share_all_lus.sh", "/root/share_all_lus.sh")
 | |
|             for xenServerIp in xenServerIqns.keys():
 | |
|                 iqn = xenServerIqns[xenServerIp]
 | |
|                 command = "bash /root/share_all_lus.sh -i " + iqn
 | |
|                 if (share):
 | |
|                     command += " -s"
 | |
|                 else:
 | |
|                     command += " -u"
 | |
|                 stdin, stdout, stderr = sshConn.exec_command(command)
 | |
|                 if (stdin.channel.recv_exit_status() != 0):
 | |
|                     return False
 | |
|             if (not share):
 | |
|                 sftpConn.remove("/root/share_all_lus.sh")
 | |
|             sshConn.close()
 | |
|             sftpConn.close()
 | |
| 
 | |
|         return True
 | |
|             
 | |
|     @staticmethod
 | |
|     def getHost(system, hostId):
 | |
|         columns = ["id", "private_ip_address", "iqn"]
 | |
|         hostRow =  system.db.getDbValues("host", columns, {"id":hostId})[0]
 | |
|         return Host(hostRow["id"], hostRow["private_ip_address"], hostRow["iqn"])
 | |
| 
 | |
|     @staticmethod
 | |
|     def getStorageHostIps():
 | |
|         storageHostIps = []
 | |
|         columns = ["private_ip_address"]
 | |
|         hostRows = GLOBALS["SRC_SYSTEM"].db.getDbValues("host", columns, {"type":"Storage"})
 | |
|         for hostRow in hostRows:
 | |
|             storageHostIps.append(hostRow["private_ip_address"])
 | |
|         return storageHostIps
 | |
| 
 | |
|     @staticmethod
 | |
|     def getXenServerIqns():
 | |
|         xenServerIqns = dict()
 | |
|         columns = ["private_ip_address", "url"]
 | |
|         hostRows = GLOBALS["DEST_SYSTEM"].db.getDbValues("host", columns, {"type":"Routing"})
 | |
|         for hostRow in hostRows:
 | |
|             xenServerIqns[hostRow["private_ip_address"]] = hostRow["url"]
 | |
|         return xenServerIqns
 | |
|     
 | |
| 
 | |
| ### Runtime
 | |
| 
 | |
| GLOBALS = dict()
 | |
| 
 | |
| @basicLogDecorator("Reading upgrade.properties...")
 | |
| def readUpgradeProperties():
 | |
|     upgradePropertiesFile = open("upgrade.properties", "r")    
 | |
|     upgradeProperties = upgradePropertiesFile.read().splitlines()
 | |
|     for upgradeProperty in upgradeProperties:
 | |
|         if (upgradeProperty.strip() == ""):
 | |
|             continue
 | |
|         elif (upgradeProperty.startswith("#")):
 | |
|             continue
 | |
|         else:
 | |
|             propList = upgradeProperty.split("=")
 | |
|             var = propList[0].strip()
 | |
|             val = propList[1].strip()
 | |
|             if (val == ""):
 | |
|                 continue
 | |
|             GLOBALS[var] = val
 | |
| 
 | |
|     # Create the log file
 | |
|     logFilePath = GLOBALS["LOG_FILE"]
 | |
|     GLOBALS["LOG_FILE"] = open(logFilePath, "a")
 | |
| 
 | |
|     # Create the guest OS map
 | |
|     GLOBALS["GUEST_OS_MAP"] = csvToMap(GLOBALS["GUEST_OS_MAP"])
 | |
| 
 | |
|     # Create the XenServer passwords map
 | |
|     if (GLOBALS.get("DEST_XENSERVER_PASSWORDS") != None):
 | |
|         GLOBALS["DEST_XENSERVER_PASSWORDS"] = csvToMap(GLOBALS.get("DEST_XENSERVER_PASSWORDS"))
 | |
| 
 | |
|     # Create the Storage Host passwords map
 | |
|     if (GLOBALS.get("STORAGE_HOST_PASSWORDS") == None):
 | |
|         raise Exception ("Please fill out the variable STORAGE_HOST_PASSWORDS in upgrade.properties.")
 | |
|     else:
 | |
|         GLOBALS["STORAGE_HOST_PASSWORDS"] = csvToMap(GLOBALS["STORAGE_HOST_PASSWORDS"])
 | |
| 
 | |
|     # Create the list of users to upgrade
 | |
|     if GLOBALS.has_key("USERS"):
 | |
|         GLOBALS["USERS"] = [userId.strip() for userId in GLOBALS["USERS"].split(",")]
 | |
|     else:
 | |
|         GLOBALS["USERS"] = None
 | |
| 
 | |
|     return True
 | |
| 
 | |
| def csvToMap(csv):
 | |
|     entries = csv.split(",")
 | |
|     entryMap = dict()
 | |
|     for entry in entries:
 | |
|         entryList = entry.strip().split(":")
 | |
|         key = entryList[0].strip()
 | |
|         val = entryList[1].strip()
 | |
|         entryMap[key] = val
 | |
|     return entryMap
 | |
|     
 | |
| @basicLogDecorator("Running diagnostic...")
 | |
| def runDiagnostic():    
 | |
|     # Either one XenServer IP and password should be specified, or a mapping between XenServer IPs and passwords should be specified
 | |
|     if ((GLOBALS.get("DEST_XENSERVER_IP") == None and (GLOBALS.get("DEST_XENSERVER_PASSWORD") != None or GLOBALS.get("DEST_XENSERVER_PASSWORDS") == None))
 | |
|         or (GLOBALS.get("DEST_XENSERVER_IP") != None and (GLOBALS.get("DEST_XENSERVER_PASSWORD") == None or GLOBALS.get("DEST_XENSERVER_PASSWORDS") != None))):
 | |
|         raise Exception("Please specify the IP and root password for one XenServer (if all XenServers have the same root password), or the IPs and root passwords of all XenServers.")
 | |
| 
 | |
|     GLOBALS["SRC_SYSTEM"] = System(GLOBALS["SRC_MANAGEMENT_SERVER_IP"], False, None, None, None, "vmops", GLOBALS["SRC_DB_LOGIN"],
 | |
|                                    GLOBALS.get("SRC_DB_PASSWORD"), GLOBALS["SRC_ZONE_ID"], None, None, None, None)
 | |
|     
 | |
|     GLOBALS["DEST_SYSTEM"] = System(GLOBALS["DEST_MANAGEMENT_SERVER_IP"], True, GLOBALS.get("DEST_XENSERVER_IP"),
 | |
|                                     GLOBALS.get("DEST_XENSERVER_PASSWORD"), GLOBALS.get("DEST_XENSERVER_PASSWORDS"),
 | |
|                                     "cloud", GLOBALS["DEST_DB_LOGIN"], GLOBALS.get("DEST_DB_PASSWORD"), GLOBALS["DEST_ZONE_ID"],
 | |
|                                     GLOBALS["DEST_TEMPLATE_ID"], GLOBALS["DEST_ISO_ID"], GLOBALS["DEST_SERVICE_OFFERING_ID"],
 | |
|                                     GLOBALS["DEST_DISK_OFFERING_ID"])
 | |
| 
 | |
|     srcSystemServiceOfferings = ServiceOffering.getSrcSystemServiceOfferings()
 | |
|     for srcSystemServiceOffering in srcSystemServiceOfferings:
 | |
|         # Every service offering in the src system must have a corresponding service offering in the dest system
 | |
|         destSystemServiceOffering = ServiceOffering.getCorrespondingServiceOffering(srcSystemServiceOffering.id)
 | |
|         if (destSystemServiceOffering == None):
 | |
|             raise Exception("No corresponding service offering found for: " + str(srcSystemServiceOffering))
 | |
| 
 | |
|         # Every service offering in the src system has a corresponding disk offering in the dest system
 | |
|         destSystemDiskOffering = DiskOffering.getCorrespondingDiskOffering(srcSystemServiceOffering.id)
 | |
|         if (destSystemDiskOffering == None):
 | |
|             raise Exception("No corresponding disk offering found for: " + str(srcSystemServiceOffering))
 | |
| 
 | |
|     # Every template ID in the src system has a valid entry in GUEST_OS_MAP
 | |
|     srcSystemTemplateIds = VM.getTemplateIds(GLOBALS["SRC_SYSTEM"])
 | |
|     for templateId in srcSystemTemplateIds:
 | |
|         if (not GLOBALS["GUEST_OS_MAP"].has_key(templateId)):
 | |
|             raise Exception("No corresponding guest OS ID for templateId: " + templateId)
 | |
|         else:
 | |
|             guestOsId = GLOBALS["GUEST_OS_MAP"][templateId]
 | |
|             guestOsName = VM.getGuestOsName(GLOBALS["DEST_SYSTEM"], guestOsId)
 | |
|             if (guestOsName == None):
 | |
|                 raise Exception("The guest OS ID that corresponds to template ID: " + templateId + " is not valid.")
 | |
| 
 | |
|     # The dest system's ISO id must be valid
 | |
|     template = VM.getTemplate(GLOBALS["DEST_SYSTEM"], GLOBALS["DEST_ISO_ID"])
 | |
|     if (template == None or template["format"] != "ISO"):
 | |
|         raise Exception("The dest system ISO ID is not valid.")
 | |
| 
 | |
|     # Verify that all source system storage hosts have a password
 | |
|     storageHostIps = Host.getStorageHostIps()
 | |
|     for ip in storageHostIps:
 | |
|         if (ip not in GLOBALS["STORAGE_HOST_PASSWORDS"].keys()):
 | |
|             raise Exception("The storage host IP: " + str(ip) + " has no entry in STORAGE_HOST_PASSWORDS.")
 | |
| 
 | |
|     return True        
 | |
| 
 | |
| @basicLogDecorator("Starting CloudStack Migration (1.0 -> 2.1)...")
 | |
| def upgradeUsers(userIds, onlyMigratePublicIps):
 | |
|     # Read variables from upgrade.properties
 | |
|     readUpgradeProperties()
 | |
| 
 | |
|     # Run the diagnostic
 | |
|     runDiagnostic()
 | |
| 
 | |
|     if (userIds == None):
 | |
|         if (GLOBALS["USERS"] == None):
 | |
|             raise Exception("Please specify one or more users to upgrade.")
 | |
|         else:
 | |
|             userIds = GLOBALS["USERS"]
 | |
| 
 | |
|     # Make sure all users are valid
 | |
|     for userId in userIds:
 | |
|         if (User.get(GLOBALS["SRC_SYSTEM"], userId) == None):
 | |
|             raise Exception("The user ID: " + str(userId) + " is not valid.")
 | |
| 
 | |
|     if (not onlyMigratePublicIps):
 | |
|         # Share all LUs
 | |
|         Host.shareAllLus()
 | |
|     
 | |
|     try:
 | |
|         for userId in userIds:
 | |
|             doUpgrade(userId, onlyMigratePublicIps)
 | |
|         return True
 | |
|     finally:
 | |
|         if (not onlyMigratePublicIps):
 | |
|             # Forget all iSCSI SRs
 | |
|             SR.forgetAllSrcSrs()
 | |
|             # Unshare all LUs
 | |
|             Host.unshareAllLus()
 | |
| 
 | |
| def doUpgrade(userId, onlyMigratePublicIps):            
 | |
|     # Get the specified user from the source system
 | |
|     srcUser = User.get(GLOBALS["SRC_SYSTEM"], userId)
 | |
| 
 | |
|     writeToLog("\nStarting migration for " + str(srcUser), True)
 | |
|     
 | |
|     # Create a new user in the destination system with the same attributes as the original user
 | |
|     destUser = User.create(GLOBALS["DEST_SYSTEM"], srcUser)
 | |
| 
 | |
|     if (not srcUser.alreadyMigrated()):
 | |
|         # Allocate the src user's public IPs in the dest system
 | |
|         allocatedPublicIps = PublicIp.migrateAllocatedPublicIps(srcUser, destUser)
 | |
| 
 | |
|         if (onlyMigratePublicIps):
 | |
|             writeToLog("\nMigrated public IPs for " + str(srcUser), True)
 | |
|             return 
 | |
| 
 | |
|         # Stop the source user's DomR
 | |
|         srcUserDomR = DomainRouter(srcUser)
 | |
| 
 | |
|         # Only migrate the user's VMs if there is a DomR
 | |
|         if (srcUserDomR.id != None):
 | |
|             srcUserDomR.stop()
 | |
|             
 | |
|             # If the dest user doesn't have a DomR, deploy a temporary VM
 | |
|             destUserDomR = DomainRouter(destUser)
 | |
|             tempVm = None
 | |
|             if (destUserDomR.id == None):
 | |
|                 tempVm = VM(None, destUser, None, None, None)
 | |
|                 tempVm.deployTemp()
 | |
|                 destUserDomR.id = destUserDomR.getId()
 | |
|             else:
 | |
|                 tempVm = VM.getTempVm(destUser)
 | |
|                             
 | |
|             # Migrate the source user's VM's to the dest system
 | |
|             vmIdMap = VM.migrateVirtualMachines(srcUser, destUser)    
 | |
|                 
 | |
|             # Migrate the source user's port forwarding and load balancer rules
 | |
|             ForwardingRule.migrateForwardingRules(srcUser, destUser, allocatedPublicIps, vmIdMap)
 | |
|         
 | |
|             # Reboot the dest user's router    
 | |
|             destUserDomR.reboot()
 | |
|         
 | |
|             # Destroy the temporary VM
 | |
|             if (tempVm != None):
 | |
|                 tempVm.destroy()
 | |
| 
 | |
|         srcUser.tagAsMigrated() 
 | |
|     else:
 | |
|         writeToLog("\n" + str(srcUser) + " has already been migrated.", True)
 | |
| 
 | |
|     writeToLog("\nMigration was successful for " + str(srcUser), True)
 | |
| 
 | |
| if (len(sys.argv) > 1):
 | |
|     if (sys.argv[1].lower() == "publicips"):
 | |
|         if (len(sys.argv) > 2):
 | |
|             upgradeUsers(sys.argv[2:], True)
 | |
|         else:
 | |
|             upgradeUsers(None, True)
 | |
|     else:
 | |
|         upgradeUsers(sys.argv[1:], False)
 | |
| else:
 | |
|     upgradeUsers(None, False)
 | |
|     
 |