main.go 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142
  1. package main
  2. import (
  3. "context"
  4. "errors"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "math/rand"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "strings"
  15. "time"
  16. "github.com/oracle/oci-go-sdk/v49/common"
  17. "github.com/oracle/oci-go-sdk/v49/core"
  18. "github.com/oracle/oci-go-sdk/v49/example/helpers"
  19. "github.com/oracle/oci-go-sdk/v49/identity"
  20. "gopkg.in/ini.v1"
  21. )
  22. const (
  23. defConfigFilePath = "./oci-help.ini"
  24. IPsFilePrefix = "IPs"
  25. )
  26. var (
  27. provider common.ConfigurationProvider
  28. config Config
  29. providerName string
  30. proxy string
  31. token string
  32. chat_id string
  33. sendMessageUrl string
  34. EACH bool
  35. )
  36. type Config struct {
  37. AvailabilityDomain string `ini:"availabilityDomain"`
  38. SSH_Public_Key string `ini:"ssh_authorized_key"`
  39. CompartmentID string `ini:"tenancy"`
  40. VcnDisplayName string `ini:"vcnDisplayName"`
  41. SubnetDisplayName string `ini:"subnetDisplayName"`
  42. Shape string `ini:"shape"`
  43. OperatingSystem string `ini:"OperatingSystem"`
  44. OperatingSystemVersion string `ini:"OperatingSystemVersion"`
  45. InstanceDisplayName string `ini:"instanceDisplayName"`
  46. Ocpus float32 `ini:"cpus"`
  47. MemoryInGBs float32 `ini:"memoryInGBs"`
  48. BootVolumeSizeInGBs int64 `ini:"bootVolumeSizeInGBs"`
  49. Sum int32 `ini:"sum"`
  50. Each int32 `ini:"each"`
  51. Retry int32 `ini:"retry"`
  52. CloudInit string `ini:"cloud-init"`
  53. MinTime int32 `ini:"minTime"`
  54. MaxTime int32 `ini:"maxTime"`
  55. }
  56. func main() {
  57. var configFilePath string
  58. flag.StringVar(&configFilePath, "config", defConfigFilePath, "配置文件路径")
  59. flag.StringVar(&configFilePath, "c", defConfigFilePath, "配置文件路径")
  60. flag.Parse()
  61. cfg, err := ini.Load(configFilePath)
  62. helpers.FatalIfError(err)
  63. sections := cfg.Sections()
  64. defSec := cfg.Section(ini.DefaultSection)
  65. proxy = defSec.Key("proxy").Value()
  66. token = defSec.Key("token").Value()
  67. chat_id = defSec.Key("chat_id").Value()
  68. if defSec.HasKey("EACH") {
  69. EACH, _ = defSec.Key("EACH").Bool()
  70. } else {
  71. EACH = true
  72. }
  73. sendMessageUrl = "https://api.telegram.org/bot" + token + "/sendMessage"
  74. rand.Seed(time.Now().UnixNano())
  75. fmt.Printf("\n\033[1;32m%s\033[0m\n\n", "欢迎使用甲骨文实例创建工具")
  76. fmt.Printf("\033[1;36m%s\033[0m %s\n", "1.", "开始创建实例")
  77. fmt.Printf("\033[1;36m%s\033[0m %s\n", "2.", "获取实例IP地址")
  78. fmt.Println("")
  79. fmt.Print("请选择[1-2]: ")
  80. var num int
  81. fmt.Scanln(&num)
  82. switch num {
  83. case 1:
  84. CreateInstances(sections, configFilePath)
  85. case 2:
  86. ListAllIPs(sections, configFilePath)
  87. default:
  88. }
  89. }
  90. func CreateInstances(sections []*ini.Section, configFile string) {
  91. for _, section := range sections {
  92. if len(section.ChildSections()) > 0 {
  93. provider = getProvider(configFile, section.Name(), "")
  94. printf("\033[1;36m[%s]\033[0m\n", section.Name())
  95. var SUM, NUM int32 = 0, 0
  96. sendMessage(section.Name(), "开始创建")
  97. // 获取可用性域
  98. AvailabilityDomains := ListAvailabilityDomains()
  99. for _, child := range section.ChildSections() {
  100. providerName = child.Name()
  101. config = Config{}
  102. err := child.MapTo(&config)
  103. helpers.FatalIfError(err)
  104. sum, num := LaunchInstances(AvailabilityDomains)
  105. SUM = SUM + sum
  106. NUM = NUM + num
  107. }
  108. printf("\033[1;36m[%s], 创建总数: %d, 创建成功 %d , 创建失败 %d\033[0m\n", section.Name(), SUM, NUM, SUM-NUM)
  109. text := fmt.Sprintf("结束创建。创建总数: %d, 创建成功 %d , 创建失败 %d", SUM, NUM, SUM-NUM)
  110. sendMessage(section.Name(), text)
  111. }
  112. }
  113. }
  114. // 返回值 sum: 创建实例总数; num: 创建成功的个数
  115. func LaunchInstances(ads []identity.AvailabilityDomain) (sum, num int32) {
  116. /* 创建实例的几种情况
  117. * 1. 设置了 availabilityDomain 参数,即在设置的可用性域中创建 sum 个实例。
  118. * 2. 没有设置 availabilityDomain 但是设置了 each 参数。即在获取的每个可用性域中创建 each 个实例,创建的实例总数 sum = each * adCount。
  119. * 3. 没有设置 availabilityDomain 且没有设置 each 参数,即在获取到的可用性域中创建的实例总数为 sum。
  120. */
  121. //可用性域数量
  122. var adCount int32 = int32(len(ads))
  123. adName := common.String(config.AvailabilityDomain)
  124. each := config.Each
  125. sum = config.Sum
  126. // 没有设置可用性域并且没有设置each时,才有用。
  127. var usableAds = make([]identity.AvailabilityDomain, 0)
  128. //可用性域不固定,即没有提供 availabilityDomain 参数
  129. var AD_NOT_FIXED bool = false
  130. var EACH_AD = false
  131. if adName == nil || *adName == "" {
  132. AD_NOT_FIXED = true
  133. if each > 0 {
  134. EACH_AD = true
  135. sum = each * adCount
  136. } else {
  137. EACH_AD = false
  138. usableAds = ads
  139. }
  140. }
  141. printf("\033[1;36m[%s] 开始创建...\033[0m\n", providerName)
  142. computeClient, err := core.NewComputeClientWithConfigurationProvider(provider)
  143. helpers.FatalIfError(err)
  144. setProxyOrNot(&computeClient.BaseClient)
  145. networkClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(provider)
  146. helpers.FatalIfError(err)
  147. setProxyOrNot(&networkClient.BaseClient)
  148. ctx := context.Background()
  149. name := config.InstanceDisplayName
  150. if name == "" {
  151. name = time.Now().Format("instance-20060102-1504")
  152. }
  153. displayName := common.String(name)
  154. if sum > 1 {
  155. displayName = common.String(name + "-1")
  156. }
  157. // create the launch instance request
  158. request := core.LaunchInstanceRequest{}
  159. request.CompartmentId = common.String(config.CompartmentID)
  160. request.DisplayName = displayName
  161. // create a subnet or get the one already created
  162. subnet := CreateOrGetNetworkInfrastructure(ctx, networkClient)
  163. printf("子网: %s\n", *subnet.DisplayName)
  164. request.CreateVnicDetails = &core.CreateVnicDetails{SubnetId: subnet.Id}
  165. // Get a image.
  166. image, err := GetImage(ctx, computeClient)
  167. helpers.FatalIfError(err)
  168. printf("系统镜像: %s\n", *image.DisplayName)
  169. sd := core.InstanceSourceViaImageDetails{}
  170. sd.ImageId = image.Id
  171. if config.BootVolumeSizeInGBs > 0 {
  172. sd.BootVolumeSizeInGBs = common.Int64(config.BootVolumeSizeInGBs)
  173. }
  174. request.SourceDetails = sd
  175. request.IsPvEncryptionInTransitEnabled = common.Bool(true)
  176. request.Shape = common.String(config.Shape)
  177. if config.Ocpus > 0 && config.MemoryInGBs > 0 {
  178. request.ShapeConfig = &core.LaunchInstanceShapeConfigDetails{
  179. Ocpus: common.Float32(config.Ocpus),
  180. MemoryInGBs: common.Float32(config.MemoryInGBs),
  181. }
  182. }
  183. metaData := map[string]string{}
  184. metaData["ssh_authorized_keys"] = config.SSH_Public_Key
  185. if config.CloudInit != "" {
  186. metaData["user_data"] = config.CloudInit
  187. }
  188. request.Metadata = metaData
  189. minTime := config.MinTime
  190. maxTime := config.MaxTime
  191. SKIP_RETRY_MAP := make(map[int32]bool)
  192. var usableAdsTemp = make([]identity.AvailabilityDomain, 0)
  193. retry := config.Retry // 重试次数
  194. var failTimes int32 = 0 // 失败次数
  195. var adIndex int32 = 0 // 当前可用性域下标
  196. var pos int32 = 0 // for 循环次数
  197. var SUCCESS = false // 创建是否成功
  198. for pos < sum {
  199. if AD_NOT_FIXED {
  200. if EACH_AD {
  201. if pos%each == 0 && failTimes == 0 {
  202. adName = ads[adIndex].Name
  203. adIndex++
  204. }
  205. } else {
  206. if SUCCESS {
  207. adIndex = 0
  208. }
  209. if adIndex >= adCount {
  210. adIndex = 0
  211. }
  212. //adName = ads[adIndex].Name
  213. adName = usableAds[adIndex].Name
  214. adIndex++
  215. }
  216. }
  217. printf("\033[1;36m[%s] 第 %d 个实例正在创建, AD: %s\033[0m\n", providerName, pos+1, *adName)
  218. request.AvailabilityDomain = adName
  219. createResp, err := computeClient.LaunchInstance(ctx, request)
  220. if err == nil {
  221. // 创建实例成功
  222. SUCCESS = true
  223. num++ //成功个数+1
  224. // 获取实例公共IP
  225. ips, err := getInstancePublicIps(ctx, computeClient, networkClient, createResp.Instance.Id)
  226. var strIps string
  227. if err != nil {
  228. strIps = err.Error()
  229. } else {
  230. strIps = strings.Join(ips, ",")
  231. }
  232. printf("\033[1;32m[%s] 第 %d 个实例创建成功. 实例名称: %s, 公网IP: %s\033[0m\n", providerName, pos+1, *createResp.Instance.DisplayName, strIps)
  233. if EACH {
  234. sendMessage(providerName, fmt.Sprintf("第 %d 个实例创建成功\n实例名称: %s\n公网IP: %s", pos+1, *createResp.Instance.DisplayName, strIps))
  235. }
  236. sleepRandomSecond(minTime, maxTime)
  237. displayName = common.String(fmt.Sprintf("%s-%d", name, pos+1))
  238. request.DisplayName = displayName
  239. } else {
  240. // 创建实例失败
  241. SUCCESS = false
  242. // 错误信息
  243. errInfo := err.Error()
  244. // 是否跳过重试
  245. SKIP_RETRY := false
  246. //isRetryable := common.IsErrorRetryableByDefault(err)
  247. //isNetErr := common.IsNetworkError(err)
  248. servErr, isServErr := common.IsServiceError(err)
  249. // API Errors: https://docs.cloud.oracle.com/Content/API/References/apierrors.htm
  250. if isServErr && (400 <= servErr.GetHTTPStatusCode() && servErr.GetHTTPStatusCode() <= 405) ||
  251. (servErr.GetHTTPStatusCode() == 409 && !strings.EqualFold(servErr.GetCode(), "IncorrectState")) ||
  252. servErr.GetHTTPStatusCode() == 412 || servErr.GetHTTPStatusCode() == 413 || servErr.GetHTTPStatusCode() == 422 ||
  253. servErr.GetHTTPStatusCode() == 431 || servErr.GetHTTPStatusCode() == 501 {
  254. // 不可重试
  255. if isServErr {
  256. errInfo = servErr.GetMessage()
  257. }
  258. printf("\033[1;31m[%s] 创建失败, Error: \033[0m%s\n", providerName, errInfo)
  259. if EACH {
  260. sendMessage(providerName, "创建失败,Error: "+errInfo)
  261. }
  262. SKIP_RETRY = true
  263. if AD_NOT_FIXED && !EACH_AD {
  264. SKIP_RETRY_MAP[adIndex-1] = true
  265. }
  266. } else {
  267. // 可重试
  268. if isServErr {
  269. errInfo = servErr.GetMessage()
  270. }
  271. printf("\033[1;31m[%s] 创建失败, Error: \033[0m%s\n", providerName, errInfo)
  272. SKIP_RETRY = false
  273. if AD_NOT_FIXED && !EACH_AD {
  274. SKIP_RETRY_MAP[adIndex-1] = false
  275. }
  276. }
  277. sleepRandomSecond(minTime, maxTime)
  278. if AD_NOT_FIXED {
  279. if !EACH_AD {
  280. if adIndex < adCount {
  281. // 没有设置可用性域,且没有设置each。即在获取到的每个可用性域里尝试创建。当前使用的可用性域不是最后一个,继续尝试。
  282. continue
  283. } else {
  284. // 当前使用的可用性域是最后一个,判断失败次数是否达到重试次数,未达到重试次数继续尝试。
  285. failTimes++
  286. for index, skip := range SKIP_RETRY_MAP {
  287. if !skip {
  288. usableAdsTemp = append(usableAdsTemp, usableAds[index])
  289. }
  290. }
  291. // 重新设置 usableAds
  292. usableAds = usableAdsTemp
  293. adCount = int32(len(usableAds))
  294. // 重置变量
  295. usableAdsTemp = nil
  296. for k := range SKIP_RETRY_MAP {
  297. delete(SKIP_RETRY_MAP, k)
  298. }
  299. // 判断是否需要重试
  300. if (retry < 0 || failTimes <= retry) && adCount > 0 {
  301. continue
  302. }
  303. }
  304. } else {
  305. // 没有设置可用性域,且设置了each,即在每个域创建each个实例。判断失败次数继续尝试。
  306. failTimes++
  307. if (retry < 0 || failTimes <= retry) && !SKIP_RETRY {
  308. continue
  309. }
  310. }
  311. } else {
  312. //设置了可用性域,判断是否需要重试
  313. failTimes++
  314. if (retry < 0 || failTimes <= retry) && !SKIP_RETRY {
  315. continue
  316. }
  317. }
  318. }
  319. // 重置变量
  320. usableAds = ads
  321. adCount = int32(len(usableAds))
  322. usableAdsTemp = nil
  323. for k := range SKIP_RETRY_MAP {
  324. delete(SKIP_RETRY_MAP, k)
  325. }
  326. // 成功或者失败次数达到重试次数,重置失败次数为0
  327. failTimes = 0
  328. // for 循环次数+1
  329. pos++
  330. }
  331. return
  332. }
  333. func sleepRandomSecond(min, max int32) {
  334. var second int32
  335. if min <= 0 || max <= 0 {
  336. second = 1
  337. } else if min >= max {
  338. second = max
  339. } else {
  340. second = rand.Int31n(max-min) + min
  341. }
  342. printf("Sleep %d Second...\n", second)
  343. time.Sleep(time.Duration(second) * time.Second)
  344. }
  345. func ListAllIPs(sections []*ini.Section, configFile string) {
  346. IPsFilePath := IPsFilePrefix + "-" + time.Now().Format("2006-01-02-150405.txt")
  347. _, err := os.Stat(IPsFilePath)
  348. if err != nil && os.IsNotExist(err) {
  349. os.Create(IPsFilePath)
  350. }
  351. printf("获取实例IP地址...\n")
  352. for _, section := range sections {
  353. if len(section.ChildSections()) > 0 {
  354. provider = getProvider(configFile, section.Name(), "")
  355. ListInstancesIPs(IPsFilePath, section.Name())
  356. }
  357. }
  358. printf("获取实例IP地址完成,请查看文件 %s\n", IPsFilePath)
  359. }
  360. func ListInstancesIPs(filePath string, sectionName string) {
  361. ctx := context.Background()
  362. computeClient, err := core.NewComputeClientWithConfigurationProvider(provider)
  363. helpers.FatalIfError(err)
  364. setProxyOrNot(&computeClient.BaseClient)
  365. netClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(provider)
  366. helpers.FatalIfError(err)
  367. setProxyOrNot(&netClient.BaseClient)
  368. vnicAttachments, err := ListVnicAttachments(ctx, computeClient, nil)
  369. if err != nil {
  370. printf("ListVnicAttachments Error: %s\n", err.Error())
  371. }
  372. file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
  373. if err != nil {
  374. printf("打开文件失败, Error: %s\n", err.Error())
  375. return
  376. }
  377. _, err = io.WriteString(file, "["+sectionName+"]\n")
  378. if err != nil {
  379. printf("%s\n", err.Error())
  380. }
  381. for _, vnicAttachment := range vnicAttachments {
  382. vnic, err := GetVnic(ctx, netClient, vnicAttachment.VnicId)
  383. if err != nil {
  384. printf("IP地址获取失败, %s\n", err.Error())
  385. continue
  386. }
  387. printf("实例: %s, IP: %s\n", *vnic.DisplayName, *vnic.PublicIp)
  388. _, err = io.WriteString(file, "实例: "+*vnic.DisplayName+", IP: "+*vnic.PublicIp+"\n")
  389. if err != nil {
  390. printf("写入文件失败, Error: %s\n", err.Error())
  391. }
  392. }
  393. _, err = io.WriteString(file, "\n")
  394. if err != nil {
  395. printf("%s\n", err.Error())
  396. }
  397. }
  398. // ExampleLaunchInstance does create an instance
  399. // NOTE: launch instance will create a new instance and VCN. please make sure delete the instance
  400. // after execute this sample code, otherwise, you will be charged for the running instance
  401. func ExampleLaunchInstance() {
  402. c, err := core.NewComputeClientWithConfigurationProvider(provider)
  403. helpers.FatalIfError(err)
  404. networkClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(provider)
  405. helpers.FatalIfError(err)
  406. ctx := context.Background()
  407. // create the launch instance request
  408. request := core.LaunchInstanceRequest{}
  409. request.CompartmentId = common.String(config.CompartmentID)
  410. request.DisplayName = common.String(config.InstanceDisplayName)
  411. request.AvailabilityDomain = common.String(config.AvailabilityDomain)
  412. // create a subnet or get the one already created
  413. subnet := CreateOrGetNetworkInfrastructure(ctx, networkClient)
  414. fmt.Println("subnet created")
  415. request.CreateVnicDetails = &core.CreateVnicDetails{SubnetId: subnet.Id}
  416. // get a image
  417. image := listImages(ctx, c)[0]
  418. fmt.Println("list images")
  419. request.SourceDetails = core.InstanceSourceViaImageDetails{
  420. ImageId: image.Id,
  421. BootVolumeSizeInGBs: common.Int64(config.BootVolumeSizeInGBs),
  422. }
  423. // use [config.Shape] to create instance
  424. request.Shape = common.String(config.Shape)
  425. request.ShapeConfig = &core.LaunchInstanceShapeConfigDetails{
  426. Ocpus: common.Float32(config.Ocpus),
  427. MemoryInGBs: common.Float32(config.MemoryInGBs),
  428. }
  429. // add ssh_authorized_keys
  430. //metaData := map[string]string{
  431. // "ssh_authorized_keys": config.SSH_Public_Key,
  432. //}
  433. //request.Metadata = metaData
  434. request.Metadata = map[string]string{"ssh_authorized_keys": config.SSH_Public_Key}
  435. // default retry policy will retry on non-200 response
  436. request.RequestMetadata = helpers.GetRequestMetadataWithDefaultRetryPolicy()
  437. createResp, err := c.LaunchInstance(ctx, request)
  438. helpers.FatalIfError(err)
  439. fmt.Println("launching instance")
  440. // should retry condition check which returns a bool value indicating whether to do retry or not
  441. // it checks the lifecycle status equals to Running or not for this case
  442. shouldRetryFunc := func(r common.OCIOperationResponse) bool {
  443. if converted, ok := r.Response.(core.GetInstanceResponse); ok {
  444. return converted.LifecycleState != core.InstanceLifecycleStateRunning
  445. }
  446. return true
  447. }
  448. // create get instance request with a retry policy which takes a function
  449. // to determine shouldRetry or not
  450. pollingGetRequest := core.GetInstanceRequest{
  451. InstanceId: createResp.Instance.Id,
  452. RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc),
  453. }
  454. instance, pollError := c.GetInstance(ctx, pollingGetRequest)
  455. helpers.FatalIfError(pollError)
  456. fmt.Println("instance launched")
  457. // 创建辅助 VNIC 并将其附加到指定的实例
  458. attachVnicResponse, err := c.AttachVnic(context.Background(), core.AttachVnicRequest{
  459. AttachVnicDetails: core.AttachVnicDetails{
  460. CreateVnicDetails: &core.CreateVnicDetails{
  461. SubnetId: subnet.Id,
  462. AssignPublicIp: common.Bool(true),
  463. },
  464. InstanceId: instance.Id,
  465. },
  466. })
  467. helpers.FatalIfError(err)
  468. fmt.Println("vnic attached")
  469. vnicState := attachVnicResponse.VnicAttachment.LifecycleState
  470. for vnicState != core.VnicAttachmentLifecycleStateAttached {
  471. time.Sleep(15 * time.Second)
  472. getVnicAttachmentRequest, err := c.GetVnicAttachment(context.Background(), core.GetVnicAttachmentRequest{
  473. VnicAttachmentId: attachVnicResponse.Id,
  474. })
  475. helpers.FatalIfError(err)
  476. vnicState = getVnicAttachmentRequest.VnicAttachment.LifecycleState
  477. }
  478. // 分离并删除指定的辅助 VNIC
  479. _, err = c.DetachVnic(context.Background(), core.DetachVnicRequest{
  480. VnicAttachmentId: attachVnicResponse.Id,
  481. })
  482. helpers.FatalIfError(err)
  483. fmt.Println("vnic dettached")
  484. defer func() {
  485. terminateInstance(ctx, c, createResp.Id)
  486. client, clerr := core.NewVirtualNetworkClientWithConfigurationProvider(common.DefaultConfigProvider())
  487. helpers.FatalIfError(clerr)
  488. vcnID := subnet.VcnId
  489. deleteSubnet(ctx, client, subnet.Id)
  490. deleteVcn(ctx, client, vcnID)
  491. }()
  492. // Output:
  493. // subnet created
  494. // list images
  495. // list shapes
  496. // launching instance
  497. // instance launched
  498. // vnic attached
  499. // vnic dettached
  500. // terminating instance
  501. // instance terminated
  502. // deleteing subnet
  503. // subnet deleted
  504. // deleteing VCN
  505. // VCN deleted
  506. }
  507. func getProvider(configPath, profile, privateKeyPassword string) common.ConfigurationProvider {
  508. //provider := common.DefaultConfigProvider()
  509. //provider, err := common.ConfigurationProviderFromFile("./oci-config", "")
  510. provider, err := common.ConfigurationProviderFromFileWithProfile(configPath, profile, privateKeyPassword)
  511. helpers.FatalIfError(err)
  512. return provider
  513. }
  514. // 创建或获取基础网络设施
  515. func CreateOrGetNetworkInfrastructure(ctx context.Context, c core.VirtualNetworkClient) core.Subnet {
  516. vcn := createOrGetVcn(ctx, c)
  517. gateway := createOrGetInternetGateway(c, vcn.Id)
  518. createOrGetRouteTable(c, gateway.Id, vcn.Id)
  519. subnet := createOrGetSubnetWithDetails(
  520. ctx, c, vcn.Id,
  521. common.String(config.SubnetDisplayName),
  522. common.String("10.0.0.0/24"),
  523. common.String("subnetdns"),
  524. common.String(config.AvailabilityDomain))
  525. return subnet
  526. }
  527. // CreateOrGetSubnetWithDetails either creates a new Virtual Cloud Network (VCN) or get the one already exist
  528. // with detail info
  529. func createOrGetSubnetWithDetails(ctx context.Context, c core.VirtualNetworkClient, vcnID *string,
  530. displayName *string, cidrBlock *string, dnsLabel *string, availableDomain *string) core.Subnet {
  531. subnets := listSubnets(ctx, c, vcnID)
  532. if displayName == nil {
  533. displayName = common.String(config.SubnetDisplayName)
  534. }
  535. if len(subnets) > 0 && *displayName == "" {
  536. return subnets[0]
  537. }
  538. // check if the subnet has already been created
  539. for _, element := range subnets {
  540. if *element.DisplayName == *displayName {
  541. // find the subnet, return it
  542. return element
  543. }
  544. }
  545. // create a new subnet
  546. printf("开始创建Subnet(没有可用的Subnet,或指定的Subnet不存在)\n")
  547. // 子网名称为空,以当前时间为名称创建子网
  548. if *displayName == "" {
  549. displayName = common.String(time.Now().Format("subnet-20060102-1504"))
  550. }
  551. request := core.CreateSubnetRequest{}
  552. //request.AvailabilityDomain = availableDomain //省略此属性创建区域性子网(regional subnet),提供此属性创建特定于可用性域的子网。建议创建区域性子网。
  553. request.CompartmentId = &config.CompartmentID
  554. request.CidrBlock = cidrBlock
  555. request.DisplayName = displayName
  556. request.DnsLabel = dnsLabel
  557. request.RequestMetadata = helpers.GetRequestMetadataWithDefaultRetryPolicy()
  558. request.VcnId = vcnID
  559. r, err := c.CreateSubnet(ctx, request)
  560. helpers.FatalIfError(err)
  561. // retry condition check, stop unitl return true
  562. pollUntilAvailable := func(r common.OCIOperationResponse) bool {
  563. if converted, ok := r.Response.(core.GetSubnetResponse); ok {
  564. return converted.LifecycleState != core.SubnetLifecycleStateAvailable
  565. }
  566. return true
  567. }
  568. pollGetRequest := core.GetSubnetRequest{
  569. SubnetId: r.Id,
  570. RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(pollUntilAvailable),
  571. }
  572. // wait for lifecyle become running
  573. _, pollErr := c.GetSubnet(ctx, pollGetRequest)
  574. helpers.FatalIfError(pollErr)
  575. // update the security rules
  576. getReq := core.GetSecurityListRequest{
  577. SecurityListId: common.String(r.SecurityListIds[0]),
  578. }
  579. getResp, err := c.GetSecurityList(ctx, getReq)
  580. helpers.FatalIfError(err)
  581. // this security rule allows remote control the instance
  582. /*portRange := core.PortRange{
  583. Max: common.Int(1521),
  584. Min: common.Int(1521),
  585. }*/
  586. newRules := append(getResp.IngressSecurityRules, core.IngressSecurityRule{
  587. //Protocol: common.String("6"), // TCP
  588. Protocol: common.String("all"), // 允许所有协议
  589. Source: common.String("0.0.0.0/0"),
  590. /*TcpOptions: &core.TcpOptions{
  591. DestinationPortRange: &portRange, // 省略该参数,允许所有目标端口。
  592. },*/
  593. })
  594. updateReq := core.UpdateSecurityListRequest{
  595. SecurityListId: common.String(r.SecurityListIds[0]),
  596. }
  597. updateReq.IngressSecurityRules = newRules
  598. _, err = c.UpdateSecurityList(ctx, updateReq)
  599. helpers.FatalIfError(err)
  600. printf("Subnet创建成功: %s\n", *r.Subnet.DisplayName)
  601. return r.Subnet
  602. }
  603. // 列出指定虚拟云网络 (VCN) 中的所有子网,如果该 VCN 不存在会创建 VCN
  604. func listSubnets(ctx context.Context, c core.VirtualNetworkClient, vcnID *string) []core.Subnet {
  605. request := core.ListSubnetsRequest{
  606. CompartmentId: &config.CompartmentID,
  607. VcnId: vcnID,
  608. }
  609. r, err := c.ListSubnets(ctx, request)
  610. helpers.FatalIfError(err)
  611. return r.Items
  612. }
  613. // 创建一个新的虚拟云网络 (VCN) 或获取已经存在的虚拟云网络
  614. func createOrGetVcn(ctx context.Context, c core.VirtualNetworkClient) core.Vcn {
  615. vcnItems := listVcns(ctx, c)
  616. displayName := common.String(config.VcnDisplayName)
  617. if len(vcnItems) > 0 && *displayName == "" {
  618. return vcnItems[0]
  619. }
  620. for _, element := range vcnItems {
  621. if *element.DisplayName == config.VcnDisplayName {
  622. // VCN already created, return it
  623. return element
  624. }
  625. }
  626. // create a new VCN
  627. printf("开始创建VCN(没有可用的VCN,或指定的VCN不存在)\n")
  628. if *displayName == "" {
  629. displayName = common.String(time.Now().Format("vcn-20060102-1504"))
  630. }
  631. request := core.CreateVcnRequest{}
  632. request.CidrBlock = common.String("10.0.0.0/16")
  633. request.CompartmentId = common.String(config.CompartmentID)
  634. request.DisplayName = displayName
  635. request.DnsLabel = common.String("vcndns")
  636. r, err := c.CreateVcn(ctx, request)
  637. helpers.FatalIfError(err)
  638. printf("VCN创建成功: %s\n", *r.Vcn.DisplayName)
  639. return r.Vcn
  640. }
  641. // 列出所有虚拟云网络 (VCN)
  642. func listVcns(ctx context.Context, c core.VirtualNetworkClient) []core.Vcn {
  643. request := core.ListVcnsRequest{
  644. CompartmentId: &config.CompartmentID,
  645. }
  646. r, err := c.ListVcns(ctx, request)
  647. helpers.FatalIfError(err)
  648. return r.Items
  649. }
  650. // 创建或者获取 Internet 网关
  651. func createOrGetInternetGateway(c core.VirtualNetworkClient, vcnID *string) (gateway core.InternetGateway) {
  652. ctx := context.Background()
  653. //List Gateways
  654. listGWRequest := core.ListInternetGatewaysRequest{
  655. CompartmentId: &config.CompartmentID,
  656. VcnId: vcnID,
  657. }
  658. listGWRespone, err := c.ListInternetGateways(ctx, listGWRequest)
  659. if err != nil {
  660. printf("Internet gateway list error: %s\n", err.Error())
  661. }
  662. if len(listGWRespone.Items) >= 1 {
  663. //Gateway with name already exists
  664. gateway = listGWRespone.Items[0]
  665. } else {
  666. //Create new Gateway
  667. printf("开始创建Internet网关\n")
  668. enabled := true
  669. createGWDetails := core.CreateInternetGatewayDetails{
  670. CompartmentId: &config.CompartmentID,
  671. IsEnabled: &enabled,
  672. VcnId: vcnID,
  673. }
  674. createGWRequest := core.CreateInternetGatewayRequest{CreateInternetGatewayDetails: createGWDetails}
  675. createGWResponse, err := c.CreateInternetGateway(ctx, createGWRequest)
  676. if err != nil {
  677. printf("Internet gateway create error: %s\n", err.Error())
  678. }
  679. gateway = createGWResponse.InternetGateway
  680. printf("Internet网关创建成功: %s\n", *gateway.DisplayName)
  681. }
  682. return
  683. }
  684. // 创建或者获取路由表
  685. func createOrGetRouteTable(c core.VirtualNetworkClient, gatewayID, VcnID *string) (routeTable core.RouteTable) {
  686. ctx := context.Background()
  687. //List Route Table
  688. listRTRequest := core.ListRouteTablesRequest{
  689. CompartmentId: &config.CompartmentID,
  690. VcnId: VcnID,
  691. }
  692. listRTResponse, err := c.ListRouteTables(ctx, listRTRequest)
  693. if err != nil {
  694. printf("Route table list error: %s\n", err.Error())
  695. }
  696. cidrRange := "0.0.0.0/0"
  697. rr := core.RouteRule{
  698. NetworkEntityId: gatewayID,
  699. Destination: &cidrRange,
  700. DestinationType: core.RouteRuleDestinationTypeCidrBlock,
  701. }
  702. if len(listRTResponse.Items) >= 1 {
  703. //Default Route Table found and has at least 1 route rule
  704. if len(listRTResponse.Items[0].RouteRules) >= 1 {
  705. routeTable = listRTResponse.Items[0]
  706. //Default Route table needs route rule adding
  707. } else {
  708. printf("路由表未添加规则,开始添加Internet路由规则\n")
  709. updateRTDetails := core.UpdateRouteTableDetails{
  710. RouteRules: []core.RouteRule{rr},
  711. }
  712. updateRTRequest := core.UpdateRouteTableRequest{
  713. RtId: listRTResponse.Items[0].Id,
  714. UpdateRouteTableDetails: updateRTDetails,
  715. }
  716. updateRTResponse, err := c.UpdateRouteTable(ctx, updateRTRequest)
  717. if err != nil {
  718. printf("Error updating route table: %s\n", err)
  719. }
  720. printf("Internet路由规则添加成功\n")
  721. routeTable = updateRTResponse.RouteTable
  722. }
  723. } else {
  724. //No default route table found
  725. printf("Error could not find VCN default route table, VCN OCID: %s Could not find route table.\n", *VcnID)
  726. }
  727. return
  728. }
  729. // 获取符合条件系统镜像中的第一个
  730. func GetImage(ctx context.Context, c core.ComputeClient) (image core.Image, err error) {
  731. images := listImages(ctx, c)
  732. if len(images) > 0 {
  733. image = images[0]
  734. } else {
  735. err = fmt.Errorf("未找到[%s %s]的镜像, 或该镜像不支持[%s]", config.OperatingSystem, config.OperatingSystemVersion, config.Shape)
  736. }
  737. return
  738. }
  739. // 列出所有符合条件的系统镜像
  740. func listImages(ctx context.Context, c core.ComputeClient) []core.Image {
  741. request := core.ListImagesRequest{
  742. CompartmentId: common.String(config.CompartmentID),
  743. OperatingSystem: common.String(config.OperatingSystem),
  744. OperatingSystemVersion: common.String(config.OperatingSystemVersion),
  745. Shape: common.String(config.Shape),
  746. }
  747. r, err := c.ListImages(ctx, request)
  748. helpers.FatalIfError(err)
  749. return r.Items
  750. }
  751. // ListShapes Lists the shapes that can be used to launch an instance within the specified compartment.
  752. func listShapes(ctx context.Context, c core.ComputeClient, imageID *string) []core.Shape {
  753. request := core.ListShapesRequest{
  754. CompartmentId: common.String(config.CompartmentID),
  755. ImageId: imageID,
  756. }
  757. r, err := c.ListShapes(ctx, request)
  758. helpers.FatalIfError(err)
  759. if r.Items == nil || len(r.Items) == 0 {
  760. log.Fatalln("Invalid response from ListShapes")
  761. }
  762. return r.Items
  763. }
  764. // 列出符合条件的可用性域
  765. func ListAvailabilityDomains() []identity.AvailabilityDomain {
  766. c, err := identity.NewIdentityClientWithConfigurationProvider(provider)
  767. helpers.FatalIfError(err)
  768. setProxyOrNot(&c.BaseClient)
  769. req := identity.ListAvailabilityDomainsRequest{}
  770. compartmentID, err := provider.TenancyOCID()
  771. helpers.FatalIfError(err)
  772. req.CompartmentId = common.String(compartmentID)
  773. resp, err := c.ListAvailabilityDomains(context.Background(), req)
  774. helpers.FatalIfError(err)
  775. return resp.Items
  776. }
  777. func ListInstances(ctx context.Context, c core.ComputeClient) []core.Instance {
  778. compartmentID, err := provider.TenancyOCID()
  779. helpers.FatalIfError(err)
  780. req := core.ListInstancesRequest{
  781. CompartmentId: &compartmentID,
  782. }
  783. resp, err := c.ListInstances(ctx, req)
  784. helpers.FatalIfError(err)
  785. return resp.Items
  786. }
  787. func ListVnicAttachments(ctx context.Context, c core.ComputeClient, instanceId *string) ([]core.VnicAttachment, error) {
  788. compartmentID, err := provider.TenancyOCID()
  789. if err != nil {
  790. return nil, err
  791. }
  792. req := core.ListVnicAttachmentsRequest{CompartmentId: &compartmentID}
  793. if instanceId != nil && *instanceId != "" {
  794. req.InstanceId = instanceId
  795. }
  796. resp, err := c.ListVnicAttachments(ctx, req)
  797. return resp.Items, err
  798. }
  799. func GetVnic(ctx context.Context, c core.VirtualNetworkClient, vnicID *string) (core.Vnic, error) {
  800. req := core.GetVnicRequest{VnicId: vnicID}
  801. resp, err := c.GetVnic(ctx, req)
  802. if err != nil && resp.RawResponse != nil {
  803. err = errors.New(resp.RawResponse.Status)
  804. }
  805. return resp.Vnic, err
  806. }
  807. func listPublicIPs(ctx context.Context, c core.VirtualNetworkClient, ad *string) []core.PublicIp {
  808. com, err := provider.TenancyOCID()
  809. helpers.FatalIfError(err)
  810. req := core.ListPublicIpsRequest{
  811. Scope: core.ListPublicIpsScopeAvailabilityDomain,
  812. CompartmentId: &com,
  813. AvailabilityDomain: ad,
  814. }
  815. resp, err := c.ListPublicIps(ctx, req)
  816. helpers.FatalIfError(err)
  817. return resp.Items
  818. }
  819. // 终止实例
  820. func terminateInstance(ctx context.Context, c core.ComputeClient, id *string) {
  821. request := core.TerminateInstanceRequest{
  822. InstanceId: id,
  823. RequestMetadata: helpers.GetRequestMetadataWithDefaultRetryPolicy(),
  824. }
  825. _, err := c.TerminateInstance(ctx, request)
  826. helpers.FatalIfError(err)
  827. fmt.Println("terminating instance")
  828. // should retry condition check which returns a bool value indicating whether to do retry or not
  829. // it checks the lifecycle status equals to Terminated or not for this case
  830. shouldRetryFunc := func(r common.OCIOperationResponse) bool {
  831. if converted, ok := r.Response.(core.GetInstanceResponse); ok {
  832. return converted.LifecycleState != core.InstanceLifecycleStateTerminated
  833. }
  834. return true
  835. }
  836. pollGetRequest := core.GetInstanceRequest{
  837. InstanceId: id,
  838. RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc),
  839. }
  840. _, pollErr := c.GetInstance(ctx, pollGetRequest)
  841. helpers.FatalIfError(pollErr)
  842. fmt.Println("instance terminated")
  843. }
  844. // 删除虚拟云网络
  845. func deleteVcn(ctx context.Context, c core.VirtualNetworkClient, id *string) {
  846. request := core.DeleteVcnRequest{
  847. VcnId: id,
  848. RequestMetadata: helpers.GetRequestMetadataWithDefaultRetryPolicy(),
  849. }
  850. fmt.Println("deleteing VCN")
  851. _, err := c.DeleteVcn(ctx, request)
  852. helpers.FatalIfError(err)
  853. // should retry condition check which returns a bool value indicating whether to do retry or not
  854. // it checks the lifecycle status equals to Terminated or not for this case
  855. shouldRetryFunc := func(r common.OCIOperationResponse) bool {
  856. if serviceError, ok := common.IsServiceError(r.Error); ok && serviceError.GetHTTPStatusCode() == 404 {
  857. // resource been deleted, stop retry
  858. return false
  859. }
  860. if converted, ok := r.Response.(core.GetVcnResponse); ok {
  861. return converted.LifecycleState != core.VcnLifecycleStateTerminated
  862. }
  863. return true
  864. }
  865. pollGetRequest := core.GetVcnRequest{
  866. VcnId: id,
  867. RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc),
  868. }
  869. _, pollErr := c.GetVcn(ctx, pollGetRequest)
  870. if serviceError, ok := common.IsServiceError(pollErr); !ok ||
  871. (ok && serviceError.GetHTTPStatusCode() != 404) {
  872. // fail if the error is not service error or
  873. // if the error is service error and status code not equals to 404
  874. helpers.FatalIfError(pollErr)
  875. }
  876. fmt.Println("VCN deleted")
  877. }
  878. // 删除子网
  879. func deleteSubnet(ctx context.Context, c core.VirtualNetworkClient, id *string) {
  880. request := core.DeleteSubnetRequest{
  881. SubnetId: id,
  882. RequestMetadata: helpers.GetRequestMetadataWithDefaultRetryPolicy(),
  883. }
  884. _, err := c.DeleteSubnet(context.Background(), request)
  885. helpers.FatalIfError(err)
  886. fmt.Println("deleteing subnet")
  887. // should retry condition check which returns a bool value indicating whether to do retry or not
  888. // it checks the lifecycle status equals to Terminated or not for this case
  889. shouldRetryFunc := func(r common.OCIOperationResponse) bool {
  890. if serviceError, ok := common.IsServiceError(r.Error); ok && serviceError.GetHTTPStatusCode() == 404 {
  891. // resource been deleted
  892. return false
  893. }
  894. if converted, ok := r.Response.(core.GetSubnetResponse); ok {
  895. return converted.LifecycleState != core.SubnetLifecycleStateTerminated
  896. }
  897. return true
  898. }
  899. pollGetRequest := core.GetSubnetRequest{
  900. SubnetId: id,
  901. RequestMetadata: helpers.GetRequestMetadataWithCustomizedRetryPolicy(shouldRetryFunc),
  902. }
  903. _, pollErr := c.GetSubnet(ctx, pollGetRequest)
  904. if serviceError, ok := common.IsServiceError(pollErr); !ok ||
  905. (ok && serviceError.GetHTTPStatusCode() != 404) {
  906. // fail if the error is not service error or
  907. // if the error is service error and status code not equals to 404
  908. helpers.FatalIfError(pollErr)
  909. }
  910. fmt.Println("subnet deleted")
  911. }
  912. func printf(format string, a ...interface{}) {
  913. fmt.Printf("%s ", time.Now().Format("2006-01-02 15:04:05"))
  914. fmt.Printf(format, a...)
  915. }
  916. // 根据实例OCID获取公共IP
  917. func getInstancePublicIps(ctx context.Context, computeClient core.ComputeClient, networkClient core.VirtualNetworkClient, instanceId *string) (ips []string, err error) {
  918. vnicAttachments, attachmentsErr := ListVnicAttachments(ctx, computeClient, instanceId)
  919. if attachmentsErr != nil {
  920. err = errors.New("获取失败")
  921. return
  922. }
  923. for _, vnicAttachment := range vnicAttachments {
  924. vnic, vnicErr := GetVnic(ctx, networkClient, vnicAttachment.VnicId)
  925. if vnicErr != nil {
  926. printf("GetVnic error: %s\n", vnicErr.Error())
  927. continue
  928. }
  929. ips = append(ips, *vnic.PublicIp)
  930. }
  931. return
  932. }
  933. func sendMessage(name, text string) {
  934. if token != "" && chat_id != "" {
  935. data := url.Values{
  936. "parse_mode": {"Markdown"},
  937. "chat_id": {chat_id},
  938. "text": {"*甲骨文通知*\n名称: " + name + "\n" + "内容: " + text},
  939. }
  940. req, err := http.NewRequest(http.MethodPost, sendMessageUrl, strings.NewReader(data.Encode()))
  941. if err != nil {
  942. printf("\033[1;31mNewRequest Error: \033[0m%s\n", err.Error())
  943. }
  944. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  945. client := common.BaseClient{HTTPClient: &http.Client{}}
  946. setProxyOrNot(&client)
  947. resp, err := client.HTTPClient.Do(req)
  948. if err != nil {
  949. printf("\033[1;31mTelegram 消息提醒发送失败, Error: \033[0m%s\n", err.Error())
  950. } else {
  951. if resp.StatusCode != 200 {
  952. bodyBytes, err := ioutil.ReadAll(resp.Body)
  953. var error string
  954. if err != nil {
  955. error = err.Error()
  956. } else {
  957. error = string(bodyBytes)
  958. }
  959. printf("\033[1;31mTelegram 消息提醒发送失败, Error: \033[0m%s\n", error)
  960. }
  961. }
  962. }
  963. }
  964. func setProxyOrNot(client *common.BaseClient) {
  965. if proxy != "" {
  966. proxyURL, err := url.Parse(proxy)
  967. helpers.FatalIfError(err)
  968. client.HTTPClient = &http.Client{
  969. Transport: &http.Transport{
  970. Proxy: http.ProxyURL(proxyURL),
  971. },
  972. }
  973. }
  974. }