package net.lamgc.scext.oraclemanager import com.google.gson.JsonElement import com.oracle.bmc.core.ComputeClient import com.oracle.bmc.core.VirtualNetworkClient import com.oracle.bmc.core.model.* import com.oracle.bmc.core.requests.* import com.oracle.bmc.model.BmcException import mu.KotlinLogging import net.lamgc.scalabot.extension.BotExtensionFactory import org.telegram.abilitybots.api.bot.BaseAbilityBot import org.telegram.abilitybots.api.objects.Reply import org.telegram.abilitybots.api.util.AbilityExtension import org.telegram.telegrambots.meta.api.methods.send.SendMessage import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageReplyMarkup import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText import org.telegram.telegrambots.meta.api.objects.replykeyboard.ForceReplyKeyboard import java.io.File class ServerExtensionFactory : BotExtensionFactory { override fun createExtensionInstance(bot: BaseAbilityBot, shareDataFolder: File): AbilityExtension { return OracleServerExtension(bot) } } @Suppress("unused") class OracleServerExtension(private val bot: BaseAbilityBot) : AbilityExtension { fun listServers() = callbackQueryOf("oc_server_list") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val client = ComputeClient(profile.getAuthenticationDetailsProvider()) val instances = client.listInstances( ListInstancesRequest.builder() .compartmentId(profile.tenantId) .build() ).items val keyboardBuilder = InlineKeyboardGroupBuilder() for (instance in instances) { keyboardBuilder .rowButton { text("${instance.displayName}【${instance.region}】") callbackData( action = "oc_server_manage", extraData = jsonObjectOf { JsonFields.AccountProfile += profile JsonFields.ServerInstance += ServerInstance.fromBmcInstance(instance) } ) } } keyboardBuilder.rowButton { text("<<< 返回上一级") callbackData(upd.callbackQuery.callbackData.next("oc_account_manager")) } EditMessageText.builder() .text("Oracle 账号 ${profile.name} 有下列服务器") .messageId(upd.callbackQuery.message.messageId) .chatId(upd.callbackQuery.message.chatId.toString()) .replyMarkup(keyboardBuilder.build()) .build().execute(bot.silent()) } fun manageServerReply(): Reply = callbackQueryOf("oc_server_manage") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val client = ComputeClient(profile.getAuthenticationDetailsProvider()) val instance = client.getInstance(GetInstanceRequest.builder().instanceId(instanceInfo.id).build()).instance val keyboardBuilder = InlineKeyboardGroupBuilder() .newRow() .addButton { text("电源") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_edit")) } .addButton { text("网络") callbackData(upd.callbackQuery.callbackData.next("oc_server_network_edit")) } .then() .rowButton { text("服务器设置") callbackData(upd.callbackQuery.callbackData.next("oc_server_edit")) } .rowButton { text("*** 刷新 ***") callbackData(upd.callbackQuery.callbackData) } .rowButton { text("<<< 返回上一级") callbackData(upd.callbackQuery.callbackData.next("oc_server_list")) } val shape = instance.shapeConfig EditMessageText.builder() .text( """ ${instanceInfo.displayName} 当前状态:${instance.lifecycleState} 配置:${shape.ocpus} Core (CPU:${shape.processorDescription}) / ${shape.memoryInGBs} GB (内存) GPU:${shape.gpus}(${shape.gpuDescription}) 网络带宽:${shape.networkingBandwidthInGbps} Gbps 区域:${instanceInfo.regionId} 可用区 / 容错区:${instanceInfo.availabilityDomain} / ${instanceInfo.faultDomain} """.trimIndent() ) .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup(keyboardBuilder.build()) .build().execute(bot.silent()) } fun editServerInstance(): Reply = callbackQueryOf("oc_server_edit") { bot, upd -> val keyboardBuilder = InlineKeyboardGroupBuilder() .newRow() .addButton { text("更改名称") callbackData(upd.callbackQuery.callbackData.next("oc_server_name_change")) } .then() .rowButton { text("释放服务器(永久删除)") callbackData(upd.callbackQuery.callbackData.next("oc_server_remove")) } .rowButton { text("<<< 返回上一级") callbackData(upd.callbackQuery.callbackData.next("oc_server_manage")) } EditMessageReplyMarkup.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup(keyboardBuilder.build()) .build().execute(bot.silent()) } fun changeServerName(): Reply = callbackQueryOf("oc_server_name_change") { bot, upd -> bot.db().getVar( "oc_server_name_change::chat_${upd.callbackQuery.message.chatId}::user_${upd.callbackQuery.from.id}::profile" ) .set(upd.callbackQuery.callbackData.toJson()) val message = SendMessage.builder() .text("请发送服务器的新名称。") .chatId(upd.callbackQuery.message.chatId.toString()) .replyMarkup( ForceReplyKeyboard.builder() .selective(true) .forceReply(true) .inputFieldPlaceholder("新的服务器名称") .build() ) .build().execute(bot.sender()) bot.db() .getVar("oc_server_name_change::chat_${upd.callbackQuery.message.chatId}::user_${upd.callbackQuery.from.id}::messageId") .set(message.messageId.toLong()) } fun changeServerNameExecute(): Reply = Reply.of({ bot, upd -> bot.db() .getVar("oc_server_name_change::chat_${upd.message.chatId}::user_${upd.message.from.id}::messageId") .set(null) val callbackEntry = bot.db().getVar( "oc_server_name_change::chat_${upd.message.chatId}::user_${upd.message.from.id}::profile" ) val callbackJson = callbackEntry.get() callbackEntry.set(null) val callback = InlineKeyboardCallback.fromJson(callbackJson) val profile = getProfileByCallback(callback) val client = ComputeClient(profile.getAuthenticationDetailsProvider()) val instanceInfo = ServerInstance.fromJson(callback.extraData[JsonFields.ServerInstance]) try { val instance = client.updateInstance( UpdateInstanceRequest.builder() .instanceId(instanceInfo.id) .updateInstanceDetails( UpdateInstanceDetails.builder() .displayName(upd.message.text.trim()) .build() ) .build() ).instance bot.silent().send("服务器改名成功!新的名称为:\n${instance.displayName}", upd.message.chatId) } catch (e: Exception) { logger.error(e) { "请求更改实例名称时发生错误." } bot.silent().send("", upd.message.chatId) } }, { it.hasMessage() && it.message.hasText() && it.message.isReply && bot.db() .getVar("oc_server_name_change::chat_${it.message.chatId}::user_${it.message.from.id}::messageId") .get() == it.message.replyToMessage.messageId.toLong() }) fun removeServerRequest(): Reply = callbackQueryOf("oc_server_remove") { bot, upd -> val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) // Minecraft .text( """ 你确定要删除这个实例吗? “${instanceInfo.displayName}”将会永久失去!(真的很久!) """.trimIndent() ) .replyMarkup( createPromptKeyboard( yesCallback = upd.callbackQuery.callbackData.next("oc_server_remove::confirm"), yesMsg = "删除它!", noCallback = upd.callbackQuery.callbackData.next("oc_server_manage"), ) ) .build().execute(bot.silent()) } fun removeServerConfirm(): Reply = callbackQueryOf("oc_server_remove::confirm") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val client = ComputeClient(profile.getAuthenticationDetailsProvider()) val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) try { client.terminateInstance( TerminateInstanceRequest.builder() .instanceId(instanceInfo.id) .build() ) EditMessageText.builder() .text("实例 ${instanceInfo.displayName} 已执行释放。") .messageId(upd.callbackQuery.message.messageId) .chatId(upd.callbackQuery.message.chatId.toString()) .replyMarkup(InlineKeyboardGroupBuilder() .rowButton { text("<<< 回到服务器列表") callbackData(upd.callbackQuery.callbackData.next("oc_server_list", jsonObjectOf { JsonFields.AccountProfile += profile }, replaceData = true)) } .build()) .build().execute(bot.silent()) } catch (e: BmcException) { logger.error(e) { "请求释放实例时发生错误." } bot.silent().send("请求释放实例时发生错误,请稍后重试。", upd.callbackQuery.message.chatId) } } fun editServerPower(): Reply = callbackQueryOf("oc_server_power_edit") { bot, upd -> val keyboardBuilder = InlineKeyboardGroupBuilder() .rowButton { text("开机") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_query", jsonObjectOf { JsonFields.PowerAction += InstanceAction.START.actionValue })) } .newRow() .addButton { text("重启(软重启)") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_query", jsonObjectOf { JsonFields.PowerAction += InstanceAction.SOFT_RESET.actionValue })) } .addButton { text("强制重启") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_query", jsonObjectOf { JsonFields.PowerAction += InstanceAction.RESET.actionValue })) } .newRow() .addButton { text("关机(软关机)") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_query", jsonObjectOf { JsonFields.PowerAction += InstanceAction.SOFT_STOP.actionValue })) } .addButton { text("强制关机") callbackData(upd.callbackQuery.callbackData.next("oc_server_power_query", jsonObjectOf { JsonFields.PowerAction += InstanceAction.STOP.actionValue })) } .then() .rowButton { text("<<< 返回上一级") callbackData(upd.callbackQuery.callbackData.next("oc_server_manage")) } EditMessageReplyMarkup.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup(keyboardBuilder.build()) .build().execute(bot.silent()) } fun queryServerPowerAction(): Reply = callbackQueryOf("oc_server_power_query") { bot, upd -> val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val powerAction = upd.callbackQuery.callbackData.extraData["power_action"] EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text( """ 实例 ${instanceInfo.displayName} 将要执行电源操作:$powerAction 确定执行操作吗? """.trimIndent() ) .replyMarkup(createPromptKeyboard( yesCallback = upd.callbackQuery.callbackData.next("oc_server_power_execute"), noCallback = upd.callbackQuery.callbackData.next( newAction = "oc_server_manage", replaceData = true, newExtraData = jsonObjectOf { JsonFields.AccountProfile += getProfileByCallback(upd.callbackQuery.callbackData) JsonFields.ServerInstance += upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance] } ) )) .build().execute(bot.silent()) } fun executeServerPowerAction(): Reply = callbackQueryOf("oc_server_power_execute") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val powerAction = upd.callbackQuery.callbackData.extraData["power_action"].asString val client = ComputeClient(profile.getAuthenticationDetailsProvider()) try { client.instanceAction( InstanceActionRequest.builder() .action(powerAction) .instanceId(instanceInfo.id) .build() ) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("操作已请求执行。") .replyMarkup(InlineKeyboardGroupBuilder() .rowButton { text("<<< 返回服务器") callbackData(upd.callbackQuery.callbackData.next( newAction = "oc_server_manage", replaceData = true, newExtraData = jsonObjectOf { JsonFields.AccountProfile += getProfileByCallback(upd.callbackQuery.callbackData) JsonFields.ServerInstance += upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance] } )) } .build()) .build().execute(bot.silent()) } catch (e: Exception) { logger.error(e) { "请求执行实例电源操作时发生错误." } bot.silent().send("请求电源操作时发生错误,请重试一次。", upd.callbackQuery.message.chatId) } } fun editServerNetworkMenu(): Reply = callbackQueryOf("oc_server_network_edit") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val adp = profile.getAuthenticationDetailsProvider() val computeClient = ComputeClient(adp) val networkClient = VirtualNetworkClient(adp) val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val vnicAttachments = computeClient.listVnicAttachments( ListVnicAttachmentsRequest.builder() .compartmentId(profile.tenantId) .instanceId(instanceInfo.id) .build() ).items val keyboardBuilder = InlineKeyboardGroupBuilder() for (vnicAttachment in vnicAttachments) { keyboardBuilder.rowButton { val vnic = networkClient.getVnic(GetVnicRequest.builder().vnicId(vnicAttachment.vnicId).build()).vnic text("${vnic.displayName}【${vnic.privateIp}】${if (vnic.isPrimary) "(主要)" else ""}") callbackData( upd.callbackQuery.callbackData.next( newAction = "oc_server_network_vnic_edit", newExtraData = jsonObjectOf { JsonFields.VnicId += vnicAttachment.vnicId }) ) } } keyboardBuilder.addBackButton(upd.callbackQuery.callbackData.next("oc_server_manage")) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup(keyboardBuilder.build()) .text( """ 服务器实例 ${instanceInfo.displayName} 有以下 VNIC(网卡): """.trimIndent() ) .build().execute(bot) } fun editServerVnicMenu(): Reply = callbackQueryOf("oc_server_network_vnic_edit") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val vnicId = upd.callbackQuery.callbackData.extraData[JsonFields.VnicId].asString val adp = profile.getAuthenticationDetailsProvider() val networkClient = VirtualNetworkClient(adp) val vnic = networkClient.getVnic(GetVnicRequest.builder().vnicId(vnicId).build()).vnic val keyboardBuilder = InlineKeyboardGroupBuilder() .rowButton { text("更换公网 IP(保留 IP)") callbackData( upd.callbackQuery.callbackData.next( "oc_server_network_change_ipv4pub_reserved" ) ) } .rowButton { text("更换公网 IP(临时 IP)") callbackData( upd.callbackQuery.callbackData.next( "oc_server_network_change_ipv4pub_random" ) ) } EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup( keyboardBuilder .addBackButton(upd.callbackQuery.callbackData.next("oc_server_network_edit")) .build() ) .text( """ ----------- [ VNIC ] ----------- ${vnic.displayName} 私有 IP:${vnic.privateIp} 公网 IP:${if (vnic.publicIp == null) "无" else vnic.publicIp} MAC 地址:${vnic.macAddress} 主要网卡:${if (vnic.isPrimary) "是" else "否"} 当前状态:${vnic.lifecycleState} """.trimIndent() ) .build().execute(bot) } fun changeReservedPublicIpList(): Reply = callbackQueryOf("oc_server_network_change_ipv4pub_reserved") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val adp = profile.getAuthenticationDetailsProvider() val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val networkClient = VirtualNetworkClient(adp) val publicIps = networkClient.listPublicIps( ListPublicIpsRequest.builder() .compartmentId(profile.tenantId) .scope(ListPublicIpsRequest.Scope.Region) .lifetime(ListPublicIpsRequest.Lifetime.Reserved) .build() ).items.filter { it.privateIpId == null && ( it.lifecycleState == PublicIp.LifecycleState.Available || it.lifecycleState == PublicIp.LifecycleState.Unassigned ) } if (publicIps.isEmpty()) { EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup( InlineKeyboardGroupBuilder() .addBackButton(upd.callbackQuery.callbackData.next("oc_server_network_vnic_edit")) .build() ) .text( """ 你还没有可以绑定到实例的公共 IP。 """.trimIndent() ) .build().execute(bot) return@callbackQueryOf } val keyboardBuilder = InlineKeyboardGroupBuilder() for (publicIp in publicIps) { keyboardBuilder.rowButton { text(publicIp.ipAddress) callbackData( upd.callbackQuery.callbackData.next( newAction = "oc_server_network_change_ipv4pub_reserved::prompt", newExtraData = jsonObjectOf { JsonFields.PublicIpId += publicIp.id }) ) } } bot.db().getVar( "oc_server_network_change_ipv4pub::" + "chat_${upd.callbackQuery.message.chatId}::user_${upd.callbackQuery.from.id}::flag" ).set(false) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup( keyboardBuilder .addBackButton(upd.callbackQuery.callbackData.next("oc_server_network_vnic_edit")) .build() ) .text( """ 选择一个绑定到 ${instanceInfo.displayName} 的公共 IP: """.trimIndent() ) .build().execute(bot) } fun changeReservedPublicIpListPrompt(): Reply = callbackQueryOf("oc_server_network_change_ipv4pub_reserved::prompt") { bot, upd -> val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup( createPromptKeyboard( yesCallback = upd.callbackQuery.callbackData.next("oc_server_network_change_ipv4pub_reserved::execute"), noCallback = upd.callbackQuery.callbackData.next("oc_server_network_change_ipv4pub_reserved") ) ) .text( "你真的要为实例 ${instanceInfo.displayName} 更换成预留公共 IP?\n" + "(如果原来的公共 IP 是预留 IP 的话,将不会删除该 IP)" ) .build().execute(bot) } fun changeReservedPublicIpListExecute(): Reply = callbackQueryOf("oc_server_network_change_ipv4pub_reserved::execute") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val vnicId = upd.callbackQuery.callbackData.extraData[JsonFields.VnicId].asString val adp = profile.getAuthenticationDetailsProvider() val networkClient = VirtualNetworkClient(adp) val afterPublicIpId = upd.callbackQuery.callbackData.extraData[JsonFields.PublicIpId].asString val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在准备更换公共 IP(预留)...") .build() .execute(bot) val vnic = networkClient.getVnic( GetVnicRequest.builder() .vnicId(vnicId).build() ).vnic val privateIp = networkClient.listPrivateIps( ListPrivateIpsRequest.builder().vnicId(vnicId).subnetId(vnic.subnetId).build() ).items.first() val beforePublicIp = try { networkClient.getPublicIpByPrivateIpId( GetPublicIpByPrivateIpIdRequest.builder() .getPublicIpByPrivateIpIdDetails( GetPublicIpByPrivateIpIdDetails.builder() .privateIpId(privateIp.id) .build() ) .build() ).publicIp } catch (e: BmcException) { if (e.statusCode != 404) { logger.error(e) { "获取原公共 IP 时发生错误." } bot.silent().send("获取原公共 IP 时发生错误。\n${e.message}", upd.callbackQuery.message.chatId) return@callbackQueryOf } null } if (beforePublicIp != null) { EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在解绑旧的公共 IP...") .build() .execute(bot) if (beforePublicIp.lifetime == PublicIp.Lifetime.Reserved) { networkClient.updatePublicIp( UpdatePublicIpRequest.builder() .publicIpId(beforePublicIp.id) .updatePublicIpDetails( UpdatePublicIpDetails.builder() .privateIpId("") .build() ) .build() ) } else { networkClient.deletePublicIp( DeletePublicIpRequest.builder() .publicIpId(beforePublicIp.id) .build() ) } } EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在绑定新的预留公共 IP...") .build() .execute(bot) val afterPublicIp = networkClient.updatePublicIp( UpdatePublicIpRequest.builder() .publicIpId(afterPublicIpId) .updatePublicIpDetails( UpdatePublicIpDetails.builder() .privateIpId(privateIp.id) .build() ) .build() ).publicIp EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text( """ 实例 ${instanceInfo.displayName} 的 VNIC ${vnic.displayName} 已成功更换公共 IP(保留) 新的公共 IP:${afterPublicIp.ipAddress} """.trimIndent() ) .replyMarkup( InlineKeyboardGroupBuilder().addBackButton( callback = upd.callbackQuery.callbackData.next("oc_server_network_vnic_edit") ).build() ) .build() .execute(bot) } fun changePublicIpRandomPrompt(): Reply = callbackQueryOf("oc_server_network_change_ipv4pub_random") { bot, upd -> val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .replyMarkup( createPromptKeyboard( yesCallback = upd.callbackQuery.callbackData.next("oc_server_network_change_ipv4pub_random::execute"), noCallback = upd.callbackQuery.callbackData.next("oc_server_network_vnic_edit") ) ) .text( "你真的要为实例 ${instanceInfo.displayName} 更换成临时公共 IP?\n" + "(如果原来的公共 IP 是预留 IP 的话,将不会删除该 IP)" ) .build().execute(bot) } fun changePublicIpRandomExecute(): Reply = callbackQueryOf("oc_server_network_change_ipv4pub_random::execute") { bot, upd -> val profile = getProfileByCallback(upd.callbackQuery.callbackData) val adp = profile.getAuthenticationDetailsProvider() val vnicId = upd.callbackQuery.callbackData.extraData[JsonFields.VnicId].asString val instanceInfo = ServerInstance.fromJson(upd.callbackQuery.callbackData.extraData[JsonFields.ServerInstance]) val networkClient = VirtualNetworkClient(adp) EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在准备更换公共 IP...") .build() .execute(bot) val vnic = networkClient.getVnic( GetVnicRequest.builder() .vnicId(vnicId) .build() ).vnic val privateIp = networkClient.listPrivateIps( ListPrivateIpsRequest.builder() .vnicId(vnicId) .subnetId(vnic.subnetId) .build() ).items.first() val beforePublicIp = try { networkClient.getPublicIpByPrivateIpId( GetPublicIpByPrivateIpIdRequest.builder() .getPublicIpByPrivateIpIdDetails( GetPublicIpByPrivateIpIdDetails.builder() .privateIpId(privateIp.id) .build() ) .build() ).publicIp } catch (e: BmcException) { if (e.statusCode != 404) { logger.error(e) { "获取原公共 IP 时发生错误." } bot.silent().send("获取原公共 IP 时发生错误。\n${e.message}", upd.callbackQuery.message.chatId) return@callbackQueryOf } null } if (beforePublicIp != null) { EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在解绑旧的公共 IP...") .build() .execute(bot) if (beforePublicIp.lifetime == PublicIp.Lifetime.Reserved) { networkClient.updatePublicIp( UpdatePublicIpRequest.builder() .publicIpId(beforePublicIp.id) .updatePublicIpDetails( UpdatePublicIpDetails.builder() .privateIpId("") .build() ) .build() ) } else { networkClient.deletePublicIp( DeletePublicIpRequest.builder() .publicIpId(beforePublicIp.id) .build() ) } } EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text("正在创建新的临时公共 IP...") .build() .execute(bot) val afterPublicIp = networkClient.createPublicIp( CreatePublicIpRequest.builder() .createPublicIpDetails( CreatePublicIpDetails.builder() .lifetime(CreatePublicIpDetails.Lifetime.Ephemeral) .privateIpId(privateIp.id) .compartmentId(profile.tenantId) .build() ) .build() ).publicIp EditMessageText.builder() .chatId(upd.callbackQuery.message.chatId.toString()) .messageId(upd.callbackQuery.message.messageId) .text( """ 实例 ${instanceInfo.displayName} 的 VNIC ${vnic.displayName} 已成功更换公共 IP(临时) 新的公共 IP:${afterPublicIp.ipAddress} """.trimIndent() ) .replyMarkup( InlineKeyboardGroupBuilder().addBackButton( callback = upd.callbackQuery.callbackData.next("oc_server_network_vnic_edit") ).build() ) .build() .execute(bot) } companion object { val logger = KotlinLogging.logger { } } } data class ServerInstance( val id: String, val displayName: String, val compartmentId: String, val availabilityDomain: String, val faultDomain: String, val regionId: String, val imageId: String ) { companion object { @JvmStatic fun fromJson(jsonStr: String): ServerInstance = gson.fromJson(jsonStr, ServerInstance::class.java) @JvmStatic fun fromJson(json: JsonElement): ServerInstance = gson.fromJson(json, ServerInstance::class.java) @JvmStatic fun fromBmcInstance(bmcInstance: Instance): ServerInstance = ServerInstance( id = bmcInstance.id, displayName = bmcInstance.displayName, compartmentId = bmcInstance.compartmentId, availabilityDomain = bmcInstance.availabilityDomain, faultDomain = bmcInstance.faultDomain, regionId = bmcInstance.region, imageId = bmcInstance.imageId ) } } /** * 实例动作. * * 可对实例执行的操作. * @author LamGC */ enum class InstanceAction( /** * 获取动作的 API 调用值. * @return 返回 API 所规定的对应值. */ val actionValue: String ) { /** * 启动实例. */ START("start"), /** * 硬停止实例. */ STOP("stop"), /** * 硬重启实例. */ RESET("reset"), /** * 软重启实例, 操作系统将按照正常的重启过程进行. */ SOFT_RESET("softreset"), /** * 软停止实例, 操作系统将按照正常的关机过程进行. */ SOFT_STOP("softstop"); }