main.go 35 KB

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