func doOSList(c *cli.Context) { tmplName := "" if len(c.Args()) > 0 { tmplName = "/" + c.Args().Get(0) } resp, err := client.SendRequest("GET", "/template"+tmplName, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } if bytes.Contains(resp.Body, []byte("template-list")) { tmpls := lib.TemplateList{} assert(xml.Unmarshal(resp.Body, &tmpls)) outputResult(c, tmpls, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(tmpls) } else { tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "TEMPLATE_NAME"}, {Header: "TECHNOLOGY"}, {Header: "TYPE"}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } for _, e := range tmpls.Template { tbl.AddRow(e.Name, e.Technology, e.OSType) } tbl.Print() } }) } else { tmpl := lib.Template{} assert(xml.Unmarshal(resp.Body, &tmpl)) outputResult(c, tmpl, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(tmpl) } else { tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "TEMPLATE_NAME"}, {Header: "TECHNOLOGY"}, {Header: "TYPE"}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } tbl.AddRow(tmpl.Name, tmpl.Technology, tmpl.OSType) tbl.Print() } }) } }
func doRecreate(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) path := "/ve/" + vename + "/recreate" var q []string if len(c.String("template")) > 0 { q = append(q, "template="+c.String("template")) } if c.Bool("drop-apps") { q = append(q, "drop-apps=true") } if len(q) > 0 { path += "?" + strings.Join(q, "&") } resp, err := client.SendRequest("POST", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } pwd := lib.PasswordResponse{} assert(xml.Unmarshal(resp.Body, &pwd)) outputResult(c, pwd, func(format string) { lib.PrintXMLStruct(pwd) }) }
func doClone(c *cli.Context) { if len(c.Args()) < 2 { displayWrongNumOfArgsAndExit(c) } srcve := c.Args().Get(0) destve := c.Args().Get(1) path := "/ve/" + srcve + "/clone-to/" + destve if c.Int("subscription-id") > 0 { path += "/for/" + strconv.Itoa(c.Int("subscription-id")) } resp, err := client.SendRequest("POST", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } pwd := lib.PasswordResponse{} assert(xml.Unmarshal(resp.Body, &pwd)) outputResult(c, pwd, func(format string) { lib.PrintXMLStruct(pwd) }) }
func doLbCreate(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } lbname := c.Args().Get(0) path := "/load-balancer" if c.Int("subscription-id") > 0 { path += "/" + strconv.Itoa(c.Int("subscription-id")) } path += "/create/" + lbname resp, err := client.SendRequest("POST", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } pwd := lib.PasswordResponse{} assert(xml.Unmarshal(resp.Body, &pwd)) outputResult(c, pwd, func(format string) { lib.PrintXMLStruct(pwd) }) }
func doHistory(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) path := "/ve/" + vename + "/history/" if len(c.String("from")) > 0 && len(c.String("to")) > 0 { _, err := lib.ParseArgTimestampFormat(c.String("from")) assert(err, "'from' arg value must be in "+lib.ArgTimestampFormatStr()+" format") _, err = lib.ParseArgTimestampFormat(c.String("to")) assert(err, "'to' arg value must be in "+lib.ArgTimestampFormatStr()+" format") path += c.String("from") + "/" + c.String("to") } else if c.Int("num-records") > 0 { path += strconv.Itoa(c.Int("num-records")) } else { displayErrorAndExit("This command must be used with a pair of --from and --to flags arguments or --num-records flag argument. Please see '" + c.App.Name + " help " + c.Command.Name + "'") } resp, err := client.SendRequest("GET", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } hst := lib.VeHistory{} assert(xml.Unmarshal(resp.Body, &hst)) assert(err) outputResult(c, hst, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(hst) } else { tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "DATETIME"}, {Header: "CPU", AlignRight: true}, {Header: "MEMORY", AlignRight: true}, {Header: "DISK", AlignRight: true}, {Header: "BANDWIDTH", AlignRight: true}, {Header: "PUB_IPS", AlignRight: true}, {Header: "STATUS"}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } for _, e := range hst.VeSnapshot { ts, _ := e.EventTimestamp.MarshalText() tbl.AddRow(ts, e.CPU, e.RAM, e.LocalDisk, e.Bandwidth, e.NoOfPublicIP, e.State) } tbl.Print() } }) }
func doUsage(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) if len(c.String("from")) == 0 || len(c.String("to")) == 0 { displayErrorAndExit("This command must be used with a pair of --from and --to flags arguments. Please see '" + c.App.Name + " help " + c.Command.Name + "'") } path := "/ve/" + vename + "/usage/" from, err := lib.ParseArgTimestampFormat(c.String("from")) assert(err, "'from' arg value must be in "+lib.ArgTimestampFormatStr()+" format") to, err := lib.ParseArgTimestampFormat(c.String("to")) assert(err, "'to' arg value must be in "+lib.ArgTimestampFormatStr()+" format") path += c.String("from") + "/" + c.String("to") resp, err := client.SendRequest("GET", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } usage := lib.VeResourceUsageReport{} assert(xml.Unmarshal(resp.Body, &usage)) outputResult(c, usage, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(usage) } else { fmt.Println("RESOURCE USAGE REPORT") fmt.Printf("Server: %s\n", usage.VeName) fmt.Printf(" From: %s\n", from.Format(lib.DataTimestampFormat)) fmt.Printf(" To: %s\n\n", to.Format(lib.DataTimestampFormat)) tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "RESOURCE_TYPE"}, {Header: "USAGE", AlignRight: true}, }...) assert(err) for _, e := range usage.ResourceUsage { name := e.ResourceType if len(e.ResourceUsageType) > 0 { name += "(" + e.ResourceUsageType + ")" } tbl.AddRow(name, e.Value) } for _, e := range usage.VeTraffic { tbl.AddRow(e.TrafficType, e.Used) } tbl.Print() } }) }
func doLbHistory(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } lbname := c.Args().Get(0) if c.Int("num-records") <= 0 { displayErrorAndExit("This command must be used with --num-records flag and its argument. Please see '" + c.App.Name + " help " + c.Command.Name + "'") } numRecord := strconv.Itoa(c.Int("num-records")) resp, err := client.SendRequest("GET", "/load-balancer/"+lbname+"/history/"+numRecord, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } hst := lib.VeHistory{} assert(xml.Unmarshal(resp.Body, &hst)) outputResult(c, hst, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(hst) } else { tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "DATETIME"}, {Header: "CPU", AlignRight: true}, {Header: "MEMORY", AlignRight: true}, {Header: "DISK", AlignRight: true}, {Header: "BANDWIDTH", AlignRight: true}, {Header: "PUB_IPS", AlignRight: true}, {Header: "STATUS"}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } for _, e := range hst.VeSnapshot { ts, _ := e.EventTimestamp.MarshalText() tbl.AddRow(ts, e.CPU, e.RAM, e.LocalDisk, e.Bandwidth, e.NoOfPublicIP, e.State) } tbl.Print() } }) }
func doImageInfo(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } imgname := c.Args().Get(0) resp, err := client.SendRequest("GET", "/image/"+imgname, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } img := lib.VeImage{} assert(xml.Unmarshal(resp.Body, &img)) outputResult(c, img, func(format string) { lib.PrintXMLStruct(img) }) }
func doAutoscale(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) resp, err := client.SendRequest("GET", "/ve/"+vename+"/autoscale", nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } autoscale := lib.Autoscale{} assert(xml.Unmarshal(resp.Body, &autoscale)) outputResult(c, autoscale, func(format string) { lib.PrintXMLStruct(autoscale) }) }
func doInitiatingVnc(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) resp, err := client.SendRequest("POST", "/ve/"+vename+"/console", nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } pwd := lib.PasswordResponse{} assert(xml.Unmarshal(resp.Body, &pwd)) outputResult(c, pwd, func(format string) { lib.PrintXMLStruct(pwd) }) }
func doLbInfo(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } lbname := c.Args().Get(0) resp, err := client.SendRequest("GET", "/load-balancer/"+lbname, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } lb := lib.LoadBalancer{} assert(xml.Unmarshal(resp.Body, &lb)) outputResult(c, lb, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(lb) } else { var publicIP lib.IPAddr if len(lb.Network.PublicIP) > 0 { publicIP = lb.Network.PublicIP[0].Address } fmt.Println("LOAD BALANCER INFO") fmt.Printf(" Name: %s\n", lb.Name) fmt.Printf(" Subscription ID: %d\n", lb.SubscriptionID) fmt.Printf("Public IP address: %s\n", publicIP) fmt.Printf(" Status: %s\n\n", lb.State) fmt.Println("BALANCED SERVERS") tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "NAME"}, {Header: "IPADDR"}, }...) assert(err) for _, e := range lb.UsedBy { tbl.AddRow(e.VeName, e.IP) } tbl.Print() } }) }
func doApplicationInfo(c *cli.Context) { if len(c.Args()) < 2 { displayWrongNumOfArgsAndExit(c) } appname := c.Args().Get(0) foros := c.Args().Get(1) resp, err := client.SendRequest("GET", "/application-template/"+appname+"/"+foros, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } app := lib.ApplicationTemplate{} assert(xml.Unmarshal(resp.Body, &app)) outputResult(c, app, func(format string) { lib.PrintXMLStruct(app) }) }
func doBackupInfo(c *cli.Context) { if len(c.Args()) < 2 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) backupid := getBackupID(c.Args().Get(1)) resp, err := client.SendRequest("GET", "/ve/"+vename+"/backup/"+backupid, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } backup := lib.Backup{} assert(xml.Unmarshal(resp.Body, &backup)) outputResult(c, backup, func(format string) { lib.PrintXMLStruct(backup) }) }
func doCreate(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) var ve lib.CreateVe if len(c.String("setting-file")) > 0 { assert(lib.LoadConfig(c.String("setting-file"), &ve)) ve.Name = vename } else { if s, ok := conf.Servers[vename]; ok && s.Spec != nil { ve = *s.Spec } else { cli.ShowCommandHelp(c, c.Command.Name) os.Exit(1) } } if len(ve.Hostname) == 0 { ve.Hostname = ve.Name } var b bytes.Buffer assert(xml.NewEncoder(&b).Encode(ve)) resp, err := client.SendRequest("POST", "/ve/", &b) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } pwd := lib.PasswordResponse{} assert(xml.Unmarshal(resp.Body, &pwd)) outputResult(c, pwd, func(format string) { lib.PrintXMLStruct(pwd) }) }
func doAutoscaleCreateUpdate(c *cli.Context) { method := "PUT" if c.Command.Name == "autoscale-create" { method = "POST" } if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) var data lib.AutoscaleData if len(c.String("setting-file")) > 0 { assert(lib.LoadConfig(c.String("setting-file"), &data)) } else { if s, ok := conf.Servers[vename]; ok && len(s.AutoscaleRule) > 0 { data = lib.AutoscaleData{AutoscaleRule: s.AutoscaleRule} } else { displayErrorAndExit("Couldn't find Autoscale rules for '" + vename + "'") } } var b bytes.Buffer assert(xml.NewEncoder(&b).Encode(data)) resp, err := client.SendRequest(method, "/ve/"+vename+"/autoscale", &b) assert(err) if resp.StatusCode != 200 { displayErrorAndExit(string(resp.Body)) } autoscale := lib.Autoscale{} assert(xml.Unmarshal(resp.Body, &autoscale)) outputResult(c, autoscale, func(format string) { lib.PrintXMLStruct(autoscale) }) }
func doBackupList(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) if len(c.String("from")) == 0 || len(c.String("to")) == 0 { displayErrorAndExit("This command must be used with a pair of --from and --to flags arguments. Please see '" + c.App.Name + " help " + c.Command.Name + "'") } path := "/ve/" + vename + "/backups/" from, err := lib.ParseArgTimestampFormat(c.String("from")) assert(err, "'from' arg value must be in "+lib.ArgTimestampFormatStr()+" format") to, err := lib.ParseArgTimestampFormat(c.String("to")) assert(err, "'to' arg value must be in "+lib.ArgTimestampFormatStr()+" format") path += c.String("from") + "/" + c.String("to") resp, err := client.SendRequest("GET", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } backups := lib.VeBackups{} assert(xml.Unmarshal(resp.Body, &backups)) outputResult(c, backups, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(backups) } else { fmt.Println("BACKUP LIST") fmt.Printf("Server: %s\n", vename) fmt.Printf(" From: %s\n", from.Format(lib.DataTimestampFormat)) fmt.Printf(" To: %s\n\n", to.Format(lib.DataTimestampFormat)) tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "ID"}, {Header: "SCHEDULE"}, {Header: "START"}, {Header: "END"}, {Header: "RESULT", AlignRight: true}, {Header: "SIZE(GB)", AlignRight: true}, {Header: "NODE"}, {Header: "DESCRIPTION"}, }...) assert(err) for _, e := range backups.Backup { schedule := "-" if len(e.ScheduleName) > 0 { schedule = e.ScheduleName } result := "fail" if e.Successful == true { result = "ok" } size := strconv.FormatFloat(float64(e.BackupSize)/(1<<30), 'f', 3, 64) tbl.AddRow(e.CloudBackupID, schedule, e.Started, e.Ended, result, size, e.BackupNodeName, e.Description) } tbl.Print() } }) }
func doAutoscaleHistory(c *cli.Context) { if len(c.Args()) == 0 { displayWrongNumOfArgsAndExit(c) } vename := c.Args().Get(0) path := "/ve/" + vename + "/autoscale/history/" if len(c.String("from")) > 0 && len(c.String("to")) > 0 { _, err := lib.ParseArgTimestampFormat(c.String("from")) assert(err, "'from' arg value must be in "+lib.ArgTimestampFormatStr()+" format") _, err = lib.ParseArgTimestampFormat(c.String("to")) assert(err, "'to' arg value must be in "+lib.ArgTimestampFormatStr()+" format") path += c.String("from") + "/" + c.String("to") var q []string if c.Int("average-period") > 0 { q = append(q, "average-period="+strconv.Itoa(c.Int("average-period"))) } if c.Int("tail") > 0 { q = append(q, "tail="+strconv.Itoa(c.Int("tail"))) } if len(q) > 0 { path += "?" + strings.Join(q, "&") } } else if c.Int("num-records") > 0 { path += strconv.Itoa(c.Int("num-records")) } else { displayErrorAndExit("This command must be used with a pair of --from and --to flags arguments or --num-records flag argument. Please see '" + c.App.Name + " help " + c.Command.Name + "'") } resp, err := client.SendRequest("GET", path, nil) assert(err) if resp.StatusCode >= 400 { displayErrorAndExit(string(resp.Body)) } hst := lib.ResourceConsumptionAndAutoscaleHistory{} assert(xml.Unmarshal(resp.Body, &hst)) assert(err) outputResult(c, hst, func(format string) { if c.Bool("verbose") { lib.PrintXMLStruct(hst) } else { if len(hst.AutoscaleRule) > 0 { fmt.Println("AUTOSCALE RULE HISTORY") tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "METRIC"}, {Header: "VERSION", AlignRight: true}, {Header: "UPDATED"}, {Header: "DELIVERED"}, {Header: "DELIVERED-OK"}, {Header: "MIGRATION"}, {Header: "RESTART"}, {Header: "MIN", AlignRight: true}, {Header: "MAX", AlignRight: true}, {Header: "STEP", AlignRight: true}, {Header: "UP_THRES", AlignRight: true}, {Header: "UP_PERIOD", AlignRight: true}, {Header: "DOWN_THRES", AlignRight: true}, {Header: "DOWN_PERIOD", AlignRight: true}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } for _, e := range hst.AutoscaleRule { tbl.AddRow( e.Metric, *e.Version, *e.Updated, *e.UpdateDelivered, *e.UpdateDeliveredOk, *e.AllowMigration, *e.AllowRestart, e.Limits.Min, e.Limits.Max, e.Limits.Step, *e.Thresholds.Up.Threshold, e.Thresholds.Up.Period, *e.Thresholds.Down.Threshold, e.Thresholds.Down.Period, ) } tbl.Print() fmt.Println() } fmt.Println("RESOURCE CONSUMPTION") tbl, err := prettytable.NewTable([]prettytable.Column{ {Header: "CPU_USAGE", AlignRight: true}, {Header: "RAM_USAGE", AlignRight: true}, {Header: "PRIV_IN", AlignRight: true}, {Header: "PRIV_OUT", AlignRight: true}, {Header: "PUB_IN", AlignRight: true}, {Header: "PUB_OUT", AlignRight: true}, {Header: "DATETIME"}, {Header: "CPU", AlignRight: true}, {Header: "RAM", AlignRight: true}, {Header: "BANDWIDTH", AlignRight: true}, }...) assert(err) if c.Bool("no-header") { tbl.NoHeader = true } for _, e := range hst.ResourceConsumptionSample { tbl.AddRow( e.CPUUsage, e.RAMUsage, e.PrivateIncomingTraffic, e.PrivateOutgoingTraffic, e.PublicIncomingTraffic, e.PublicOutgoingTraffic, e.PaciTimestamp, e.CPU, e.RAM, e.Bandwidth, ) } tbl.Print() } }) }