def __init__(self, platforminfo, azureiaasclient, conn): self.client = azureiaasclient self.conn = conn self.platforminfo = platforminfo self.volumecontroller = azureVolumeController(self.platforminfo, self.client, self.conn) self.passwordencryptor = PasswordEncryptor()
class azureInstanceController(object): logger = IaasLogger() client = None conn = None platforminfo = None volumecontroller = None def __init__(self, platforminfo, azureiaasclient, conn): self.client = azureiaasclient self.conn = conn self.platforminfo = platforminfo self.volumecontroller = azureVolumeController(self.platforminfo, self.client, self.conn) self.passwordencryptor = PasswordEncryptor() def _generateOsHardDisk(self, storageAccountName, instanceName, imageName): # OS用ハードディスクの設定を生成する # URLを生成 mediaLink = 'https://%s.blob.core.windows.net/vhds/%s.vhd' % \ (storageAccountName, instanceName) return self.client.generateOSHardDiskConfig(mediaLink, imageName) def _generateConfigurationSet(self, osType, instanceNo): # 仮想マシン作成時に指定する設定を生成する instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) # ホスト名 hostName = instanceInfo['INSTANCE_NAME'] farmNo = instanceInfo['FARM_NO'] farmTable = self.conn.getTable("FARM") farmInfo = self.conn.selectOne(farmTable.select\ (farmTable.c.FARM_NO==farmNo)) userNo = farmInfo['USER_NO'] userTable = self.conn.getTable("USER") userInfo = self.conn.selectOne(userTable.select\ (userTable.c.USER_NO==userNo)) azureInstanceTable = self.conn.getTable("AZURE_INSTANCE") azureInstanceInfo = self.conn.selectOne(azureInstanceTable.select\ (azureInstanceTable.c.INSTANCE_NO==instanceNo)) platformNo = instanceInfo['PLATFORM_NO'] azureCertificateTable = self.conn.getTable("AZURE_CERTIFICATE") azureCertificateInfo = self.conn.selectOne(azureCertificateTable.select\ (azureCertificateTable.c.USER_NO==userNo and azureCertificateTable.c.PLATFORM_NO==platformNo)) keyPublic = azureCertificateInfo['KEY_PUBLIC'] # カスタムデータ userData = self.createUserData(instanceNo, instanceInfo, azureInstanceInfo, keyPublic) userData = self.makeUserData(userData) customData = userData self.logger.debug("userData:"+userData) # Salt情報 pccSystemInfoTable = self.conn.getTable("PCC_SYSTEM_INFO") pccSystemInfo = self.conn.selectOne(pccSystemInfoTable.select()) # パスワード復号化 userPassword = self.passwordencryptor.decrypt(userInfo['PASSWORD'], pccSystemInfo['SECRET_KEY']) if osType == 'Linux': # Linuxのユーザ名 (rootのsshキー認証が設定できるのが望ましい) userName = userInfo['USERNAME'] return self.client.generateLinuxConfig(hostName, userName, userPassword, customData) elif osType == 'Windows': # Windowsのユーザ名 userName = userInfo['USERNAME'] return self.client.generateWindowsConfig(hostName, userName, userPassword, customData) else: return None def _generateVirtualMachineName(self, instanceNo): # インスタンスNoとサーバ名を組み合わせてAzureの仮想マシン名を生成する instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) # サーバ名 instanceName = instanceInfo['INSTANCE_NAME'] # "インスタンスNo"_"サーバ名"をAzureの仮想マシン名とする return '%s_%s' % (instanceNo, instanceName) def _createInstance(self, platformNo, instanceNo): # 作成パラメータの取得 platformAzureTable = self.conn.getTable("PLATFORM_AZURE") platformAzureInfo = self.conn.selectOne(platformAzureTable.select\ (platformAzureTable.c.PLATFORM_NO==platformNo)) # クラウドサービス名 cloudService = platformAzureInfo['CLOUD_SERVICE_NAME'] self.logger.debug(' Cloud Serivce: %s' % (cloudService)) # ストレージアカウント名 storageAccount = platformAzureInfo['STORAGE_ACCOUNT_NAME'] self.logger.debug(' Storage Account: %s' % (storageAccount)) # ネットワーク名 networkName = platformAzureInfo['NETWORK_NAME'] self.logger.debug(' Network: %s' % (networkName)) # インスタンス名 instanceName = self._generateVirtualMachineName(instanceNo) self.logger.debug(' Virtual Machine Name: %s' % (instanceName)) # インスタンスタイプ azureInstanceTable = self.conn.getTable("AZURE_INSTANCE") azureInstanceInfo = self.conn.selectOne(azureInstanceTable.select\ (azureInstanceTable.c.INSTANCE_NO==instanceNo)) instanceType = azureInstanceInfo['INSTANCE_TYPE'] self.logger.debug(' Instance Type: %s' % (instanceType)) # 可用性セット availabilitySet = azureInstanceInfo['AVAILABILITY_SET'] self.logger.debug(' Availability Set: %s' % (availabilitySet)) # サブネットID subnetID = azureInstanceInfo['SUBNET_ID'] self.logger.debug(' Subnet ID: %s' % (subnetID)) networkConfig = self.client.generateNetworkConfig(subnetID) # イメージ名 instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) imageNo = instanceInfo['IMAGE_NO'] imageAzureTable = self.conn.getTable("IMAGE_AZURE") imageAzureInfo = self.conn.selectOne(imageAzureTable.select\ (imageAzureTable.c.IMAGE_NO==imageNo)) imageName = imageAzureInfo['IMAGE_NAME'] self.logger.debug(' Image: %s' % (imageName)) # OSタイプ(Azureから取得) osType = self.client.identifyImageOsType(imageName) self.logger.debug(' OS: %s' % (osType)) # ホスト名、パスワード等のOSにまつわる設定 osConfig = self._generateConfigurationSet(osType, instanceNo) osHardDisk = self._generateOsHardDisk(storageAccount, instanceName, imageName) # VM作成 status = self.client.createVirtualMachine(cloudService, instanceName, \ osConfig, osHardDisk, instanceType, networkConfig, networkName, availabilitySet) # プライベートIPを取得する前に、VMの作成成功を確認する privateIp = self.client.getVirtualMachineIpAddress(cloudService, instanceName) self.logger.debug(' Instance: %s, Status: %s, Private IP Address: %s' \ % (instanceName, status, privateIp)) # 異常系テストコード #status = 'None' if status != 'ReadyRole': # 新規作成時、VMが起動成功しなかったら、データベースを更新せずに終了 # status ReadyRoleになる前にタイムアウトした場合と、最終的にReadyRoleにならない場合に分けて検討する必要あり。 raise IaasException("EPROCESS-000918", [instanceName, status]) # データベース更新 table = self.conn.getTable("AZURE_INSTANCE") updateDict = self.conn.selectOne(table.select(table.c.INSTANCE_NO==instanceNo)) updateDict["INSTANCE_NAME"] = instanceName updateDict["STATUS"] = status updateDict["PRIVATE_IP_ADDRESS"] = privateIp sql = table.update(table.c.INSTANCE_NO == updateDict["INSTANCE_NO"], values=updateDict) self.conn.execute(sql) def startInstance(self, instanceNo): self.logger.info(' StartInstance: %s' % (instanceNo)) # プラットフォーム番号の取得 instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) platformNo = instanceInfo['PLATFORM_NO'] # 仮想マシンの起動実績確認 azureInstanceTable = self.conn.getTable("AZURE_INSTANCE") azureInstanceInfo = self.conn.selectOne(azureInstanceTable.select\ (azureInstanceTable.c.INSTANCE_NO==instanceNo)) # インスタンス名 instanceName = azureInstanceInfo['INSTANCE_NAME'] if isEmpty(instanceName): # 仮想マシンの新規作成 self._createInstance(platformNo, instanceNo) return else: # クラウドサービス名 platformAzureTable = self.conn.getTable("PLATFORM_AZURE") platformAzureInfo = self.conn.selectOne(platformAzureTable.select\ (platformAzureTable.c.PLATFORM_NO==platformNo)) cloudService = platformAzureInfo['CLOUD_SERVICE_NAME'] # 既存仮想マシンの起動 # Azure上に仮想マシンが存在するか確認 status = self.client.getVirtualMachineStatus(cloudService, instanceName) self.logger.info(' Instance: %s, Status: %s' % (instanceName, status)) if status is None: # インスタンスが存在しないまたは、状態取得に失敗した場合は、処理を終了する # ステータスは変更しない raise IaasException("EPROCESS-000914", [instanceName]) elif status == 'ReadyRole': self.logger.info(' Instance %s is already running' % (instanceName)) # インスタンスタイプをチェックし、食い違う場合は警告をログに出力する # インスタンスタイプ instanceType = azureInstanceInfo['INSTANCE_TYPE'] currentInstanceType = self.client.getVirtualMachineType(cloudService, instanceName) if currentInstanceType != instanceType: raise IaasException("EPROCESS-000913", [instanceName]) # Azure上では起動している場合、DBを更新して終了。 pass else: # ReadyRole以外の場合、起動を試みる # インスタンスタイプをチェックし、食い違う場合は警告をログに出力する # インスタンスタイプ instanceType = azureInstanceInfo['INSTANCE_TYPE'] currentInstanceType = self.client.getVirtualMachineType(cloudService, instanceName) if currentInstanceType != instanceType: self.logger.info(' Changing its instance type to %s' % (instanceType)) # サブネット subnetID = azureInstanceInfo['SUBNET_ID'] self.logger.info(' Subnet ID: %s' % (subnetID)) networkConfig = self.client.generateNetworkConfig(subnetID) # 可用性セット availabilitySet = azureInstanceInfo['AVAILABILITY_SET'] self.logger.info(' Availability Set: %s' % (availabilitySet)) # インスタンスタイプの変更を反映 newInstanceType = self.client.updateVirtualMachineType(cloudService, instanceName, instanceType, \ networkConfig, availabilitySet) if isEmpty(newInstanceType): # インスタンスタイプの変更に失敗した場合は、起動も行わない raise IaasException("EPROCESS-000913", [instanceName]) self.logger.info(' Instance type for %s has been updated: %s' % (instanceName, newInstanceType)) # インスタンス起動 status = self.client.startVirtualMachine(cloudService, instanceName) # 異常系テストコード #status = 'None' if status != 'ReadyRole': # VMが起動成功しなかったら、データベースを更新せずに終了 raise IaasException("EPROCESS-000906", [instanceName, status]) # データベース更新 table = self.conn.getTable("AZURE_INSTANCE") updateDict = self.conn.selectOne(table.select(table.c.INSTANCE_NO==instanceNo)) updateDict["STATUS"] = status sql = table.update(table.c.INSTANCE_NO == updateDict["INSTANCE_NO"], values=updateDict) self.conn.execute(sql) return def stopInstance(self, instanceNo): # インスタンス名の取得 azureInstanceTable = self.conn.getTable("AZURE_INSTANCE") azureInstanceInfo = self.conn.selectOne(azureInstanceTable.select\ (azureInstanceTable.c.INSTANCE_NO==instanceNo)) instanceName = azureInstanceInfo['INSTANCE_NAME'] #1度も起動されていない if (isEmpty(instanceName)) : return instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) platformNo = instanceInfo['PLATFORM_NO'] platformAzureTable = self.conn.getTable("PLATFORM_AZURE") platformAzureInfo = self.conn.selectOne(platformAzureTable.select\ (platformAzureTable.c.PLATFORM_NO==platformNo)) # クラウドサービス名の取得 cloudService = platformAzureInfo['CLOUD_SERVICE_NAME'] status = self.client.getVirtualMachineStatus(cloudService, instanceName) self.logger.info(' Instance: %s, Status: %s' % (instanceName, status)) if status != 'ReadyRole' and status != 'RoleStateUnknown': if status == 'StoppedVM': self.logger.info(' Instance: %s is already stopped' % (instanceName)) else: # 停止できる状態ではない。 raise IaasException("EPROCESS-000908", [instanceName, status]) else: status = self.client.stopVirtualMachine(cloudService, instanceName) # データベース更新 table = self.conn.getTable("AZURE_INSTANCE") updateDict = self.conn.selectOne(table.select(table.c.INSTANCE_NO==instanceNo)) updateDict["STATUS"] = status sql = table.update(table.c.INSTANCE_NO == updateDict["INSTANCE_NO"], values=updateDict) self.conn.execute(sql) # ボリュームに関する処理 azureDiskTable = self.conn.getTable("AZURE_DISK") disks = self.conn.select(azureDiskTable.select(azureDiskTable.c.INSTANCE_NO==instanceNo)) for azureDisk in disks: self.volumecontroller.stopVolume(instanceNo, azureDisk["DISK_NO"]) return def terminateInstance(self, instanceNo): instanceTable = self.conn.getTable("INSTANCE") instanceInfo = self.conn.selectOne(instanceTable.select\ (instanceTable.c.INSTANCE_NO==instanceNo)) platformNo = instanceInfo['PLATFORM_NO'] platformAzureTable = self.conn.getTable("PLATFORM_AZURE") platformAzureInfo = self.conn.selectOne(platformAzureTable.select\ (platformAzureTable.c.PLATFORM_NO==platformNo)) # クラウドサービス名の取得 cloudService = platformAzureInfo['CLOUD_SERVICE_NAME'] # ストレージアカウント名の取得 storageAccount = platformAzureInfo['STORAGE_ACCOUNT_NAME'] # インスタンス名の取得 azureInstanceTable = self.conn.getTable("AZURE_INSTANCE") azureInstanceInfo = self.conn.selectOne(azureInstanceTable.select\ (azureInstanceTable.c.INSTANCE_NO==instanceNo)) instanceName = azureInstanceInfo['INSTANCE_NAME'] status = self.client.getVirtualMachineStatus(cloudService, instanceName) self.logger.info(' Instance: %s, Status: %s' % (instanceName, status)) if status is None: # 既に削除されているので、何もしない self.logger.info(' Instance: %s does not exist, Status: %s' % (instanceName, status)) self.conn.info(instanceInfo["FARM_NO"], None, None, instanceNo, \ instanceInfo["INSTANCE_NAME"], "AzureInstanceNotExist",["AZURE", instanceName, status]) elif status == 'StoppedVM': self.client.deleteVirtualMachine(cloudService, instanceName, storageAccount, \ instanceNo, instanceInfo["FARM_NO"], instanceInfo["INSTANCE_NAME"]) self.logger.info(' Terminated %s' % (instanceName)) else: # 削除を許さない。 self.logger.error(' Instance: %s cannot be terminated because the status is not StoppedVM (%s)' % (instanceName, status)) self.conn.error(instanceInfo["FARM_NO"], None, None, instanceNo, \ instanceInfo["INSTANCE_NAME"], "AzureInstanceDeleteFail",["AZURE", instanceName, status]) raise IaasException("EPROCESS-000904", [instanceName]) # データベース更新 # テスト目的で、terminate後のDB状態でstart(作成)できるようにしている table = self.conn.getTable("AZURE_INSTANCE") updateDict = self.conn.selectOne(table.select(table.c.INSTANCE_NO==instanceNo)) updateDict["INSTANCE_NAME"] = None updateDict["STATUS"] = None updateDict["SUBNET_ID"] = None updateDict["PRIVATE_IP_ADDRESS"] = None sql = table.update(table.c.INSTANCE_NO == updateDict["INSTANCE_NO"], values=updateDict) self.conn.execute(sql) return def createUserData(self, instanceNo, pccInstance, azureInstance, keyPublic): table = self.conn.getTable("FARM") farm = self.conn.selectOne(table.select(table.c.FARM_NO==pccInstance["FARM_NO"])) #UserDataを作成 userData = {} #DB情報 userData.update({"instanceName": pccInstance["INSTANCE_NAME"]}) userData.update({"farmName": farm["FARM_NAME"]}) # FQDN userData.update({"hostname": pccInstance["FQDN"]}) #初期スクリプト情報 userData.update({"scriptserver": getScriptProperty("script.server")}) # Publicキー userData.update({"sshpubkey": keyPublic}) #DNS情報 userData.update(self.createDnsUserData(instanceNo)) # Puppet情報 userData.update(self.createPuppetUserData()) # VPN情報 # VPNについては別途検討とする #internal = self.platforminfo["internal"] #if (internal == 0): # userData.update(self.createVpnUserData(pccInstance)) return userData; def createDnsUserData(self,instanceNo): # UserDataを作成 userData = {} # Primary DNSサーバ userData.update({"dns": getDnsProperty("dns.server")}) # Secondry DNSサーバ dns2 = getDnsProperty("dns.server2") if (isNotEmpty(dns2)): userData.update({"dns2": dns2}) # DNSドメイン userData.update({"dnsdomain": getDnsProperty("dns.domain")}) return userData; def createPuppetUserData(self): # UserDataを作成 userData = {} # PuppetMaster情報 userData.update({"puppetmaster": getPuppetProperty("puppet.masterHost")}) return userData; def createVpnUserData(self, pccInstance): # UserDataを作成 userData = {} #VPN情報のユーザとパスワードをセットする userData.update({"vpnuser": pccInstance["FQDN"]}) userData.update({"vpnuserpass": pccInstance["INSTANCE_CODE"]}) # VPNサーバ情報 userData.update({"vpnserver": getVpnProperty("vpn.server")}) userData.update({"vpnport": getVpnProperty("vpn.port")}) # userData.update({"vpnuser": getVpnProperty("vpn.user")}) # userData.update({"vpnuserpass": getVpnProperty("vpn.userpass")}) # ZIPパスワード userData.update({"vpnzippass": getVpnProperty("vpn.zippass")}) # クライアント証明書ダウンロードURL userData.update({"vpnclienturl": getVpnProperty("vpn.clienturl")}) return userData; def makeUserData(self, map): if not map or len(map) == 0: return None userdata = '' for key in map.keys(): value = map[key] if isNotEmpty(value): if userdata != '': userdata = userdata + ';' userdata = userdata + key + "=" + value return userdata