├── .github └── workflows │ └── release.yml ├── .gitignore ├── Makefile ├── README.md ├── doc ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png └── 9.png ├── go.mod ├── go.sum ├── main.go └── oci-help.ini /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | # Controls when the workflow will run 4 | on: 5 | release: 6 | types: [published] 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: ^1.17.1 21 | 22 | - name: Cache go module 23 | uses: actions/cache@v2 24 | with: 25 | path: | 26 | ~/.cache/go-build 27 | ~/go/pkg/mod 28 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 29 | restore-keys: | 30 | ${{ runner.os }}-go- 31 | 32 | - name: Build 33 | if: startsWith(github.ref, 'refs/tags/') 34 | env: 35 | NAME: oci-help 36 | BINDIR: build 37 | run: | 38 | make release -j$(nproc) 39 | 40 | - name: Upload Release 41 | uses: softprops/action-gh-release@v1 42 | if: startsWith(github.ref, 'refs/tags/') 43 | with: 44 | files: build/*.zip 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .DS_Store 18 | 19 | *.pem 20 | build/ 21 | example/ -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := oci-help 2 | PACKAGE_NAME := github.com/lemoex/oci-help 3 | VERSION := $(shell git describe --tags || echo "unknown-version") 4 | COMMIT := $(shell git rev-parse HEAD) 5 | BUILDTIME := $(shell date -u "+%Y-%m-%d %H:%M:%S %Z") 6 | BUILD_DIR := build 7 | VAR_SETTING := -X "$(PACKAGE_NAME)/constant.Version=$(VERSION)" -X "$(PACKAGE_NAME)/constant.Commit=$(COMMIT)" -X "$(PACKAGE_NAME)/constant.BuildTime=$(BUILDTIME)" 8 | GOBUILD = CGO_ENABLED=0 go build -trimpath -ldflags '-s -w -buildid= $(VAR_SETTING)' \ 9 | -o $(BUILD_DIR) 10 | 11 | PLATFORM_LIST = \ 12 | darwin-amd64 \ 13 | darwin-arm64 \ 14 | linux-amd64 \ 15 | linux-arm64 \ 16 | windows-amd64 \ 17 | windows-arm64 18 | 19 | 20 | zip_release = $(addsuffix .zip, $(PLATFORM_LIST)) 21 | 22 | 23 | .PHONY: build clean release 24 | normal: clean build 25 | 26 | clean: 27 | @rm -rf $(BUILD_DIR) 28 | @echo "Cleaning up." 29 | 30 | $(zip_release): %.zip : % 31 | @zip -du $(BUILD_DIR)/$(NAME)-$<-$(VERSION).zip -j -m $(BUILD_DIR)/$ 下载地址: https://github.com/lemoex/oci-help/releases/latest 9 | 10 | 11 | ## 获取配置信息 12 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/1.png) 13 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/2.png) 14 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/3.png) 15 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/4.png) 16 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/5.png) 17 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/6.png) 18 | 19 | 20 | ## 编辑配置文件 21 | 用文本编辑器打开在第一步获取到的 `oci-help.ini` 文件,进行如下配置: 22 | 23 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/7.png) 24 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/8.png) 25 | 26 | ## Telegram 消息提醒配置 27 | ![image](https://github.com/lemoex/oci-help/raw/main/doc/9.png) 28 | 29 | > BotFather: https://t.me/BotFather 30 | > IDBot: https://t.me/myidbot 31 | 32 | 33 | ## 运行程序 34 | ```bash 35 | # 前台运行程序 36 | ./oci-help 37 | 38 | # 前台运行需要一直开着终端窗口,可以在 Screen 中运行程序,以实现断开终端窗口后一直运行。 39 | # 创建 Screen 终端 40 | screen -S oci-help 41 | # 在 Screen 中运行程序 42 | ./oci-help 43 | # 离开 Screen 终端 44 | 按下 Ctrl 键不松,依次按字母 A 键和 D 键。或者直接关闭终端窗口也可以。 45 | # 查看已创建的 Screen 终端 46 | screen -ls 47 | # 重新连接 Screen 终端 48 | screen -r oci-help 49 | ``` -------------------------------------------------------------------------------- /doc/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/1.png -------------------------------------------------------------------------------- /doc/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/2.png -------------------------------------------------------------------------------- /doc/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/3.png -------------------------------------------------------------------------------- /doc/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/4.png -------------------------------------------------------------------------------- /doc/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/5.png -------------------------------------------------------------------------------- /doc/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/6.png -------------------------------------------------------------------------------- /doc/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/7.png -------------------------------------------------------------------------------- /doc/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/8.png -------------------------------------------------------------------------------- /doc/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemoex/oci-help/5cae07b160bdd3832995b8a0251e2f4a5a206a3a/doc/9.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module oci-help 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/oracle/oci-go-sdk/v54 v54.0.0 7 | github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b // indirect 8 | gopkg.in/ini.v1 v1.63.2 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/oracle/oci-go-sdk/v54 v54.0.0 h1:CDLjeSejv2aDpElAJrhKpi6zvT/zhZCZuXchUUZ+LS4= 4 | github.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b h1:br+bPNZsJWKicw/5rALEo67QHs5weyD5tf8WST+4sJ0= 8 | github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 9 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 12 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 13 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= 16 | gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 19 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | 甲骨文云API文档 3 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/ 4 | 5 | 实例: 6 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Instance/ 7 | VCN: 8 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Vcn/ 9 | Subnet: 10 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Subnet/ 11 | VNIC: 12 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Vnic/ 13 | VnicAttachment: 14 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/VnicAttachment/ 15 | 私有IP 16 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/PrivateIp/ 17 | 公共IP 18 | https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/PublicIp/ 19 | 20 | 获取可用性域 21 | https://docs.oracle.com/en-us/iaas/api/#/en/identity/20160918/AvailabilityDomain/ListAvailabilityDomains 22 | */ 23 | package main 24 | 25 | import ( 26 | "bytes" 27 | "context" 28 | "encoding/json" 29 | "errors" 30 | "flag" 31 | "fmt" 32 | "io" 33 | "io/ioutil" 34 | "math" 35 | "math/rand" 36 | "net/http" 37 | "net/url" 38 | "os" 39 | "os/exec" 40 | "strconv" 41 | "strings" 42 | "sync" 43 | "text/tabwriter" 44 | "time" 45 | 46 | "github.com/oracle/oci-go-sdk/v54/common" 47 | "github.com/oracle/oci-go-sdk/v54/core" 48 | "github.com/oracle/oci-go-sdk/v54/example/helpers" 49 | "github.com/oracle/oci-go-sdk/v54/identity" 50 | "gopkg.in/ini.v1" 51 | ) 52 | 53 | const ( 54 | defConfigFilePath = "./oci-help.ini" 55 | IPsFilePrefix = "IPs" 56 | ) 57 | 58 | var ( 59 | configFilePath string 60 | provider common.ConfigurationProvider 61 | computeClient core.ComputeClient 62 | networkClient core.VirtualNetworkClient 63 | storageClient core.BlockstorageClient 64 | identityClient identity.IdentityClient 65 | ctx context.Context = context.Background() 66 | oracleSections []*ini.Section 67 | oracleSection *ini.Section 68 | oracleSectionName string 69 | oracle Oracle 70 | instanceBaseSection *ini.Section 71 | instance Instance 72 | proxy string 73 | token string 74 | chat_id string 75 | cmd string 76 | sendMessageUrl string 77 | editMessageUrl string 78 | EACH bool 79 | availabilityDomains []identity.AvailabilityDomain 80 | ) 81 | 82 | type Oracle struct { 83 | User string `ini:"user"` 84 | Fingerprint string `ini:"fingerprint"` 85 | Tenancy string `ini:"tenancy"` 86 | Region string `ini:"region"` 87 | Key_file string `ini:"key_file"` 88 | Key_password string `ini:"key_password"` 89 | } 90 | 91 | type Instance struct { 92 | AvailabilityDomain string `ini:"availabilityDomain"` 93 | SSH_Public_Key string `ini:"ssh_authorized_key"` 94 | VcnDisplayName string `ini:"vcnDisplayName"` 95 | SubnetDisplayName string `ini:"subnetDisplayName"` 96 | Shape string `ini:"shape"` 97 | OperatingSystem string `ini:"OperatingSystem"` 98 | OperatingSystemVersion string `ini:"OperatingSystemVersion"` 99 | InstanceDisplayName string `ini:"instanceDisplayName"` 100 | Ocpus float32 `ini:"cpus"` 101 | MemoryInGBs float32 `ini:"memoryInGBs"` 102 | Burstable string `ini:"burstable"` 103 | BootVolumeSizeInGBs int64 `ini:"bootVolumeSizeInGBs"` 104 | Sum int32 `ini:"sum"` 105 | Each int32 `ini:"each"` 106 | Retry int32 `ini:"retry"` 107 | CloudInit string `ini:"cloud-init"` 108 | MinTime int32 `ini:"minTime"` 109 | MaxTime int32 `ini:"maxTime"` 110 | } 111 | 112 | type Message struct { 113 | OK bool `json:"ok"` 114 | Result `json:"result"` 115 | ErrorCode int `json:"error_code"` 116 | Description string `json:"description"` 117 | } 118 | type Result struct { 119 | MessageId int `json:"message_id"` 120 | } 121 | 122 | func main() { 123 | flag.StringVar(&configFilePath, "config", defConfigFilePath, "配置文件路径") 124 | flag.StringVar(&configFilePath, "c", defConfigFilePath, "配置文件路径") 125 | flag.Parse() 126 | 127 | cfg, err := ini.Load(configFilePath) 128 | helpers.FatalIfError(err) 129 | defSec := cfg.Section(ini.DefaultSection) 130 | proxy = defSec.Key("proxy").Value() 131 | token = defSec.Key("token").Value() 132 | chat_id = defSec.Key("chat_id").Value() 133 | cmd = defSec.Key("cmd").Value() 134 | if defSec.HasKey("EACH") { 135 | EACH, _ = defSec.Key("EACH").Bool() 136 | } else { 137 | EACH = true 138 | } 139 | sendMessageUrl = "https://api.telegram.org/bot" + token + "/sendMessage" 140 | editMessageUrl = "https://api.telegram.org/bot" + token + "/editMessageText" 141 | rand.Seed(time.Now().UnixNano()) 142 | 143 | sections := cfg.Sections() 144 | oracleSections = []*ini.Section{} 145 | for _, sec := range sections { 146 | if len(sec.ParentKeys()) == 0 { 147 | user := sec.Key("user").Value() 148 | fingerprint := sec.Key("fingerprint").Value() 149 | tenancy := sec.Key("tenancy").Value() 150 | region := sec.Key("region").Value() 151 | key_file := sec.Key("key_file").Value() 152 | if user != "" && fingerprint != "" && tenancy != "" && region != "" && key_file != "" { 153 | oracleSections = append(oracleSections, sec) 154 | } 155 | } 156 | } 157 | if len(oracleSections) == 0 { 158 | fmt.Printf("\033[1;31m未找到正确的配置信息, 请参考链接文档配置相关信息。链接: https://github.com/lemoex/oci-help\033[0m\n") 159 | return 160 | } 161 | instanceBaseSection = cfg.Section("INSTANCE") 162 | 163 | listOracleAccount() 164 | } 165 | 166 | func listOracleAccount() { 167 | if len(oracleSections) == 1 { 168 | oracleSection = oracleSections[0] 169 | } else { 170 | fmt.Printf("\n\033[1;32m%s\033[0m\n\n", "欢迎使用甲骨文实例管理工具") 171 | w := new(tabwriter.Writer) 172 | w.Init(os.Stdout, 4, 8, 1, '\t', 0) 173 | fmt.Fprintf(w, "%s\t%s\t\n", "序号", "账号") 174 | for i, section := range oracleSections { 175 | fmt.Fprintf(w, "%d\t%s\t\n", i+1, section.Name()) 176 | } 177 | w.Flush() 178 | fmt.Printf("\n") 179 | var input string 180 | var index int 181 | for { 182 | fmt.Print("请输入账号对应的序号进入相关操作: ") 183 | _, err := fmt.Scanln(&input) 184 | if err != nil { 185 | return 186 | } 187 | if strings.EqualFold(input, "oci") { 188 | multiBatchLaunchInstances() 189 | listOracleAccount() 190 | return 191 | } else if strings.EqualFold(input, "ip") { 192 | multiBatchListInstancesIp() 193 | listOracleAccount() 194 | return 195 | } 196 | index, _ = strconv.Atoi(input) 197 | if 0 < index && index <= len(oracleSections) { 198 | break 199 | } else { 200 | index = 0 201 | input = "" 202 | fmt.Printf("\033[1;31m错误! 请输入正确的序号\033[0m\n") 203 | } 204 | } 205 | oracleSection = oracleSections[index-1] 206 | } 207 | 208 | var err error 209 | //ctx = context.Background() 210 | err = initVar(oracleSection) 211 | if err != nil { 212 | return 213 | } 214 | // 获取可用性域 215 | fmt.Println("正在获取可用性域...") 216 | availabilityDomains, err = ListAvailabilityDomains() 217 | if err != nil { 218 | printlnErr("获取可用性域失败", err.Error()) 219 | return 220 | } 221 | 222 | //getUsers() 223 | 224 | showMainMenu() 225 | } 226 | 227 | func initVar(oracleSec *ini.Section) (err error) { 228 | oracleSectionName = oracleSec.Name() 229 | oracle = Oracle{} 230 | err = oracleSec.MapTo(&oracle) 231 | if err != nil { 232 | printlnErr("解析账号相关参数失败", err.Error()) 233 | return 234 | } 235 | provider, err = getProvider(oracle) 236 | if err != nil { 237 | printlnErr("获取 Provider 失败", err.Error()) 238 | return 239 | } 240 | 241 | computeClient, err = core.NewComputeClientWithConfigurationProvider(provider) 242 | if err != nil { 243 | printlnErr("创建 ComputeClient 失败", err.Error()) 244 | return 245 | } 246 | setProxyOrNot(&computeClient.BaseClient) 247 | networkClient, err = core.NewVirtualNetworkClientWithConfigurationProvider(provider) 248 | if err != nil { 249 | printlnErr("创建 VirtualNetworkClient 失败", err.Error()) 250 | return 251 | } 252 | setProxyOrNot(&networkClient.BaseClient) 253 | storageClient, err = core.NewBlockstorageClientWithConfigurationProvider(provider) 254 | if err != nil { 255 | printlnErr("创建 BlockstorageClient 失败", err.Error()) 256 | return 257 | } 258 | setProxyOrNot(&storageClient.BaseClient) 259 | identityClient, err = identity.NewIdentityClientWithConfigurationProvider(provider) 260 | if err != nil { 261 | printlnErr("创建 IdentityClient 失败", err.Error()) 262 | return 263 | } 264 | setProxyOrNot(&identityClient.BaseClient) 265 | return 266 | } 267 | 268 | func showMainMenu() { 269 | fmt.Printf("\n\033[1;32m欢迎使用甲骨文实例管理工具\033[0m \n(当前账号: %s)\n\n", oracleSection.Name()) 270 | fmt.Printf("\033[1;36m%s\033[0m %s\n", "1.", "查看实例") 271 | fmt.Printf("\033[1;36m%s\033[0m %s\n", "2.", "创建实例") 272 | fmt.Printf("\033[1;36m%s\033[0m %s\n", "3.", "管理引导卷") 273 | fmt.Print("\n请输入序号进入相关操作: ") 274 | var input string 275 | var num int 276 | fmt.Scanln(&input) 277 | if strings.EqualFold(input, "oci") { 278 | batchLaunchInstances(oracleSection) 279 | showMainMenu() 280 | return 281 | } else if strings.EqualFold(input, "ip") { 282 | IPsFilePath := IPsFilePrefix + "-" + time.Now().Format("2006-01-02-150405.txt") 283 | batchListInstancesIp(IPsFilePath, oracleSection) 284 | showMainMenu() 285 | return 286 | } 287 | num, _ = strconv.Atoi(input) 288 | switch num { 289 | case 1: 290 | listInstances() 291 | case 2: 292 | listLaunchInstanceTemplates() 293 | case 3: 294 | listBootVolumes() 295 | default: 296 | if len(oracleSections) > 1 { 297 | listOracleAccount() 298 | } 299 | } 300 | } 301 | 302 | func listInstances() { 303 | fmt.Println("正在获取实例数据...") 304 | var instances []core.Instance 305 | var ins []core.Instance 306 | var nextPage *string 307 | var err error 308 | for { 309 | ins, nextPage, err = ListInstances(ctx, computeClient, nextPage) 310 | if err == nil { 311 | instances = append(instances, ins...) 312 | } 313 | if nextPage == nil || len(ins) == 0 { 314 | break 315 | } 316 | } 317 | 318 | if err != nil { 319 | printlnErr("获取失败, 回车返回上一级菜单.", err.Error()) 320 | fmt.Scanln() 321 | showMainMenu() 322 | return 323 | } 324 | if len(instances) == 0 { 325 | fmt.Printf("\033[1;32m实例为空, 回车返回上一级菜单.\033[0m") 326 | fmt.Scanln() 327 | showMainMenu() 328 | return 329 | } 330 | fmt.Printf("\n\033[1;32m实例信息\033[0m \n(当前账号: %s)\n\n", oracleSection.Name()) 331 | w := new(tabwriter.Writer) 332 | w.Init(os.Stdout, 4, 8, 1, '\t', 0) 333 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", "序号", "名称", "状态  ", "配置") 334 | //fmt.Printf("%-5s %-28s %-18s %-20s\n", "序号", "名称", "公共IP", "配置") 335 | for i, ins := range instances { 336 | // 获取实例公共IP 337 | /* 338 | var strIps string 339 | ips, err := getInstancePublicIps(ctx, computeClient, networkClient, ins.Id) 340 | if err != nil { 341 | strIps = err.Error() 342 | } else { 343 | strIps = strings.Join(ips, ",") 344 | } 345 | */ 346 | //fmt.Printf("%-7d %-30s %-20s %-20s\n", i+1, *ins.DisplayName, strIps, *ins.Shape) 347 | 348 | fmt.Fprintf(w, "%d\t%s\t%s\t%s\t\n", i+1, *ins.DisplayName, getInstanceState(ins.LifecycleState), *ins.Shape) 349 | } 350 | w.Flush() 351 | fmt.Println("--------------------") 352 | fmt.Printf("\n\033[1;32ma: %s b: %s c: %s d: %s\033[0m\n", "启动全部", "停止全部", "重启全部", "终止全部") 353 | var input string 354 | var index int 355 | for { 356 | fmt.Print("请输入序号查看实例详细信息: ") 357 | _, err := fmt.Scanln(&input) 358 | if err != nil { 359 | showMainMenu() 360 | return 361 | } 362 | switch input { 363 | case "a": 364 | fmt.Printf("确定启动全部实例?(输入 y 并回车): ") 365 | var input string 366 | fmt.Scanln(&input) 367 | if strings.EqualFold(input, "y") { 368 | for _, ins := range instances { 369 | _, err := instanceAction(ins.Id, core.InstanceActionActionStart) 370 | if err != nil { 371 | fmt.Printf("\033[1;31m实例 %s 启动失败.\033[0m %s\n", *ins.DisplayName, err.Error()) 372 | } else { 373 | fmt.Printf("\033[1;32m实例 %s 启动成功.\033[0m\n", *ins.DisplayName) 374 | } 375 | } 376 | } else { 377 | continue 378 | } 379 | time.Sleep(1 * time.Second) 380 | listInstances() 381 | return 382 | case "b": 383 | fmt.Printf("确定停止全部实例?(输入 y 并回车): ") 384 | var input string 385 | fmt.Scanln(&input) 386 | if strings.EqualFold(input, "y") { 387 | for _, ins := range instances { 388 | _, err := instanceAction(ins.Id, core.InstanceActionActionSoftstop) 389 | if err != nil { 390 | fmt.Printf("\033[1;31m实例 %s 停止失败.\033[0m %s\n", *ins.DisplayName, err.Error()) 391 | } else { 392 | fmt.Printf("\033[1;32m实例 %s 停止成功.\033[0m\n", *ins.DisplayName) 393 | } 394 | } 395 | } else { 396 | continue 397 | } 398 | time.Sleep(1 * time.Second) 399 | listInstances() 400 | return 401 | case "c": 402 | fmt.Printf("确定重启全部实例?(输入 y 并回车): ") 403 | var input string 404 | fmt.Scanln(&input) 405 | if strings.EqualFold(input, "y") { 406 | for _, ins := range instances { 407 | _, err := instanceAction(ins.Id, core.InstanceActionActionSoftreset) 408 | if err != nil { 409 | fmt.Printf("\033[1;31m实例 %s 重启失败.\033[0m %s\n", *ins.DisplayName, err.Error()) 410 | } else { 411 | fmt.Printf("\033[1;32m实例 %s 重启成功.\033[0m\n", *ins.DisplayName) 412 | } 413 | } 414 | } else { 415 | continue 416 | } 417 | time.Sleep(1 * time.Second) 418 | listInstances() 419 | return 420 | case "d": 421 | fmt.Printf("确定终止全部实例?(输入 y 并回车): ") 422 | var input string 423 | fmt.Scanln(&input) 424 | if strings.EqualFold(input, "y") { 425 | for _, ins := range instances { 426 | err := terminateInstance(ins.Id) 427 | if err != nil { 428 | fmt.Printf("\033[1;31m实例 %s 终止失败.\033[0m %s\n", *ins.DisplayName, err.Error()) 429 | } else { 430 | fmt.Printf("\033[1;32m实例 %s 终止成功.\033[0m\n", *ins.DisplayName) 431 | } 432 | } 433 | } else { 434 | continue 435 | } 436 | time.Sleep(1 * time.Second) 437 | listInstances() 438 | return 439 | } 440 | index, _ = strconv.Atoi(input) 441 | if 0 < index && index <= len(instances) { 442 | break 443 | } else { 444 | input = "" 445 | index = 0 446 | fmt.Printf("\033[1;31m错误! 请输入正确的序号\033[0m\n") 447 | } 448 | } 449 | instanceDetails(instances[index-1].Id) 450 | } 451 | 452 | func instanceDetails(instanceId *string) { 453 | for { 454 | fmt.Println("正在获取实例详细信息...") 455 | instance, err := getInstance(instanceId) 456 | if err != nil { 457 | fmt.Printf("\033[1;31m获取实例详细信息失败, 回车返回上一级菜单.\033[0m") 458 | fmt.Scanln() 459 | listInstances() 460 | return 461 | } 462 | vnics, err := getInstanceVnics(instanceId) 463 | if err != nil { 464 | fmt.Printf("\033[1;31m获取实例VNIC失败, 回车返回上一级菜单.\033[0m") 465 | fmt.Scanln() 466 | listInstances() 467 | return 468 | } 469 | var publicIps = make([]string, 0) 470 | var strPublicIps string 471 | if err != nil { 472 | strPublicIps = err.Error() 473 | } else { 474 | for _, vnic := range vnics { 475 | if vnic.PublicIp != nil { 476 | publicIps = append(publicIps, *vnic.PublicIp) 477 | } 478 | } 479 | strPublicIps = strings.Join(publicIps, ",") 480 | } 481 | 482 | fmt.Printf("\n\033[1;32m实例详细信息\033[0m \n(当前账号: %s)\n\n", oracleSection.Name()) 483 | fmt.Println("--------------------") 484 | fmt.Printf("名称: %s\n", *instance.DisplayName) 485 | fmt.Printf("状态: %s\n", getInstanceState(instance.LifecycleState)) 486 | fmt.Printf("公共IP: %s\n", strPublicIps) 487 | fmt.Printf("可用性域: %s\n", *instance.AvailabilityDomain) 488 | fmt.Printf("配置: %s\n", *instance.Shape) 489 | fmt.Printf("OCPU计数: %g\n", *instance.ShapeConfig.Ocpus) 490 | fmt.Printf("网络带宽(Gbps): %g\n", *instance.ShapeConfig.NetworkingBandwidthInGbps) 491 | fmt.Printf("内存(GB): %g\n\n", *instance.ShapeConfig.MemoryInGBs) 492 | fmt.Printf("Oracle Cloud Agent 插件配置情况\n") 493 | fmt.Printf("监控插件已禁用?: %t\n", *instance.AgentConfig.IsMonitoringDisabled) 494 | fmt.Printf("管理插件已禁用?: %t\n", *instance.AgentConfig.IsManagementDisabled) 495 | fmt.Printf("所有插件均已禁用?: %t\n", *instance.AgentConfig.AreAllPluginsDisabled) 496 | for _, value := range instance.AgentConfig.PluginsConfig { 497 | fmt.Printf("%s: %s\n", *value.Name, value.DesiredState) 498 | } 499 | fmt.Println("--------------------") 500 | fmt.Printf("\n\033[1;32m1: %s 2: %s 3: %s 4: %s 5: %s\033[0m\n", "启动", "停止", "重启", "终止", "更换公共IP") 501 | fmt.Printf("\033[1;32m6: %s 7: %s 8: %s\033[0m\n", "升级/降级", "修改名称", "Oracle Cloud Agent 插件配置") 502 | var input string 503 | var num int 504 | fmt.Print("\n请输入需要执行操作的序号: ") 505 | fmt.Scanln(&input) 506 | num, _ = strconv.Atoi(input) 507 | switch num { 508 | case 1: 509 | _, err := instanceAction(instance.Id, core.InstanceActionActionStart) 510 | if err != nil { 511 | fmt.Printf("\033[1;31m启动实例失败.\033[0m %s\n", err.Error()) 512 | } else { 513 | fmt.Printf("\033[1;32m正在启动实例, 请稍后查看实例状态\033[0m\n") 514 | } 515 | time.Sleep(1 * time.Second) 516 | 517 | case 2: 518 | _, err := instanceAction(instance.Id, core.InstanceActionActionSoftstop) 519 | if err != nil { 520 | fmt.Printf("\033[1;31m停止实例失败.\033[0m %s\n", err.Error()) 521 | } else { 522 | fmt.Printf("\033[1;32m正在停止实例, 请稍后查看实例状态\033[0m\n") 523 | } 524 | time.Sleep(1 * time.Second) 525 | 526 | case 3: 527 | _, err := instanceAction(instance.Id, core.InstanceActionActionSoftreset) 528 | if err != nil { 529 | fmt.Printf("\033[1;31m重启实例失败.\033[0m %s\n", err.Error()) 530 | } else { 531 | fmt.Printf("\033[1;32m正在重启实例, 请稍后查看实例状态\033[0m\n") 532 | } 533 | time.Sleep(1 * time.Second) 534 | 535 | case 4: 536 | fmt.Printf("确定终止实例?(输入 y 并回车): ") 537 | var input string 538 | fmt.Scanln(&input) 539 | if strings.EqualFold(input, "y") { 540 | err := terminateInstance(instance.Id) 541 | if err != nil { 542 | fmt.Printf("\033[1;31m终止实例失败.\033[0m %s\n", err.Error()) 543 | } else { 544 | fmt.Printf("\033[1;32m正在终止实例, 请稍后查看实例状态\033[0m\n") 545 | } 546 | time.Sleep(1 * time.Second) 547 | } 548 | 549 | case 5: 550 | if len(vnics) == 0 { 551 | fmt.Printf("\033[1;31m实例已终止或获取实例VNIC失败,请稍后重试.\033[0m\n") 552 | break 553 | } 554 | fmt.Printf("将删除当前公共IP并创建一个新的公共IP。确定更换实例公共IP?(输入 y 并回车): ") 555 | var input string 556 | fmt.Scanln(&input) 557 | if strings.EqualFold(input, "y") { 558 | publicIp, err := changePublicIp(vnics) 559 | if err != nil { 560 | fmt.Printf("\033[1;31m更换实例公共IP失败.\033[0m %s\n", err.Error()) 561 | } else { 562 | fmt.Printf("\033[1;32m更换实例公共IP成功, 实例公共IP: \033[0m%s\n", *publicIp.IpAddress) 563 | } 564 | time.Sleep(1 * time.Second) 565 | } 566 | 567 | case 6: 568 | fmt.Printf("升级/降级实例, 请输入CPU个数: ") 569 | var input string 570 | var ocpus float32 571 | var memoryInGBs float32 572 | fmt.Scanln(&input) 573 | value, _ := strconv.ParseFloat(input, 32) 574 | ocpus = float32(value) 575 | input = "" 576 | fmt.Printf("升级/降级实例, 请输入内存大小: ") 577 | fmt.Scanln(&input) 578 | value, _ = strconv.ParseFloat(input, 32) 579 | memoryInGBs = float32(value) 580 | fmt.Println("正在升级/降级实例...") 581 | _, err := updateInstance(instance.Id, nil, &ocpus, &memoryInGBs, nil, nil) 582 | if err != nil { 583 | fmt.Printf("\033[1;31m升级/降级实例失败.\033[0m %s\n", err.Error()) 584 | } else { 585 | fmt.Printf("\033[1;32m升级/降级实例成功.\033[0m\n") 586 | } 587 | time.Sleep(1 * time.Second) 588 | 589 | case 7: 590 | fmt.Printf("请为实例输入一个新的名称: ") 591 | var input string 592 | fmt.Scanln(&input) 593 | fmt.Println("正在修改实例名称...") 594 | _, err := updateInstance(instance.Id, &input, nil, nil, nil, nil) 595 | if err != nil { 596 | fmt.Printf("\033[1;31m修改实例名称失败.\033[0m %s\n", err.Error()) 597 | } else { 598 | fmt.Printf("\033[1;32m修改实例名称成功.\033[0m\n") 599 | } 600 | time.Sleep(1 * time.Second) 601 | 602 | case 8: 603 | fmt.Printf("Oracle Cloud Agent 插件配置, 请输入 (1: 启用管理和监控插件; 2: 禁用管理和监控插件): ") 604 | var input string 605 | fmt.Scanln(&input) 606 | if input == "1" { 607 | disable := false 608 | _, err := updateInstance(instance.Id, nil, nil, nil, instance.AgentConfig.PluginsConfig, &disable) 609 | if err != nil { 610 | fmt.Printf("\033[1;31m启用管理和监控插件失败.\033[0m %s\n", err.Error()) 611 | } else { 612 | fmt.Printf("\033[1;32m启用管理和监控插件成功.\033[0m\n") 613 | } 614 | } else if input == "2" { 615 | disable := true 616 | _, err := updateInstance(instance.Id, nil, nil, nil, instance.AgentConfig.PluginsConfig, &disable) 617 | if err != nil { 618 | fmt.Printf("\033[1;31m禁用管理和监控插件失败.\033[0m %s\n", err.Error()) 619 | } else { 620 | fmt.Printf("\033[1;32m禁用管理和监控插件成功.\033[0m\n") 621 | } 622 | } else { 623 | fmt.Printf("\033[1;31m输入错误.\033[0m\n") 624 | } 625 | time.Sleep(1 * time.Second) 626 | 627 | default: 628 | listInstances() 629 | return 630 | } 631 | } 632 | } 633 | 634 | func listBootVolumes() { 635 | var bootVolumes []core.BootVolume 636 | var wg sync.WaitGroup 637 | for _, ad := range availabilityDomains { 638 | wg.Add(1) 639 | go func(adName *string) { 640 | defer wg.Done() 641 | volumes, err := getBootVolumes(adName) 642 | if err != nil { 643 | printlnErr("获取引导卷失败", err.Error()) 644 | } else { 645 | bootVolumes = append(bootVolumes, volumes...) 646 | } 647 | }(ad.Name) 648 | } 649 | wg.Wait() 650 | 651 | fmt.Printf("\n\033[1;32m引导卷\033[0m \n(当前账号: %s)\n\n", oracleSection.Name()) 652 | w := new(tabwriter.Writer) 653 | w.Init(os.Stdout, 4, 8, 1, '\t', 0) 654 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", "序号", "名称", "状态  ", "大小(GB)") 655 | for i, volume := range bootVolumes { 656 | fmt.Fprintf(w, "%d\t%s\t%s\t%d\t\n", i+1, *volume.DisplayName, getBootVolumeState(volume.LifecycleState), *volume.SizeInGBs) 657 | } 658 | w.Flush() 659 | fmt.Printf("\n") 660 | var input string 661 | var index int 662 | for { 663 | fmt.Print("请输入序号查看引导卷详细信息: ") 664 | _, err := fmt.Scanln(&input) 665 | if err != nil { 666 | showMainMenu() 667 | return 668 | } 669 | index, _ = strconv.Atoi(input) 670 | if 0 < index && index <= len(bootVolumes) { 671 | break 672 | } else { 673 | input = "" 674 | index = 0 675 | fmt.Printf("\033[1;31m错误! 请输入正确的序号\033[0m\n") 676 | } 677 | } 678 | bootvolumeDetails(bootVolumes[index-1].Id) 679 | } 680 | 681 | func bootvolumeDetails(bootVolumeId *string) { 682 | for { 683 | fmt.Println("正在获取引导卷详细信息...") 684 | bootVolume, err := getBootVolume(bootVolumeId) 685 | if err != nil { 686 | fmt.Printf("\033[1;31m获取引导卷详细信息失败, 回车返回上一级菜单.\033[0m") 687 | fmt.Scanln() 688 | listBootVolumes() 689 | return 690 | } 691 | 692 | attachments, err := listBootVolumeAttachments(bootVolume.AvailabilityDomain, bootVolume.CompartmentId, bootVolume.Id) 693 | attachIns := make([]string, 0) 694 | if err != nil { 695 | attachIns = append(attachIns, err.Error()) 696 | } else { 697 | for _, attachment := range attachments { 698 | ins, err := getInstance(attachment.InstanceId) 699 | if err != nil { 700 | attachIns = append(attachIns, err.Error()) 701 | } else { 702 | attachIns = append(attachIns, *ins.DisplayName) 703 | } 704 | } 705 | } 706 | 707 | var performance string 708 | switch *bootVolume.VpusPerGB { 709 | case 10: 710 | performance = fmt.Sprintf("均衡 (VPU:%d)", *bootVolume.VpusPerGB) 711 | case 20: 712 | performance = fmt.Sprintf("性能较高 (VPU:%d)", *bootVolume.VpusPerGB) 713 | default: 714 | performance = fmt.Sprintf("UHP (VPU:%d)", *bootVolume.VpusPerGB) 715 | } 716 | 717 | fmt.Printf("\n\033[1;32m引导卷详细信息\033[0m \n(当前账号: %s)\n\n", oracleSection.Name()) 718 | fmt.Println("--------------------") 719 | fmt.Printf("名称: %s\n", *bootVolume.DisplayName) 720 | fmt.Printf("状态: %s\n", getBootVolumeState(bootVolume.LifecycleState)) 721 | fmt.Printf("可用性域: %s\n", *bootVolume.AvailabilityDomain) 722 | fmt.Printf("大小(GB): %d\n", *bootVolume.SizeInGBs) 723 | fmt.Printf("性能: %s\n", performance) 724 | fmt.Printf("附加的实例: %s\n", strings.Join(attachIns, ",")) 725 | fmt.Println("--------------------") 726 | fmt.Printf("\n\033[1;32m1: %s 2: %s 3: %s 4: %s\033[0m\n", "修改性能", "修改大小", "分离引导卷", "终止引导卷") 727 | var input string 728 | var num int 729 | fmt.Print("\n请输入需要执行操作的序号: ") 730 | fmt.Scanln(&input) 731 | num, _ = strconv.Atoi(input) 732 | switch num { 733 | case 1: 734 | fmt.Printf("修改引导卷性能, 请输入 (1: 均衡; 2: 性能较高): ") 735 | var input string 736 | fmt.Scanln(&input) 737 | if input == "1" { 738 | _, err := updateBootVolume(bootVolume.Id, nil, common.Int64(10)) 739 | if err != nil { 740 | fmt.Printf("\033[1;31m修改引导卷性能失败.\033[0m %s\n", err.Error()) 741 | } else { 742 | fmt.Printf("\033[1;32m修改引导卷性能成功, 请稍后查看引导卷状态\033[0m\n") 743 | } 744 | } else if input == "2" { 745 | _, err := updateBootVolume(bootVolume.Id, nil, common.Int64(20)) 746 | if err != nil { 747 | fmt.Printf("\033[1;31m修改引导卷性能失败.\033[0m %s\n", err.Error()) 748 | } else { 749 | fmt.Printf("\033[1;32m修改引导卷性能成功, 请稍后查看引导卷信息\033[0m\n") 750 | } 751 | } else { 752 | fmt.Printf("\033[1;31m输入错误.\033[0m\n") 753 | } 754 | time.Sleep(1 * time.Second) 755 | 756 | case 2: 757 | fmt.Printf("修改引导卷大小, 请输入 (例如修改为50GB, 输入50): ") 758 | var input string 759 | var sizeInGBs int64 760 | fmt.Scanln(&input) 761 | sizeInGBs, _ = strconv.ParseInt(input, 10, 64) 762 | if sizeInGBs > 0 { 763 | _, err := updateBootVolume(bootVolume.Id, &sizeInGBs, nil) 764 | if err != nil { 765 | fmt.Printf("\033[1;31m修改引导卷大小失败.\033[0m %s\n", err.Error()) 766 | } else { 767 | fmt.Printf("\033[1;32m修改引导卷大小成功, 请稍后查看引导卷信息\033[0m\n") 768 | } 769 | } else { 770 | fmt.Printf("\033[1;31m输入错误.\033[0m\n") 771 | } 772 | time.Sleep(1 * time.Second) 773 | 774 | case 3: 775 | fmt.Printf("确定分离引导卷?(输入 y 并回车): ") 776 | var input string 777 | fmt.Scanln(&input) 778 | if strings.EqualFold(input, "y") { 779 | for _, attachment := range attachments { 780 | _, err := detachBootVolume(attachment.Id) 781 | if err != nil { 782 | fmt.Printf("\033[1;31m分离引导卷失败.\033[0m %s\n", err.Error()) 783 | } else { 784 | fmt.Printf("\033[1;32m分离引导卷成功, 请稍后查看引导卷信息\033[0m\n") 785 | } 786 | } 787 | } 788 | time.Sleep(1 * time.Second) 789 | 790 | case 4: 791 | fmt.Printf("确定终止引导卷?(输入 y 并回车): ") 792 | var input string 793 | fmt.Scanln(&input) 794 | if strings.EqualFold(input, "y") { 795 | _, err := deleteBootVolume(bootVolume.Id) 796 | if err != nil { 797 | fmt.Printf("\033[1;31m终止引导卷失败.\033[0m %s\n", err.Error()) 798 | } else { 799 | fmt.Printf("\033[1;32m终止引导卷成功, 请稍后查看引导卷信息\033[0m\n") 800 | } 801 | 802 | } 803 | time.Sleep(1 * time.Second) 804 | 805 | default: 806 | listBootVolumes() 807 | return 808 | } 809 | } 810 | } 811 | 812 | func listLaunchInstanceTemplates() { 813 | var instanceSections []*ini.Section 814 | instanceSections = append(instanceSections, instanceBaseSection.ChildSections()...) 815 | instanceSections = append(instanceSections, oracleSection.ChildSections()...) 816 | if len(instanceSections) == 0 { 817 | fmt.Printf("\033[1;31m未找到实例模版, 回车返回上一级菜单.\033[0m") 818 | fmt.Scanln() 819 | showMainMenu() 820 | return 821 | } 822 | 823 | for { 824 | fmt.Printf("\n\033[1;32m选择对应的实例模版开始创建实例\033[0m \n(当前账号: %s)\n\n", oracleSectionName) 825 | w := new(tabwriter.Writer) 826 | w.Init(os.Stdout, 4, 8, 1, '\t', 0) 827 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", "序号", "配置", "CPU个数", "内存(GB)") 828 | for i, instanceSec := range instanceSections { 829 | cpu := instanceSec.Key("cpus").Value() 830 | if cpu == "" { 831 | cpu = "-" 832 | } 833 | memory := instanceSec.Key("memoryInGBs").Value() 834 | if memory == "" { 835 | memory = "-" 836 | } 837 | fmt.Fprintf(w, "%d\t%s\t%s\t%s\t\n", i+1, instanceSec.Key("shape").Value(), cpu, memory) 838 | } 839 | w.Flush() 840 | fmt.Printf("\n") 841 | var input string 842 | var index int 843 | for { 844 | fmt.Print("请输入需要创建的实例的序号: ") 845 | _, err := fmt.Scanln(&input) 846 | if err != nil { 847 | showMainMenu() 848 | return 849 | } 850 | index, _ = strconv.Atoi(input) 851 | if 0 < index && index <= len(instanceSections) { 852 | break 853 | } else { 854 | input = "" 855 | index = 0 856 | fmt.Printf("\033[1;31m错误! 请输入正确的序号\033[0m\n") 857 | } 858 | } 859 | 860 | instanceSection := instanceSections[index-1] 861 | instance = Instance{} 862 | err := instanceSection.MapTo(&instance) 863 | if err != nil { 864 | printlnErr("解析实例模版参数失败", err.Error()) 865 | continue 866 | } 867 | 868 | LaunchInstances(availabilityDomains) 869 | } 870 | 871 | } 872 | 873 | func multiBatchLaunchInstances() { 874 | IPsFilePath := IPsFilePrefix + "-" + time.Now().Format("2006-01-02-150405.txt") 875 | for _, sec := range oracleSections { 876 | var err error 877 | err = initVar(sec) 878 | if err != nil { 879 | continue 880 | } 881 | // 获取可用性域 882 | availabilityDomains, err = ListAvailabilityDomains() 883 | if err != nil { 884 | printlnErr("获取可用性域失败", err.Error()) 885 | continue 886 | } 887 | batchLaunchInstances(sec) 888 | batchListInstancesIp(IPsFilePath, sec) 889 | command(cmd) 890 | sleepRandomSecond(5, 5) 891 | } 892 | } 893 | 894 | func batchLaunchInstances(oracleSec *ini.Section) { 895 | var instanceSections []*ini.Section 896 | instanceSections = append(instanceSections, instanceBaseSection.ChildSections()...) 897 | instanceSections = append(instanceSections, oracleSec.ChildSections()...) 898 | if len(instanceSections) == 0 { 899 | return 900 | } 901 | 902 | printf("\033[1;36m[%s] 开始创建\033[0m\n", oracleSectionName) 903 | var SUM, NUM int32 = 0, 0 904 | sendMessage(fmt.Sprintf("[%s]", oracleSectionName), "开始创建") 905 | 906 | for _, instanceSec := range instanceSections { 907 | instance = Instance{} 908 | err := instanceSec.MapTo(&instance) 909 | if err != nil { 910 | printlnErr("解析实例模版参数失败", err.Error()) 911 | continue 912 | } 913 | 914 | sum, num := LaunchInstances(availabilityDomains) 915 | 916 | SUM = SUM + sum 917 | NUM = NUM + num 918 | 919 | } 920 | printf("\033[1;36m[%s] 结束创建。创建实例总数: %d, 成功 %d , 失败 %d\033[0m\n", oracleSectionName, SUM, NUM, SUM-NUM) 921 | text := fmt.Sprintf("结束创建。创建实例总数: %d, 成功 %d , 失败 %d", SUM, NUM, SUM-NUM) 922 | sendMessage(fmt.Sprintf("[%s]", oracleSectionName), text) 923 | } 924 | 925 | func multiBatchListInstancesIp() { 926 | IPsFilePath := IPsFilePrefix + "-" + time.Now().Format("2006-01-02-150405.txt") 927 | _, err := os.Stat(IPsFilePath) 928 | if err != nil && os.IsNotExist(err) { 929 | os.Create(IPsFilePath) 930 | } 931 | 932 | fmt.Printf("正在导出实例公共IP地址...\n") 933 | for _, sec := range oracleSections { 934 | err := initVar(sec) 935 | if err != nil { 936 | continue 937 | } 938 | ListInstancesIPs(IPsFilePath, sec.Name()) 939 | } 940 | fmt.Printf("导出实例公共IP地址完成,请查看文件 %s\n", IPsFilePath) 941 | } 942 | 943 | func batchListInstancesIp(filePath string, sec *ini.Section) { 944 | _, err := os.Stat(filePath) 945 | if err != nil && os.IsNotExist(err) { 946 | os.Create(filePath) 947 | } 948 | fmt.Printf("正在导出实例公共IP地址...\n") 949 | ListInstancesIPs(filePath, sec.Name()) 950 | fmt.Printf("导出实例IP地址完成,请查看文件 %s\n", filePath) 951 | } 952 | 953 | func ListInstancesIPs(filePath string, sectionName string) { 954 | var vnicAttachments []core.VnicAttachment 955 | var vas []core.VnicAttachment 956 | var nextPage *string 957 | var err error 958 | for { 959 | vas, nextPage, err = ListVnicAttachments(ctx, computeClient, nil, nextPage) 960 | if err == nil { 961 | vnicAttachments = append(vnicAttachments, vas...) 962 | } 963 | if nextPage == nil || len(vas) == 0 { 964 | break 965 | } 966 | } 967 | 968 | if err != nil { 969 | fmt.Printf("ListVnicAttachments Error: %s\n", err.Error()) 970 | return 971 | } 972 | file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend) 973 | if err != nil { 974 | fmt.Printf("打开文件失败, Error: %s\n", err.Error()) 975 | return 976 | } 977 | _, err = io.WriteString(file, "["+sectionName+"]\n") 978 | if err != nil { 979 | fmt.Printf("%s\n", err.Error()) 980 | } 981 | for _, vnicAttachment := range vnicAttachments { 982 | vnic, err := GetVnic(ctx, networkClient, vnicAttachment.VnicId) 983 | if err != nil { 984 | fmt.Printf("IP地址获取失败, %s\n", err.Error()) 985 | continue 986 | } 987 | fmt.Printf("[%s] 实例: %s, IP: %s\n", sectionName, *vnic.DisplayName, *vnic.PublicIp) 988 | _, err = io.WriteString(file, "实例: "+*vnic.DisplayName+", IP: "+*vnic.PublicIp+"\n") 989 | if err != nil { 990 | fmt.Printf("写入文件失败, Error: %s\n", err.Error()) 991 | } 992 | } 993 | _, err = io.WriteString(file, "\n") 994 | if err != nil { 995 | fmt.Printf("%s\n", err.Error()) 996 | } 997 | } 998 | 999 | // 返回值 sum: 创建实例总数; num: 创建成功的个数 1000 | func LaunchInstances(ads []identity.AvailabilityDomain) (sum, num int32) { 1001 | /* 创建实例的几种情况 1002 | * 1. 设置了 availabilityDomain 参数,即在设置的可用性域中创建 sum 个实例。 1003 | * 2. 没有设置 availabilityDomain 但是设置了 each 参数。即在获取的每个可用性域中创建 each 个实例,创建的实例总数 sum = each * adCount。 1004 | * 3. 没有设置 availabilityDomain 且没有设置 each 参数,即在获取到的可用性域中创建的实例总数为 sum。 1005 | */ 1006 | 1007 | //可用性域数量 1008 | var adCount int32 = int32(len(ads)) 1009 | adName := common.String(instance.AvailabilityDomain) 1010 | each := instance.Each 1011 | sum = instance.Sum 1012 | 1013 | // 没有设置可用性域并且没有设置each时,才有用。 1014 | var usableAds = make([]identity.AvailabilityDomain, 0) 1015 | 1016 | //可用性域不固定,即没有提供 availabilityDomain 参数 1017 | var AD_NOT_FIXED bool = false 1018 | var EACH_AD = false 1019 | if adName == nil || *adName == "" { 1020 | AD_NOT_FIXED = true 1021 | if each > 0 { 1022 | EACH_AD = true 1023 | sum = each * adCount 1024 | } else { 1025 | EACH_AD = false 1026 | usableAds = ads 1027 | } 1028 | } 1029 | 1030 | name := instance.InstanceDisplayName 1031 | if name == "" { 1032 | name = time.Now().Format("instance-20060102-1504") 1033 | } 1034 | displayName := common.String(name) 1035 | if sum > 1 { 1036 | displayName = common.String(name + "-1") 1037 | } 1038 | // create the launch instance request 1039 | request := core.LaunchInstanceRequest{} 1040 | request.CompartmentId = common.String(oracle.Tenancy) 1041 | request.DisplayName = displayName 1042 | 1043 | // Get a image. 1044 | fmt.Println("正在获取系统镜像...") 1045 | image, err := GetImage(ctx, computeClient) 1046 | if err != nil { 1047 | printlnErr("获取系统镜像失败", err.Error()) 1048 | return 1049 | } 1050 | fmt.Println("系统镜像:", *image.DisplayName) 1051 | 1052 | var shape core.Shape 1053 | if strings.Contains(strings.ToLower(instance.Shape), "flex") && instance.Ocpus > 0 && instance.MemoryInGBs > 0 { 1054 | shape.Shape = &instance.Shape 1055 | shape.Ocpus = &instance.Ocpus 1056 | shape.MemoryInGBs = &instance.MemoryInGBs 1057 | } else { 1058 | fmt.Println("正在获取Shape信息...") 1059 | shape, err = getShape(image.Id, instance.Shape) 1060 | if err != nil { 1061 | printlnErr("获取Shape信息失败", err.Error()) 1062 | return 1063 | } 1064 | } 1065 | 1066 | request.Shape = shape.Shape 1067 | if strings.Contains(strings.ToLower(*shape.Shape), "flex") { 1068 | request.ShapeConfig = &core.LaunchInstanceShapeConfigDetails{ 1069 | Ocpus: shape.Ocpus, 1070 | MemoryInGBs: shape.MemoryInGBs, 1071 | } 1072 | if instance.Burstable == "1/8" { 1073 | request.ShapeConfig.BaselineOcpuUtilization = core.LaunchInstanceShapeConfigDetailsBaselineOcpuUtilization8 1074 | } else if instance.Burstable == "1/2" { 1075 | request.ShapeConfig.BaselineOcpuUtilization = core.LaunchInstanceShapeConfigDetailsBaselineOcpuUtilization2 1076 | } 1077 | } 1078 | 1079 | // create a subnet or get the one already created 1080 | fmt.Println("正在获取子网...") 1081 | subnet, err := CreateOrGetNetworkInfrastructure(ctx, networkClient) 1082 | if err != nil { 1083 | printlnErr("获取子网失败", err.Error()) 1084 | return 1085 | } 1086 | fmt.Println("子网:", *subnet.DisplayName) 1087 | request.CreateVnicDetails = &core.CreateVnicDetails{SubnetId: subnet.Id} 1088 | 1089 | sd := core.InstanceSourceViaImageDetails{} 1090 | sd.ImageId = image.Id 1091 | if instance.BootVolumeSizeInGBs > 0 { 1092 | sd.BootVolumeSizeInGBs = common.Int64(instance.BootVolumeSizeInGBs) 1093 | } 1094 | request.SourceDetails = sd 1095 | request.IsPvEncryptionInTransitEnabled = common.Bool(true) 1096 | 1097 | metaData := map[string]string{} 1098 | metaData["ssh_authorized_keys"] = instance.SSH_Public_Key 1099 | if instance.CloudInit != "" { 1100 | metaData["user_data"] = instance.CloudInit 1101 | } 1102 | request.Metadata = metaData 1103 | 1104 | minTime := instance.MinTime 1105 | maxTime := instance.MaxTime 1106 | 1107 | SKIP_RETRY_MAP := make(map[int32]bool) 1108 | var usableAdsTemp = make([]identity.AvailabilityDomain, 0) 1109 | 1110 | retry := instance.Retry // 重试次数 1111 | var failTimes int32 = 0 // 失败次数 1112 | 1113 | // 记录尝试创建实例的次数 1114 | var runTimes int32 = 0 1115 | 1116 | var adIndex int32 = 0 // 当前可用性域下标 1117 | var pos int32 = 0 // for 循环次数 1118 | var SUCCESS = false // 创建是否成功 1119 | 1120 | var startTime = time.Now() 1121 | 1122 | var bootVolumeSize float64 1123 | if instance.BootVolumeSizeInGBs > 0 { 1124 | bootVolumeSize = float64(instance.BootVolumeSizeInGBs) 1125 | } else { 1126 | bootVolumeSize = math.Round(float64(*image.SizeInMBs) / float64(1024)) 1127 | } 1128 | printf("\033[1;36m[%s] 开始创建 %s 实例, OCPU: %g 内存: %g 引导卷: %g \033[0m\n", oracleSectionName, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize) 1129 | if EACH { 1130 | text := fmt.Sprintf("正在尝试创建第 %d 个实例...⏳\n区域: %s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d", pos+1, oracle.Region, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum) 1131 | _, err := sendMessage("", text) 1132 | if err != nil { 1133 | printlnErr("Telegram 消息提醒发送失败", err.Error()) 1134 | } 1135 | } 1136 | 1137 | for pos < sum { 1138 | 1139 | if AD_NOT_FIXED { 1140 | if EACH_AD { 1141 | if pos%each == 0 && failTimes == 0 { 1142 | adName = ads[adIndex].Name 1143 | adIndex++ 1144 | } 1145 | } else { 1146 | if SUCCESS { 1147 | adIndex = 0 1148 | } 1149 | if adIndex >= adCount { 1150 | adIndex = 0 1151 | } 1152 | //adName = ads[adIndex].Name 1153 | adName = usableAds[adIndex].Name 1154 | adIndex++ 1155 | } 1156 | } 1157 | 1158 | runTimes++ 1159 | printf("\033[1;36m[%s] 正在尝试创建第 %d 个实例, AD: %s\033[0m\n", oracleSectionName, pos+1, *adName) 1160 | printf("\033[1;36m[%s] 当前尝试次数: %d \033[0m\n", oracleSectionName, runTimes) 1161 | request.AvailabilityDomain = adName 1162 | createResp, err := computeClient.LaunchInstance(ctx, request) 1163 | 1164 | if err == nil { 1165 | // 创建实例成功 1166 | SUCCESS = true 1167 | num++ //成功个数+1 1168 | 1169 | duration := fmtDuration(time.Since(startTime)) 1170 | 1171 | printf("\033[1;32m[%s] 第 %d 个实例抢到了🎉, 正在启动中请稍等...⌛️ \033[0m\n", oracleSectionName, pos+1) 1172 | var msg Message 1173 | var msgErr error 1174 | var text string 1175 | if EACH { 1176 | text = fmt.Sprintf("第 %d 个实例抢到了🎉, 正在启动中请稍等...⌛️\n区域: %s\n实例名称: %s\n公共IP: 获取中...⏳\n可用性域:%s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d\n尝试次数: %d\n耗时: %s", pos+1, oracle.Region, *createResp.Instance.DisplayName, *createResp.Instance.AvailabilityDomain, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum, runTimes, duration) 1177 | msg, msgErr = sendMessage("", text) 1178 | } 1179 | // 获取实例公共IP 1180 | var strIps string 1181 | ips, err := getInstancePublicIps(createResp.Instance.Id) 1182 | if err != nil { 1183 | printf("\033[1;32m[%s] 第 %d 个实例抢到了🎉, 但是启动失败❌ 错误信息: \033[0m%s\n", oracleSectionName, pos+1, err.Error()) 1184 | text = fmt.Sprintf("第 %d 个实例抢到了🎉, 但是启动失败❌实例已被终止😔\n区域: %s\n实例名称: %s\n可用性域:%s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d\n尝试次数: %d\n耗时: %s", pos+1, oracle.Region, *createResp.Instance.DisplayName, *createResp.Instance.AvailabilityDomain, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum, runTimes, duration) 1185 | } else { 1186 | strIps = strings.Join(ips, ",") 1187 | printf("\033[1;32m[%s] 第 %d 个实例抢到了🎉, 启动成功✅. 实例名称: %s, 公共IP: %s\033[0m\n", oracleSectionName, pos+1, *createResp.Instance.DisplayName, strIps) 1188 | text = fmt.Sprintf("第 %d 个实例抢到了🎉, 启动成功✅\n区域: %s\n实例名称: %s\n公共IP: %s\n可用性域:%s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d\n尝试次数: %d\n耗时: %s", pos+1, oracle.Region, *createResp.Instance.DisplayName, strIps, *createResp.Instance.AvailabilityDomain, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum, runTimes, duration) 1189 | } 1190 | if EACH { 1191 | if msgErr != nil { 1192 | sendMessage("", text) 1193 | } else { 1194 | editMessage(msg.MessageId, "", text) 1195 | } 1196 | } 1197 | 1198 | sleepRandomSecond(minTime, maxTime) 1199 | 1200 | displayName = common.String(fmt.Sprintf("%s-%d", name, pos+1)) 1201 | request.DisplayName = displayName 1202 | 1203 | } else { 1204 | // 创建实例失败 1205 | SUCCESS = false 1206 | // 错误信息 1207 | errInfo := err.Error() 1208 | // 是否跳过重试 1209 | SKIP_RETRY := false 1210 | 1211 | //isRetryable := common.IsErrorRetryableByDefault(err) 1212 | //isNetErr := common.IsNetworkError(err) 1213 | servErr, isServErr := common.IsServiceError(err) 1214 | 1215 | // API Errors: https://docs.cloud.oracle.com/Content/API/References/apierrors.htm 1216 | 1217 | if isServErr && (400 <= servErr.GetHTTPStatusCode() && servErr.GetHTTPStatusCode() <= 405) || 1218 | (servErr.GetHTTPStatusCode() == 409 && !strings.EqualFold(servErr.GetCode(), "IncorrectState")) || 1219 | servErr.GetHTTPStatusCode() == 412 || servErr.GetHTTPStatusCode() == 413 || servErr.GetHTTPStatusCode() == 422 || 1220 | servErr.GetHTTPStatusCode() == 431 || servErr.GetHTTPStatusCode() == 501 { 1221 | // 不可重试 1222 | if isServErr { 1223 | errInfo = servErr.GetMessage() 1224 | } 1225 | duration := fmtDuration(time.Since(startTime)) 1226 | printf("\033[1;31m[%s] 第 %d 个实例创建失败了❌, 错误信息: \033[0m%s\n", oracleSectionName, pos+1, errInfo) 1227 | if EACH { 1228 | text := fmt.Sprintf("第 %d 个实例创建失败了❌\n错误信息: %s\n区域: %s\n可用性域: %s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d\n尝试次数: %d\n耗时:%s", pos+1, errInfo, oracle.Region, *adName, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum, runTimes, duration) 1229 | sendMessage("", text) 1230 | } 1231 | 1232 | SKIP_RETRY = true 1233 | if AD_NOT_FIXED && !EACH_AD { 1234 | SKIP_RETRY_MAP[adIndex-1] = true 1235 | } 1236 | 1237 | } else { 1238 | // 可重试 1239 | if isServErr { 1240 | errInfo = servErr.GetMessage() 1241 | } 1242 | printf("\033[1;31m[%s] 创建失败, Error: \033[0m%s\n", oracleSectionName, errInfo) 1243 | 1244 | SKIP_RETRY = false 1245 | if AD_NOT_FIXED && !EACH_AD { 1246 | SKIP_RETRY_MAP[adIndex-1] = false 1247 | } 1248 | } 1249 | 1250 | sleepRandomSecond(minTime, maxTime) 1251 | 1252 | if AD_NOT_FIXED { 1253 | if !EACH_AD { 1254 | if adIndex < adCount { 1255 | // 没有设置可用性域,且没有设置each。即在获取到的每个可用性域里尝试创建。当前使用的可用性域不是最后一个,继续尝试。 1256 | continue 1257 | } else { 1258 | // 当前使用的可用性域是最后一个,判断失败次数是否达到重试次数,未达到重试次数继续尝试。 1259 | failTimes++ 1260 | 1261 | for index, skip := range SKIP_RETRY_MAP { 1262 | if !skip { 1263 | usableAdsTemp = append(usableAdsTemp, usableAds[index]) 1264 | } 1265 | } 1266 | 1267 | // 重新设置 usableAds 1268 | usableAds = usableAdsTemp 1269 | adCount = int32(len(usableAds)) 1270 | 1271 | // 重置变量 1272 | usableAdsTemp = nil 1273 | for k := range SKIP_RETRY_MAP { 1274 | delete(SKIP_RETRY_MAP, k) 1275 | } 1276 | 1277 | // 判断是否需要重试 1278 | if (retry < 0 || failTimes <= retry) && adCount > 0 { 1279 | continue 1280 | } 1281 | } 1282 | 1283 | adIndex = 0 1284 | 1285 | } else { 1286 | // 没有设置可用性域,且设置了each,即在每个域创建each个实例。判断失败次数继续尝试。 1287 | failTimes++ 1288 | if (retry < 0 || failTimes <= retry) && !SKIP_RETRY { 1289 | continue 1290 | } 1291 | } 1292 | 1293 | } else { 1294 | //设置了可用性域,判断是否需要重试 1295 | failTimes++ 1296 | if (retry < 0 || failTimes <= retry) && !SKIP_RETRY { 1297 | continue 1298 | } 1299 | } 1300 | 1301 | } 1302 | 1303 | // 重置变量 1304 | usableAds = ads 1305 | adCount = int32(len(usableAds)) 1306 | usableAdsTemp = nil 1307 | for k := range SKIP_RETRY_MAP { 1308 | delete(SKIP_RETRY_MAP, k) 1309 | } 1310 | 1311 | // 成功或者失败次数达到重试次数,重置失败次数为0 1312 | failTimes = 0 1313 | 1314 | // 重置尝试创建实例次数 1315 | runTimes = 0 1316 | startTime = time.Now() 1317 | 1318 | // for 循环次数+1 1319 | pos++ 1320 | 1321 | if pos < sum && EACH { 1322 | text := fmt.Sprintf("正在尝试创建第 %d 个实例...⏳\n区域: %s\n实例配置: %s\nOCPU计数: %g\n内存(GB): %g\n引导卷(GB): %g\n创建个数: %d", pos+1, oracle.Region, *shape.Shape, *shape.Ocpus, *shape.MemoryInGBs, bootVolumeSize, sum) 1323 | sendMessage("", text) 1324 | } 1325 | } 1326 | return 1327 | } 1328 | 1329 | func sleepRandomSecond(min, max int32) { 1330 | var second int32 1331 | if min <= 0 || max <= 0 { 1332 | second = 1 1333 | } else if min >= max { 1334 | second = max 1335 | } else { 1336 | second = rand.Int31n(max-min) + min 1337 | } 1338 | printf("Sleep %d Second...\n", second) 1339 | time.Sleep(time.Duration(second) * time.Second) 1340 | } 1341 | 1342 | // ExampleLaunchInstance does create an instance 1343 | // NOTE: launch instance will create a new instance and VCN. please make sure delete the instance 1344 | // after execute this sample code, otherwise, you will be charged for the running instance 1345 | func ExampleLaunchInstance() { 1346 | c, err := core.NewComputeClientWithConfigurationProvider(provider) 1347 | helpers.FatalIfError(err) 1348 | networkClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(provider) 1349 | helpers.FatalIfError(err) 1350 | ctx := context.Background() 1351 | 1352 | // create the launch instance request 1353 | request := core.LaunchInstanceRequest{} 1354 | request.CompartmentId = common.String(oracle.Tenancy) 1355 | request.DisplayName = common.String(instance.InstanceDisplayName) 1356 | request.AvailabilityDomain = common.String(instance.AvailabilityDomain) 1357 | 1358 | // create a subnet or get the one already created 1359 | subnet, err := CreateOrGetNetworkInfrastructure(ctx, networkClient) 1360 | helpers.FatalIfError(err) 1361 | fmt.Println("subnet created") 1362 | request.CreateVnicDetails = &core.CreateVnicDetails{SubnetId: subnet.Id} 1363 | 1364 | // get a image 1365 | images, err := listImages(ctx, c) 1366 | helpers.FatalIfError(err) 1367 | image := images[0] 1368 | fmt.Println("list images") 1369 | request.SourceDetails = core.InstanceSourceViaImageDetails{ 1370 | ImageId: image.Id, 1371 | BootVolumeSizeInGBs: common.Int64(instance.BootVolumeSizeInGBs), 1372 | } 1373 | 1374 | // use [config.Shape] to create instance 1375 | request.Shape = common.String(instance.Shape) 1376 | 1377 | request.ShapeConfig = &core.LaunchInstanceShapeConfigDetails{ 1378 | Ocpus: common.Float32(instance.Ocpus), 1379 | MemoryInGBs: common.Float32(instance.MemoryInGBs), 1380 | } 1381 | 1382 | // add ssh_authorized_keys 1383 | //metaData := map[string]string{ 1384 | // "ssh_authorized_keys": config.SSH_Public_Key, 1385 | //} 1386 | //request.Metadata = metaData 1387 | request.Metadata = map[string]string{"ssh_authorized_keys": instance.SSH_Public_Key} 1388 | 1389 | // default retry policy will retry on non-200 response 1390 | request.RequestMetadata = helpers.GetRequestMetadataWithDefaultRetryPolicy() 1391 | 1392 | createResp, err := c.LaunchInstance(ctx, request) 1393 | helpers.FatalIfError(err) 1394 | 1395 | fmt.Println("launching instance") 1396 | 1397 | // should retry condition check which returns a bool value indicating whether to do retry or not 1398 | // it checks the lifecycle status equals to Running or not for this case 1399 | shouldRetryFunc := func(r common.OCIOperationResponse) bool { 1400 | if converted, ok := r.Response.(core.GetInstanceResponse); ok { 1401 | return converted.LifecycleState != core.InstanceLifecycleStateRunning 1402 | } 1403 | return true 1404 | } 1405 | 1406 | // create get instance request with a retry policy which takes a function 1407 | // to determine shouldRetry or not 1408 | pollingGetRequest := core.GetInstanceRequest{ 1409 | InstanceId: createResp.Instance.Id, 1410 | RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc), 1411 | } 1412 | 1413 | instance, pollError := c.GetInstance(ctx, pollingGetRequest) 1414 | helpers.FatalIfError(pollError) 1415 | 1416 | fmt.Println("instance launched") 1417 | 1418 | // 创建辅助 VNIC 并将其附加到指定的实例 1419 | attachVnicResponse, err := c.AttachVnic(context.Background(), core.AttachVnicRequest{ 1420 | AttachVnicDetails: core.AttachVnicDetails{ 1421 | CreateVnicDetails: &core.CreateVnicDetails{ 1422 | SubnetId: subnet.Id, 1423 | AssignPublicIp: common.Bool(true), 1424 | }, 1425 | InstanceId: instance.Id, 1426 | }, 1427 | }) 1428 | 1429 | helpers.FatalIfError(err) 1430 | fmt.Println("vnic attached") 1431 | 1432 | vnicState := attachVnicResponse.VnicAttachment.LifecycleState 1433 | for vnicState != core.VnicAttachmentLifecycleStateAttached { 1434 | time.Sleep(15 * time.Second) 1435 | getVnicAttachmentRequest, err := c.GetVnicAttachment(context.Background(), core.GetVnicAttachmentRequest{ 1436 | VnicAttachmentId: attachVnicResponse.Id, 1437 | }) 1438 | helpers.FatalIfError(err) 1439 | vnicState = getVnicAttachmentRequest.VnicAttachment.LifecycleState 1440 | } 1441 | 1442 | // 分离并删除指定的辅助 VNIC 1443 | _, err = c.DetachVnic(context.Background(), core.DetachVnicRequest{ 1444 | VnicAttachmentId: attachVnicResponse.Id, 1445 | }) 1446 | 1447 | helpers.FatalIfError(err) 1448 | fmt.Println("vnic dettached") 1449 | 1450 | defer func() { 1451 | terminateInstance(createResp.Id) 1452 | 1453 | client, clerr := core.NewVirtualNetworkClientWithConfigurationProvider(common.DefaultConfigProvider()) 1454 | helpers.FatalIfError(clerr) 1455 | 1456 | vcnID := subnet.VcnId 1457 | deleteSubnet(ctx, client, subnet.Id) 1458 | deleteVcn(ctx, client, vcnID) 1459 | }() 1460 | 1461 | // Output: 1462 | // subnet created 1463 | // list images 1464 | // list shapes 1465 | // launching instance 1466 | // instance launched 1467 | // vnic attached 1468 | // vnic dettached 1469 | // terminating instance 1470 | // instance terminated 1471 | // deleteing subnet 1472 | // subnet deleted 1473 | // deleteing VCN 1474 | // VCN deleted 1475 | } 1476 | 1477 | func getProvider(oracle Oracle) (common.ConfigurationProvider, error) { 1478 | content, err := ioutil.ReadFile(oracle.Key_file) 1479 | if err != nil { 1480 | return nil, err 1481 | } 1482 | privateKey := string(content) 1483 | privateKeyPassphrase := common.String(oracle.Key_password) 1484 | return common.NewRawConfigurationProvider(oracle.Tenancy, oracle.User, oracle.Region, oracle.Fingerprint, privateKey, privateKeyPassphrase), nil 1485 | } 1486 | 1487 | // 创建或获取基础网络设施 1488 | func CreateOrGetNetworkInfrastructure(ctx context.Context, c core.VirtualNetworkClient) (subnet core.Subnet, err error) { 1489 | var vcn core.Vcn 1490 | vcn, err = createOrGetVcn(ctx, c) 1491 | if err != nil { 1492 | return 1493 | } 1494 | var gateway core.InternetGateway 1495 | gateway, err = createOrGetInternetGateway(c, vcn.Id) 1496 | if err != nil { 1497 | return 1498 | } 1499 | _, err = createOrGetRouteTable(c, gateway.Id, vcn.Id) 1500 | if err != nil { 1501 | return 1502 | } 1503 | subnet, err = createOrGetSubnetWithDetails( 1504 | ctx, c, vcn.Id, 1505 | common.String(instance.SubnetDisplayName), 1506 | common.String("10.0.0.0/20"), 1507 | common.String("subnetdns"), 1508 | common.String(instance.AvailabilityDomain)) 1509 | return 1510 | } 1511 | 1512 | // CreateOrGetSubnetWithDetails either creates a new Virtual Cloud Network (VCN) or get the one already exist 1513 | // with detail info 1514 | func createOrGetSubnetWithDetails(ctx context.Context, c core.VirtualNetworkClient, vcnID *string, 1515 | displayName *string, cidrBlock *string, dnsLabel *string, availableDomain *string) (subnet core.Subnet, err error) { 1516 | var subnets []core.Subnet 1517 | subnets, err = listSubnets(ctx, c, vcnID) 1518 | if err != nil { 1519 | return 1520 | } 1521 | 1522 | if displayName == nil { 1523 | displayName = common.String(instance.SubnetDisplayName) 1524 | } 1525 | 1526 | if len(subnets) > 0 && *displayName == "" { 1527 | subnet = subnets[0] 1528 | return 1529 | } 1530 | 1531 | // check if the subnet has already been created 1532 | for _, element := range subnets { 1533 | if *element.DisplayName == *displayName { 1534 | // find the subnet, return it 1535 | subnet = element 1536 | return 1537 | } 1538 | } 1539 | 1540 | // create a new subnet 1541 | fmt.Printf("开始创建Subnet(没有可用的Subnet,或指定的Subnet不存在)\n") 1542 | // 子网名称为空,以当前时间为名称创建子网 1543 | if *displayName == "" { 1544 | displayName = common.String(time.Now().Format("subnet-20060102-1504")) 1545 | } 1546 | request := core.CreateSubnetRequest{} 1547 | //request.AvailabilityDomain = availableDomain //省略此属性创建区域性子网(regional subnet),提供此属性创建特定于可用性域的子网。建议创建区域性子网。 1548 | request.CompartmentId = &oracle.Tenancy 1549 | request.CidrBlock = cidrBlock 1550 | request.DisplayName = displayName 1551 | request.DnsLabel = dnsLabel 1552 | request.RequestMetadata = getCustomRequestMetadataWithRetryPolicy() 1553 | 1554 | request.VcnId = vcnID 1555 | var r core.CreateSubnetResponse 1556 | r, err = c.CreateSubnet(ctx, request) 1557 | if err != nil { 1558 | return 1559 | } 1560 | // retry condition check, stop unitl return true 1561 | pollUntilAvailable := func(r common.OCIOperationResponse) bool { 1562 | if converted, ok := r.Response.(core.GetSubnetResponse); ok { 1563 | return converted.LifecycleState != core.SubnetLifecycleStateAvailable 1564 | } 1565 | return true 1566 | } 1567 | 1568 | pollGetRequest := core.GetSubnetRequest{ 1569 | SubnetId: r.Id, 1570 | RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(pollUntilAvailable), 1571 | } 1572 | 1573 | // wait for lifecyle become running 1574 | _, err = c.GetSubnet(ctx, pollGetRequest) 1575 | if err != nil { 1576 | return 1577 | } 1578 | 1579 | // update the security rules 1580 | getReq := core.GetSecurityListRequest{ 1581 | SecurityListId: common.String(r.SecurityListIds[0]), 1582 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1583 | } 1584 | 1585 | var getResp core.GetSecurityListResponse 1586 | getResp, err = c.GetSecurityList(ctx, getReq) 1587 | if err != nil { 1588 | return 1589 | } 1590 | 1591 | // this security rule allows remote control the instance 1592 | /*portRange := core.PortRange{ 1593 | Max: common.Int(1521), 1594 | Min: common.Int(1521), 1595 | }*/ 1596 | 1597 | newRules := append(getResp.IngressSecurityRules, core.IngressSecurityRule{ 1598 | //Protocol: common.String("6"), // TCP 1599 | Protocol: common.String("all"), // 允许所有协议 1600 | Source: common.String("0.0.0.0/0"), 1601 | /*TcpOptions: &core.TcpOptions{ 1602 | DestinationPortRange: &portRange, // 省略该参数,允许所有目标端口。 1603 | },*/ 1604 | }) 1605 | 1606 | updateReq := core.UpdateSecurityListRequest{ 1607 | SecurityListId: common.String(r.SecurityListIds[0]), 1608 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1609 | } 1610 | 1611 | updateReq.IngressSecurityRules = newRules 1612 | 1613 | _, err = c.UpdateSecurityList(ctx, updateReq) 1614 | if err != nil { 1615 | return 1616 | } 1617 | fmt.Printf("Subnet创建成功: %s\n", *r.Subnet.DisplayName) 1618 | subnet = r.Subnet 1619 | return 1620 | } 1621 | 1622 | // 列出指定虚拟云网络 (VCN) 中的所有子网 1623 | func listSubnets(ctx context.Context, c core.VirtualNetworkClient, vcnID *string) (subnets []core.Subnet, err error) { 1624 | request := core.ListSubnetsRequest{ 1625 | CompartmentId: &oracle.Tenancy, 1626 | VcnId: vcnID, 1627 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1628 | } 1629 | var r core.ListSubnetsResponse 1630 | r, err = c.ListSubnets(ctx, request) 1631 | if err != nil { 1632 | return 1633 | } 1634 | subnets = r.Items 1635 | return 1636 | } 1637 | 1638 | // 创建一个新的虚拟云网络 (VCN) 或获取已经存在的虚拟云网络 1639 | func createOrGetVcn(ctx context.Context, c core.VirtualNetworkClient) (core.Vcn, error) { 1640 | var vcn core.Vcn 1641 | vcnItems, err := listVcns(ctx, c) 1642 | if err != nil { 1643 | return vcn, err 1644 | } 1645 | displayName := common.String(instance.VcnDisplayName) 1646 | if len(vcnItems) > 0 && *displayName == "" { 1647 | vcn = vcnItems[0] 1648 | return vcn, err 1649 | } 1650 | for _, element := range vcnItems { 1651 | if *element.DisplayName == instance.VcnDisplayName { 1652 | // VCN already created, return it 1653 | vcn = element 1654 | return vcn, err 1655 | } 1656 | } 1657 | // create a new VCN 1658 | fmt.Println("开始创建VCN(没有可用的VCN,或指定的VCN不存在)") 1659 | if *displayName == "" { 1660 | displayName = common.String(time.Now().Format("vcn-20060102-1504")) 1661 | } 1662 | request := core.CreateVcnRequest{} 1663 | request.RequestMetadata = getCustomRequestMetadataWithRetryPolicy() 1664 | request.CidrBlock = common.String("10.0.0.0/16") 1665 | request.CompartmentId = common.String(oracle.Tenancy) 1666 | request.DisplayName = displayName 1667 | request.DnsLabel = common.String("vcndns") 1668 | r, err := c.CreateVcn(ctx, request) 1669 | if err != nil { 1670 | return vcn, err 1671 | } 1672 | fmt.Printf("VCN创建成功: %s\n", *r.Vcn.DisplayName) 1673 | vcn = r.Vcn 1674 | return vcn, err 1675 | } 1676 | 1677 | // 列出所有虚拟云网络 (VCN) 1678 | func listVcns(ctx context.Context, c core.VirtualNetworkClient) ([]core.Vcn, error) { 1679 | request := core.ListVcnsRequest{ 1680 | CompartmentId: &oracle.Tenancy, 1681 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1682 | } 1683 | r, err := c.ListVcns(ctx, request) 1684 | if err != nil { 1685 | return nil, err 1686 | } 1687 | return r.Items, err 1688 | } 1689 | 1690 | // 创建或者获取 Internet 网关 1691 | func createOrGetInternetGateway(c core.VirtualNetworkClient, vcnID *string) (core.InternetGateway, error) { 1692 | //List Gateways 1693 | var gateway core.InternetGateway 1694 | listGWRequest := core.ListInternetGatewaysRequest{ 1695 | CompartmentId: &oracle.Tenancy, 1696 | VcnId: vcnID, 1697 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1698 | } 1699 | 1700 | listGWRespone, err := c.ListInternetGateways(ctx, listGWRequest) 1701 | if err != nil { 1702 | fmt.Printf("Internet gateway list error: %s\n", err.Error()) 1703 | return gateway, err 1704 | } 1705 | 1706 | if len(listGWRespone.Items) >= 1 { 1707 | //Gateway with name already exists 1708 | gateway = listGWRespone.Items[0] 1709 | } else { 1710 | //Create new Gateway 1711 | fmt.Printf("开始创建Internet网关\n") 1712 | enabled := true 1713 | createGWDetails := core.CreateInternetGatewayDetails{ 1714 | CompartmentId: &oracle.Tenancy, 1715 | IsEnabled: &enabled, 1716 | VcnId: vcnID, 1717 | } 1718 | 1719 | createGWRequest := core.CreateInternetGatewayRequest{ 1720 | CreateInternetGatewayDetails: createGWDetails, 1721 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy()} 1722 | 1723 | createGWResponse, err := c.CreateInternetGateway(ctx, createGWRequest) 1724 | 1725 | if err != nil { 1726 | fmt.Printf("Internet gateway create error: %s\n", err.Error()) 1727 | return gateway, err 1728 | } 1729 | gateway = createGWResponse.InternetGateway 1730 | fmt.Printf("Internet网关创建成功: %s\n", *gateway.DisplayName) 1731 | } 1732 | return gateway, err 1733 | } 1734 | 1735 | // 创建或者获取路由表 1736 | func createOrGetRouteTable(c core.VirtualNetworkClient, gatewayID, VcnID *string) (routeTable core.RouteTable, err error) { 1737 | //List Route Table 1738 | listRTRequest := core.ListRouteTablesRequest{ 1739 | CompartmentId: &oracle.Tenancy, 1740 | VcnId: VcnID, 1741 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1742 | } 1743 | var listRTResponse core.ListRouteTablesResponse 1744 | listRTResponse, err = c.ListRouteTables(ctx, listRTRequest) 1745 | if err != nil { 1746 | fmt.Printf("Route table list error: %s\n", err.Error()) 1747 | return 1748 | } 1749 | 1750 | cidrRange := "0.0.0.0/0" 1751 | rr := core.RouteRule{ 1752 | NetworkEntityId: gatewayID, 1753 | Destination: &cidrRange, 1754 | DestinationType: core.RouteRuleDestinationTypeCidrBlock, 1755 | } 1756 | 1757 | if len(listRTResponse.Items) >= 1 { 1758 | //Default Route Table found and has at least 1 route rule 1759 | if len(listRTResponse.Items[0].RouteRules) >= 1 { 1760 | routeTable = listRTResponse.Items[0] 1761 | //Default Route table needs route rule adding 1762 | } else { 1763 | fmt.Printf("路由表未添加规则,开始添加Internet路由规则\n") 1764 | updateRTDetails := core.UpdateRouteTableDetails{ 1765 | RouteRules: []core.RouteRule{rr}, 1766 | } 1767 | 1768 | updateRTRequest := core.UpdateRouteTableRequest{ 1769 | RtId: listRTResponse.Items[0].Id, 1770 | UpdateRouteTableDetails: updateRTDetails, 1771 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1772 | } 1773 | var updateRTResponse core.UpdateRouteTableResponse 1774 | updateRTResponse, err = c.UpdateRouteTable(ctx, updateRTRequest) 1775 | if err != nil { 1776 | fmt.Printf("Error updating route table: %s\n", err) 1777 | return 1778 | } 1779 | fmt.Printf("Internet路由规则添加成功\n") 1780 | routeTable = updateRTResponse.RouteTable 1781 | } 1782 | 1783 | } else { 1784 | //No default route table found 1785 | fmt.Printf("Error could not find VCN default route table, VCN OCID: %s Could not find route table.\n", *VcnID) 1786 | } 1787 | return 1788 | } 1789 | 1790 | // 获取符合条件系统镜像中的第一个 1791 | func GetImage(ctx context.Context, c core.ComputeClient) (image core.Image, err error) { 1792 | var images []core.Image 1793 | images, err = listImages(ctx, c) 1794 | if err != nil { 1795 | return 1796 | } 1797 | if len(images) > 0 { 1798 | image = images[0] 1799 | } else { 1800 | err = fmt.Errorf("未找到[%s %s]的镜像, 或该镜像不支持[%s]", instance.OperatingSystem, instance.OperatingSystemVersion, instance.Shape) 1801 | } 1802 | return 1803 | } 1804 | 1805 | // 列出所有符合条件的系统镜像 1806 | func listImages(ctx context.Context, c core.ComputeClient) ([]core.Image, error) { 1807 | if instance.OperatingSystem == "" || instance.OperatingSystemVersion == "" { 1808 | return nil, errors.New("操作系统类型和版本不能为空, 请检查配置文件") 1809 | } 1810 | request := core.ListImagesRequest{ 1811 | CompartmentId: common.String(oracle.Tenancy), 1812 | OperatingSystem: common.String(instance.OperatingSystem), 1813 | OperatingSystemVersion: common.String(instance.OperatingSystemVersion), 1814 | Shape: common.String(instance.Shape), 1815 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1816 | } 1817 | r, err := c.ListImages(ctx, request) 1818 | return r.Items, err 1819 | } 1820 | 1821 | func getShape(imageId *string, shapeName string) (core.Shape, error) { 1822 | var shape core.Shape 1823 | shapes, err := listShapes(ctx, computeClient, imageId) 1824 | if err != nil { 1825 | return shape, err 1826 | } 1827 | for _, s := range shapes { 1828 | if strings.EqualFold(*s.Shape, shapeName) { 1829 | shape = s 1830 | return shape, nil 1831 | } 1832 | } 1833 | err = errors.New("没有符合条件的Shape") 1834 | return shape, err 1835 | } 1836 | 1837 | // ListShapes Lists the shapes that can be used to launch an instance within the specified compartment. 1838 | func listShapes(ctx context.Context, c core.ComputeClient, imageID *string) ([]core.Shape, error) { 1839 | request := core.ListShapesRequest{ 1840 | CompartmentId: common.String(oracle.Tenancy), 1841 | ImageId: imageID, 1842 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1843 | } 1844 | r, err := c.ListShapes(ctx, request) 1845 | if err == nil && (r.Items == nil || len(r.Items) == 0) { 1846 | err = errors.New("没有符合条件的Shape") 1847 | } 1848 | return r.Items, err 1849 | } 1850 | 1851 | // 列出符合条件的可用性域 1852 | func ListAvailabilityDomains() ([]identity.AvailabilityDomain, error) { 1853 | req := identity.ListAvailabilityDomainsRequest{ 1854 | CompartmentId: common.String(oracle.Tenancy), 1855 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1856 | } 1857 | resp, err := identityClient.ListAvailabilityDomains(ctx, req) 1858 | return resp.Items, err 1859 | } 1860 | 1861 | func getUsers() { 1862 | req := identity.ListUsersRequest{ 1863 | CompartmentId: &oracle.Tenancy, 1864 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1865 | } 1866 | resp, _ := identityClient.ListUsers(ctx, req) 1867 | for _, user := range resp.Items { 1868 | var userName string 1869 | if user.Name != nil { 1870 | userName = *user.Name 1871 | } 1872 | var email string 1873 | if user.Email != nil { 1874 | email = *user.Email 1875 | } 1876 | fmt.Println("用户名:", userName, "邮箱:", email) 1877 | } 1878 | 1879 | } 1880 | 1881 | func ListInstances(ctx context.Context, c core.ComputeClient, page *string) ([]core.Instance, *string, error) { 1882 | req := core.ListInstancesRequest{ 1883 | CompartmentId: common.String(oracle.Tenancy), 1884 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1885 | Limit: common.Int(100), 1886 | Page: page, 1887 | } 1888 | resp, err := c.ListInstances(ctx, req) 1889 | return resp.Items, resp.OpcNextPage, err 1890 | } 1891 | 1892 | func ListVnicAttachments(ctx context.Context, c core.ComputeClient, instanceId *string, page *string) ([]core.VnicAttachment, *string, error) { 1893 | req := core.ListVnicAttachmentsRequest{ 1894 | CompartmentId: common.String(oracle.Tenancy), 1895 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1896 | Limit: common.Int(100), 1897 | Page: page, 1898 | } 1899 | if instanceId != nil && *instanceId != "" { 1900 | req.InstanceId = instanceId 1901 | } 1902 | resp, err := c.ListVnicAttachments(ctx, req) 1903 | return resp.Items, resp.OpcNextPage, err 1904 | } 1905 | 1906 | func GetVnic(ctx context.Context, c core.VirtualNetworkClient, vnicID *string) (core.Vnic, error) { 1907 | req := core.GetVnicRequest{ 1908 | VnicId: vnicID, 1909 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1910 | } 1911 | resp, err := c.GetVnic(ctx, req) 1912 | if err != nil && resp.RawResponse != nil { 1913 | err = errors.New(resp.RawResponse.Status) 1914 | } 1915 | return resp.Vnic, err 1916 | } 1917 | 1918 | // 终止实例 1919 | // https://docs.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Instance/TerminateInstance 1920 | func terminateInstance(id *string) error { 1921 | request := core.TerminateInstanceRequest{ 1922 | InstanceId: id, 1923 | PreserveBootVolume: common.Bool(false), 1924 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 1925 | } 1926 | _, err := computeClient.TerminateInstance(ctx, request) 1927 | return err 1928 | 1929 | //fmt.Println("terminating instance") 1930 | 1931 | /* 1932 | // should retry condition check which returns a bool value indicating whether to do retry or not 1933 | // it checks the lifecycle status equals to Terminated or not for this case 1934 | shouldRetryFunc := func(r common.OCIOperationResponse) bool { 1935 | if converted, ok := r.Response.(core.GetInstanceResponse); ok { 1936 | return converted.LifecycleState != core.InstanceLifecycleStateTerminated 1937 | } 1938 | return true 1939 | } 1940 | 1941 | pollGetRequest := core.GetInstanceRequest{ 1942 | InstanceId: id, 1943 | RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc), 1944 | } 1945 | 1946 | _, pollErr := c.GetInstance(ctx, pollGetRequest) 1947 | helpers.FatalIfError(pollErr) 1948 | fmt.Println("instance terminated") 1949 | */ 1950 | } 1951 | 1952 | // 删除虚拟云网络 1953 | func deleteVcn(ctx context.Context, c core.VirtualNetworkClient, id *string) { 1954 | request := core.DeleteVcnRequest{ 1955 | VcnId: id, 1956 | RequestMetadata: helpers.GetRequestMetadataWithDefaultRetryPolicy(), 1957 | } 1958 | 1959 | fmt.Println("deleteing VCN") 1960 | _, err := c.DeleteVcn(ctx, request) 1961 | helpers.FatalIfError(err) 1962 | 1963 | // should retry condition check which returns a bool value indicating whether to do retry or not 1964 | // it checks the lifecycle status equals to Terminated or not for this case 1965 | shouldRetryFunc := func(r common.OCIOperationResponse) bool { 1966 | if serviceError, ok := common.IsServiceError(r.Error); ok && serviceError.GetHTTPStatusCode() == 404 { 1967 | // resource been deleted, stop retry 1968 | return false 1969 | } 1970 | 1971 | if converted, ok := r.Response.(core.GetVcnResponse); ok { 1972 | return converted.LifecycleState != core.VcnLifecycleStateTerminated 1973 | } 1974 | return true 1975 | } 1976 | 1977 | pollGetRequest := core.GetVcnRequest{ 1978 | VcnId: id, 1979 | RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc), 1980 | } 1981 | 1982 | _, pollErr := c.GetVcn(ctx, pollGetRequest) 1983 | if serviceError, ok := common.IsServiceError(pollErr); !ok || 1984 | (ok && serviceError.GetHTTPStatusCode() != 404) { 1985 | // fail if the error is not service error or 1986 | // if the error is service error and status code not equals to 404 1987 | helpers.FatalIfError(pollErr) 1988 | } 1989 | fmt.Println("VCN deleted") 1990 | } 1991 | 1992 | // 删除子网 1993 | func deleteSubnet(ctx context.Context, c core.VirtualNetworkClient, id *string) { 1994 | request := core.DeleteSubnetRequest{ 1995 | SubnetId: id, 1996 | RequestMetadata: helpers.GetRequestMetadataWithDefaultRetryPolicy(), 1997 | } 1998 | 1999 | _, err := c.DeleteSubnet(context.Background(), request) 2000 | helpers.FatalIfError(err) 2001 | 2002 | fmt.Println("deleteing subnet") 2003 | 2004 | // should retry condition check which returns a bool value indicating whether to do retry or not 2005 | // it checks the lifecycle status equals to Terminated or not for this case 2006 | shouldRetryFunc := func(r common.OCIOperationResponse) bool { 2007 | if serviceError, ok := common.IsServiceError(r.Error); ok && serviceError.GetHTTPStatusCode() == 404 { 2008 | // resource been deleted 2009 | return false 2010 | } 2011 | 2012 | if converted, ok := r.Response.(core.GetSubnetResponse); ok { 2013 | return converted.LifecycleState != core.SubnetLifecycleStateTerminated 2014 | } 2015 | return true 2016 | } 2017 | 2018 | pollGetRequest := core.GetSubnetRequest{ 2019 | SubnetId: id, 2020 | RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc), 2021 | } 2022 | 2023 | _, pollErr := c.GetSubnet(ctx, pollGetRequest) 2024 | if serviceError, ok := common.IsServiceError(pollErr); !ok || 2025 | (ok && serviceError.GetHTTPStatusCode() != 404) { 2026 | // fail if the error is not service error or 2027 | // if the error is service error and status code not equals to 404 2028 | helpers.FatalIfError(pollErr) 2029 | } 2030 | 2031 | fmt.Println("subnet deleted") 2032 | } 2033 | 2034 | func getInstance(instanceId *string) (core.Instance, error) { 2035 | req := core.GetInstanceRequest{ 2036 | InstanceId: instanceId, 2037 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2038 | } 2039 | resp, err := computeClient.GetInstance(ctx, req) 2040 | return resp.Instance, err 2041 | } 2042 | 2043 | func updateInstance(instanceId *string, displayName *string, ocpus, memoryInGBs *float32, 2044 | details []core.InstanceAgentPluginConfigDetails, disable *bool) (core.UpdateInstanceResponse, error) { 2045 | updateInstanceDetails := core.UpdateInstanceDetails{} 2046 | if displayName != nil && *displayName != "" { 2047 | updateInstanceDetails.DisplayName = displayName 2048 | } 2049 | shapeConfig := core.UpdateInstanceShapeConfigDetails{} 2050 | if ocpus != nil && *ocpus > 0 { 2051 | shapeConfig.Ocpus = ocpus 2052 | } 2053 | if memoryInGBs != nil && *memoryInGBs > 0 { 2054 | shapeConfig.MemoryInGBs = memoryInGBs 2055 | } 2056 | updateInstanceDetails.ShapeConfig = &shapeConfig 2057 | 2058 | // Oracle Cloud Agent 配置 2059 | if disable != nil && details != nil { 2060 | for i := 0; i < len(details); i++ { 2061 | if *disable { 2062 | details[i].DesiredState = core.InstanceAgentPluginConfigDetailsDesiredStateDisabled 2063 | } else { 2064 | details[i].DesiredState = core.InstanceAgentPluginConfigDetailsDesiredStateEnabled 2065 | } 2066 | } 2067 | agentConfig := core.UpdateInstanceAgentConfigDetails{ 2068 | IsMonitoringDisabled: disable, // 是否禁用监控插件 2069 | IsManagementDisabled: disable, // 是否禁用管理插件 2070 | AreAllPluginsDisabled: disable, // 是否禁用所有可用的插件(管理和监控插件) 2071 | PluginsConfig: details, 2072 | } 2073 | updateInstanceDetails.AgentConfig = &agentConfig 2074 | } 2075 | 2076 | req := core.UpdateInstanceRequest{ 2077 | InstanceId: instanceId, 2078 | UpdateInstanceDetails: updateInstanceDetails, 2079 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2080 | } 2081 | return computeClient.UpdateInstance(ctx, req) 2082 | } 2083 | 2084 | func instanceAction(instanceId *string, action core.InstanceActionActionEnum) (ins core.Instance, err error) { 2085 | req := core.InstanceActionRequest{ 2086 | InstanceId: instanceId, 2087 | Action: action, 2088 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2089 | } 2090 | resp, err := computeClient.InstanceAction(ctx, req) 2091 | ins = resp.Instance 2092 | return 2093 | } 2094 | 2095 | func changePublicIp(vnics []core.Vnic) (publicIp core.PublicIp, err error) { 2096 | var vnic core.Vnic 2097 | for _, v := range vnics { 2098 | if *v.IsPrimary { 2099 | vnic = v 2100 | } 2101 | } 2102 | fmt.Println("正在获取私有IP...") 2103 | var privateIps []core.PrivateIp 2104 | privateIps, err = getPrivateIps(vnic.Id) 2105 | if err != nil { 2106 | printlnErr("获取私有IP失败", err.Error()) 2107 | return 2108 | } 2109 | var privateIp core.PrivateIp 2110 | for _, p := range privateIps { 2111 | if *p.IsPrimary { 2112 | privateIp = p 2113 | } 2114 | } 2115 | 2116 | fmt.Println("正在获取公共IP OCID...") 2117 | publicIp, err = getPublicIp(privateIp.Id) 2118 | if err != nil { 2119 | printlnErr("获取公共IP OCID 失败", err.Error()) 2120 | } 2121 | fmt.Println("正在删除公共IP...") 2122 | _, err = deletePublicIp(publicIp.Id) 2123 | if err != nil { 2124 | printlnErr("删除公共IP 失败", err.Error()) 2125 | } 2126 | time.Sleep(3 * time.Second) 2127 | fmt.Println("正在创建公共IP...") 2128 | publicIp, err = createPublicIp(privateIp.Id) 2129 | return 2130 | } 2131 | 2132 | func getInstanceVnics(instanceId *string) (vnics []core.Vnic, err error) { 2133 | vnicAttachments, _, err := ListVnicAttachments(ctx, computeClient, instanceId, nil) 2134 | if err != nil { 2135 | return 2136 | } 2137 | for _, vnicAttachment := range vnicAttachments { 2138 | vnic, vnicErr := GetVnic(ctx, networkClient, vnicAttachment.VnicId) 2139 | if vnicErr != nil { 2140 | fmt.Printf("GetVnic error: %s\n", vnicErr.Error()) 2141 | continue 2142 | } 2143 | vnics = append(vnics, vnic) 2144 | } 2145 | return 2146 | } 2147 | 2148 | // 更新指定的VNIC 2149 | func updateVnic(vnicId *string) (core.Vnic, error) { 2150 | req := core.UpdateVnicRequest{ 2151 | VnicId: vnicId, 2152 | UpdateVnicDetails: core.UpdateVnicDetails{SkipSourceDestCheck: common.Bool(true)}, 2153 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2154 | } 2155 | resp, err := networkClient.UpdateVnic(ctx, req) 2156 | return resp.Vnic, err 2157 | } 2158 | 2159 | // 获取指定VNIC的私有IP 2160 | func getPrivateIps(vnicId *string) ([]core.PrivateIp, error) { 2161 | req := core.ListPrivateIpsRequest{ 2162 | VnicId: vnicId, 2163 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2164 | } 2165 | resp, err := networkClient.ListPrivateIps(ctx, req) 2166 | if err == nil && (resp.Items == nil || len(resp.Items) == 0) { 2167 | err = errors.New("私有IP为空") 2168 | } 2169 | return resp.Items, err 2170 | } 2171 | 2172 | // 获取分配给指定私有IP的公共IP 2173 | func getPublicIp(privateIpId *string) (core.PublicIp, error) { 2174 | req := core.GetPublicIpByPrivateIpIdRequest{ 2175 | GetPublicIpByPrivateIpIdDetails: core.GetPublicIpByPrivateIpIdDetails{PrivateIpId: privateIpId}, 2176 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2177 | } 2178 | resp, err := networkClient.GetPublicIpByPrivateIpId(ctx, req) 2179 | if err == nil && resp.PublicIp.Id == nil { 2180 | err = errors.New("未分配公共IP") 2181 | } 2182 | return resp.PublicIp, err 2183 | } 2184 | 2185 | // 删除公共IP 2186 | // 取消分配并删除指定公共IP(临时或保留) 2187 | // 如果仅需要取消分配保留的公共IP并将保留的公共IP返回到保留公共IP池,请使用updatePublicIp方法。 2188 | func deletePublicIp(publicIpId *string) (core.DeletePublicIpResponse, error) { 2189 | req := core.DeletePublicIpRequest{ 2190 | PublicIpId: publicIpId, 2191 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy()} 2192 | return networkClient.DeletePublicIp(ctx, req) 2193 | } 2194 | 2195 | // 创建公共IP 2196 | // 通过Lifetime指定创建临时公共IP还是保留公共IP。 2197 | // 创建临时公共IP,必须指定privateIpId,将临时公共IP分配给指定私有IP。 2198 | // 创建保留公共IP,可以不指定privateIpId。稍后可以使用updatePublicIp方法分配给私有IP。 2199 | func createPublicIp(privateIpId *string) (core.PublicIp, error) { 2200 | var publicIp core.PublicIp 2201 | req := core.CreatePublicIpRequest{ 2202 | CreatePublicIpDetails: core.CreatePublicIpDetails{ 2203 | CompartmentId: common.String(oracle.Tenancy), 2204 | Lifetime: core.CreatePublicIpDetailsLifetimeEphemeral, 2205 | PrivateIpId: privateIpId, 2206 | }, 2207 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2208 | } 2209 | resp, err := networkClient.CreatePublicIp(ctx, req) 2210 | publicIp = resp.PublicIp 2211 | return publicIp, err 2212 | } 2213 | 2214 | // 更新保留公共IP 2215 | // 1. 将保留的公共IP分配给指定的私有IP。如果该公共IP已经分配给私有IP,会取消分配,然后重新分配给指定的私有IP。 2216 | // 2. PrivateIpId设置为空字符串,公共IP取消分配到关联的私有IP。 2217 | func updatePublicIp(publicIpId *string, privateIpId *string) (core.PublicIp, error) { 2218 | req := core.UpdatePublicIpRequest{ 2219 | PublicIpId: publicIpId, 2220 | UpdatePublicIpDetails: core.UpdatePublicIpDetails{ 2221 | PrivateIpId: privateIpId, 2222 | }, 2223 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2224 | } 2225 | resp, err := networkClient.UpdatePublicIp(ctx, req) 2226 | return resp.PublicIp, err 2227 | } 2228 | 2229 | // 根据实例OCID获取公共IP 2230 | func getInstancePublicIps(instanceId *string) (ips []string, err error) { 2231 | // 多次尝试,避免刚抢购到实例,实例正在预配获取不到公共IP。 2232 | var ins core.Instance 2233 | for i := 0; i < 100; i++ { 2234 | if ins.LifecycleState != core.InstanceLifecycleStateRunning { 2235 | ins, err = getInstance(instanceId) 2236 | if err != nil { 2237 | continue 2238 | } 2239 | if ins.LifecycleState == core.InstanceLifecycleStateTerminating || ins.LifecycleState == core.InstanceLifecycleStateTerminated { 2240 | err = errors.New("实例已终止😔") 2241 | return 2242 | } 2243 | // if ins.LifecycleState != core.InstanceLifecycleStateRunning { 2244 | // continue 2245 | // } 2246 | } 2247 | 2248 | var vnicAttachments []core.VnicAttachment 2249 | vnicAttachments, _, err = ListVnicAttachments(ctx, computeClient, instanceId, nil) 2250 | if err != nil { 2251 | continue 2252 | } 2253 | if len(vnicAttachments) > 0 { 2254 | for _, vnicAttachment := range vnicAttachments { 2255 | vnic, vnicErr := GetVnic(ctx, networkClient, vnicAttachment.VnicId) 2256 | if vnicErr != nil { 2257 | printf("GetVnic error: %s\n", vnicErr.Error()) 2258 | continue 2259 | } 2260 | if vnic.PublicIp != nil && *vnic.PublicIp != "" { 2261 | ips = append(ips, *vnic.PublicIp) 2262 | } 2263 | } 2264 | return 2265 | } 2266 | time.Sleep(3 * time.Second) 2267 | } 2268 | return 2269 | } 2270 | 2271 | // 列出引导卷 2272 | func getBootVolumes(availabilityDomain *string) ([]core.BootVolume, error) { 2273 | req := core.ListBootVolumesRequest{ 2274 | AvailabilityDomain: availabilityDomain, 2275 | CompartmentId: common.String(oracle.Tenancy), 2276 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2277 | } 2278 | resp, err := storageClient.ListBootVolumes(ctx, req) 2279 | return resp.Items, err 2280 | } 2281 | 2282 | // 获取指定引导卷 2283 | func getBootVolume(bootVolumeId *string) (core.BootVolume, error) { 2284 | req := core.GetBootVolumeRequest{ 2285 | BootVolumeId: bootVolumeId, 2286 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2287 | } 2288 | resp, err := storageClient.GetBootVolume(ctx, req) 2289 | return resp.BootVolume, err 2290 | } 2291 | 2292 | // 更新引导卷 2293 | func updateBootVolume(bootVolumeId *string, sizeInGBs *int64, vpusPerGB *int64) (core.BootVolume, error) { 2294 | updateBootVolumeDetails := core.UpdateBootVolumeDetails{} 2295 | if sizeInGBs != nil { 2296 | updateBootVolumeDetails.SizeInGBs = sizeInGBs 2297 | } 2298 | if vpusPerGB != nil { 2299 | updateBootVolumeDetails.VpusPerGB = vpusPerGB 2300 | } 2301 | req := core.UpdateBootVolumeRequest{ 2302 | BootVolumeId: bootVolumeId, 2303 | UpdateBootVolumeDetails: updateBootVolumeDetails, 2304 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2305 | } 2306 | resp, err := storageClient.UpdateBootVolume(ctx, req) 2307 | return resp.BootVolume, err 2308 | } 2309 | 2310 | // 删除引导卷 2311 | func deleteBootVolume(bootVolumeId *string) (*http.Response, error) { 2312 | req := core.DeleteBootVolumeRequest{ 2313 | BootVolumeId: bootVolumeId, 2314 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2315 | } 2316 | resp, err := storageClient.DeleteBootVolume(ctx, req) 2317 | return resp.RawResponse, err 2318 | } 2319 | 2320 | // 分离引导卷 2321 | func detachBootVolume(bootVolumeAttachmentId *string) (*http.Response, error) { 2322 | req := core.DetachBootVolumeRequest{ 2323 | BootVolumeAttachmentId: bootVolumeAttachmentId, 2324 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2325 | } 2326 | resp, err := computeClient.DetachBootVolume(ctx, req) 2327 | return resp.RawResponse, err 2328 | } 2329 | 2330 | // 获取引导卷附件 2331 | func listBootVolumeAttachments(availabilityDomain, compartmentId, bootVolumeId *string) ([]core.BootVolumeAttachment, error) { 2332 | req := core.ListBootVolumeAttachmentsRequest{ 2333 | AvailabilityDomain: availabilityDomain, 2334 | CompartmentId: compartmentId, 2335 | BootVolumeId: bootVolumeId, 2336 | RequestMetadata: getCustomRequestMetadataWithRetryPolicy(), 2337 | } 2338 | resp, err := computeClient.ListBootVolumeAttachments(ctx, req) 2339 | return resp.Items, err 2340 | } 2341 | 2342 | func sendMessage(name, text string) (msg Message, err error) { 2343 | if token != "" && chat_id != "" { 2344 | data := url.Values{ 2345 | "parse_mode": {"Markdown"}, 2346 | "chat_id": {chat_id}, 2347 | "text": {"🔰*甲骨文通知* " + name + "\n" + text}, 2348 | } 2349 | var req *http.Request 2350 | req, err = http.NewRequest(http.MethodPost, sendMessageUrl, strings.NewReader(data.Encode())) 2351 | if err != nil { 2352 | return 2353 | } 2354 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 2355 | client := common.BaseClient{HTTPClient: &http.Client{}} 2356 | setProxyOrNot(&client) 2357 | var resp *http.Response 2358 | resp, err = client.HTTPClient.Do(req) 2359 | if err != nil { 2360 | return 2361 | } 2362 | var body []byte 2363 | body, err = ioutil.ReadAll(resp.Body) 2364 | if err != nil { 2365 | return 2366 | } 2367 | err = json.Unmarshal(body, &msg) 2368 | if err != nil { 2369 | return 2370 | } 2371 | if !msg.OK { 2372 | err = errors.New(msg.Description) 2373 | return 2374 | } 2375 | } 2376 | return 2377 | } 2378 | 2379 | func editMessage(messageId int, name, text string) (msg Message, err error) { 2380 | if token != "" && chat_id != "" { 2381 | data := url.Values{ 2382 | "parse_mode": {"Markdown"}, 2383 | "chat_id": {chat_id}, 2384 | "message_id": {strconv.Itoa(messageId)}, 2385 | "text": {"🔰*甲骨文通知* " + name + "\n" + text}, 2386 | } 2387 | var req *http.Request 2388 | req, err = http.NewRequest(http.MethodPost, editMessageUrl, strings.NewReader(data.Encode())) 2389 | if err != nil { 2390 | return 2391 | } 2392 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 2393 | client := common.BaseClient{HTTPClient: &http.Client{}} 2394 | setProxyOrNot(&client) 2395 | var resp *http.Response 2396 | resp, err = client.HTTPClient.Do(req) 2397 | if err != nil { 2398 | return 2399 | } 2400 | var body []byte 2401 | body, err = ioutil.ReadAll(resp.Body) 2402 | if err != nil { 2403 | return 2404 | } 2405 | err = json.Unmarshal(body, &msg) 2406 | if err != nil { 2407 | return 2408 | } 2409 | if !msg.OK { 2410 | err = errors.New(msg.Description) 2411 | return 2412 | } 2413 | 2414 | } 2415 | return 2416 | } 2417 | 2418 | func setProxyOrNot(client *common.BaseClient) { 2419 | if proxy != "" { 2420 | proxyURL, err := url.Parse(proxy) 2421 | if err != nil { 2422 | printlnErr("URL parse failed", err.Error()) 2423 | return 2424 | } 2425 | client.HTTPClient = &http.Client{ 2426 | Transport: &http.Transport{ 2427 | Proxy: http.ProxyURL(proxyURL), 2428 | }, 2429 | } 2430 | } 2431 | } 2432 | 2433 | func getInstanceState(state core.InstanceLifecycleStateEnum) string { 2434 | var friendlyState string 2435 | switch state { 2436 | case core.InstanceLifecycleStateMoving: 2437 | friendlyState = "正在移动" 2438 | case core.InstanceLifecycleStateProvisioning: 2439 | friendlyState = "正在预配" 2440 | case core.InstanceLifecycleStateRunning: 2441 | friendlyState = "正在运行" 2442 | case core.InstanceLifecycleStateStarting: 2443 | friendlyState = "正在启动" 2444 | case core.InstanceLifecycleStateStopping: 2445 | friendlyState = "正在停止" 2446 | case core.InstanceLifecycleStateStopped: 2447 | friendlyState = "已停止 " 2448 | case core.InstanceLifecycleStateTerminating: 2449 | friendlyState = "正在终止" 2450 | case core.InstanceLifecycleStateTerminated: 2451 | friendlyState = "已终止 " 2452 | default: 2453 | friendlyState = string(state) 2454 | } 2455 | return friendlyState 2456 | } 2457 | 2458 | func getBootVolumeState(state core.BootVolumeLifecycleStateEnum) string { 2459 | var friendlyState string 2460 | switch state { 2461 | case core.BootVolumeLifecycleStateProvisioning: 2462 | friendlyState = "正在预配" 2463 | case core.BootVolumeLifecycleStateRestoring: 2464 | friendlyState = "正在恢复" 2465 | case core.BootVolumeLifecycleStateAvailable: 2466 | friendlyState = "可用  " 2467 | case core.BootVolumeLifecycleStateTerminating: 2468 | friendlyState = "正在终止" 2469 | case core.BootVolumeLifecycleStateTerminated: 2470 | friendlyState = "已终止 " 2471 | case core.BootVolumeLifecycleStateFaulty: 2472 | friendlyState = "故障  " 2473 | default: 2474 | friendlyState = string(state) 2475 | } 2476 | return friendlyState 2477 | } 2478 | 2479 | func fmtDuration(d time.Duration) string { 2480 | if d.Seconds() < 1 { 2481 | return "< 1 秒" 2482 | } 2483 | var buffer bytes.Buffer 2484 | //days := int(d.Hours() / 24) 2485 | //hours := int(math.Mod(d.Hours(), 24)) 2486 | //minutes := int(math.Mod(d.Minutes(), 60)) 2487 | //seconds := int(math.Mod(d.Seconds(), 60)) 2488 | 2489 | days := int(d / (time.Hour * 24)) 2490 | hours := int((d % (time.Hour * 24)).Hours()) 2491 | minutes := int((d % time.Hour).Minutes()) 2492 | seconds := int((d % time.Minute).Seconds()) 2493 | 2494 | if days > 0 { 2495 | buffer.WriteString(fmt.Sprintf("%d 天 ", days)) 2496 | } 2497 | if hours > 0 { 2498 | buffer.WriteString(fmt.Sprintf("%d 时 ", hours)) 2499 | } 2500 | if minutes > 0 { 2501 | buffer.WriteString(fmt.Sprintf("%d 分 ", minutes)) 2502 | } 2503 | if seconds > 0 { 2504 | buffer.WriteString(fmt.Sprintf("%d 秒", seconds)) 2505 | } 2506 | return buffer.String() 2507 | } 2508 | 2509 | func printf(format string, a ...interface{}) { 2510 | fmt.Printf("%s ", time.Now().Format("2006-01-02 15:04:05")) 2511 | fmt.Printf(format, a...) 2512 | } 2513 | 2514 | func printlnErr(desc, detail string) { 2515 | fmt.Printf("\033[1;31mError: %s. %s\033[0m\n", desc, detail) 2516 | } 2517 | 2518 | func getCustomRequestMetadataWithRetryPolicy() common.RequestMetadata { 2519 | return common.RequestMetadata{ 2520 | RetryPolicy: getCustomRetryPolicy(), 2521 | } 2522 | } 2523 | 2524 | func getCustomRetryPolicy() *common.RetryPolicy { 2525 | // how many times to do the retry 2526 | attempts := uint(3) 2527 | // retry for all non-200 status code 2528 | retryOnAllNon200ResponseCodes := func(r common.OCIOperationResponse) bool { 2529 | return !(r.Error == nil && 199 < r.Response.HTTPResponse().StatusCode && r.Response.HTTPResponse().StatusCode < 300) 2530 | } 2531 | policy := common.NewRetryPolicyWithOptions( 2532 | // only base off DefaultRetryPolicyWithoutEventualConsistency() if we're not handling eventual consistency 2533 | common.WithConditionalOption(!false, common.ReplaceWithValuesFromRetryPolicy(common.DefaultRetryPolicyWithoutEventualConsistency())), 2534 | common.WithMaximumNumberAttempts(attempts), 2535 | common.WithShouldRetryOperation(retryOnAllNon200ResponseCodes)) 2536 | return &policy 2537 | } 2538 | 2539 | func command(cmd string) { 2540 | res := strings.Fields(cmd) 2541 | if len(res) > 0 { 2542 | fmt.Println("执行命令:", strings.Join(res, " ")) 2543 | name := res[0] 2544 | arg := res[1:] 2545 | out, err := exec.Command(name, arg...).CombinedOutput() 2546 | if err == nil { 2547 | fmt.Println(string(out)) 2548 | } else { 2549 | fmt.Println(err) 2550 | } 2551 | } 2552 | } 2553 | -------------------------------------------------------------------------------- /oci-help.ini: -------------------------------------------------------------------------------- 1 | # 配置 socks5 或 http 代理. socks5://127.0.0.1:7890 / http://127.0.0.1:7890 2 | #proxy=socks5://127.0.0.1:7890 3 | # Telegram Bot 消息提醒 4 | token= 5 | chat_id= 6 | 7 | 8 | ############################## 甲骨文账号配置 ############################## 9 | # 可以配置多个账号 10 | [新加坡01] 11 | user= 12 | fingerprint= 13 | tenancy= 14 | region= 15 | key_file=xxxxxx.pem 16 | 17 | [东京01] 18 | user= 19 | fingerprint= 20 | tenancy= 21 | region= 22 | key_file= 23 | 24 | [东京02] 25 | user= 26 | fingerprint= 27 | tenancy= 28 | region= 29 | key_file= 30 | 31 | 32 | 33 | 34 | 35 | 36 | ############################## 实例相关参数配置 ############################## 37 | [INSTANCE] 38 | # 虚拟云网络名称 (可选) 39 | #vcnDisplayName= 40 | # 子网名称 (可选) 41 | #subnetDisplayName= 42 | # 实例名称 (可选) 43 | #instanceDisplayName= 44 | # 系统 Canonical Ubuntu / CentOS / Oracle Linux 45 | OperatingSystem=Canonical Ubuntu 46 | # 系统版本 Canonical Ubuntu: 20.04|18.04 / CentOS :8|7 / Oracle Linux: 8|7.9 47 | OperatingSystemVersion=20.04 48 | # 失败后重试次数 49 | retry=3 50 | # 延迟时间(秒) 51 | minTime=5 52 | maxTime=30 53 | # ssh_authorized_key= # 请在下方 [INSTANCE.ARM] 和 [INSTANCE.AMD] 中配置 SSH 公钥。 54 | # 初始化脚本(将脚本内容base64编码后添加)。该脚本将在您的实例引导或重新启动时运行。 55 | cloud-init= 56 | 57 | [INSTANCE.ARM] 58 | shape=VM.Standard.A1.Flex 59 | cpus=1 # cpu个数 60 | memoryInGBs=6 # 内存大小(GB) 61 | bootVolumeSizeInGBs=50 # 引导卷大小(GB) 62 | sum=1 # 创建实例个数 63 | retry=-1 # 失败后重试次数设置为-1,失败后一直尝试直到成功。 64 | # 可用性域(选填) 65 | availabilityDomain= 66 | # SSH 公钥 67 | ssh_authorized_key= 68 | 69 | [INSTANCE.AMD] 70 | shape=VM.Standard.E2.1.Micro 71 | bootVolumeSizeInGBs=50 # 引导卷大小 72 | sum=1 # 创建实例个数 73 | retry=-1 74 | # 可用性域(选填) 75 | availabilityDomain= 76 | # SSH 公钥 77 | ssh_authorized_key= --------------------------------------------------------------------------------