yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/cloudpods/instance.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cloudpods 16 17 import ( 18 "context" 19 "fmt" 20 "time" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/pkg/errors" 24 "yunion.io/x/pkg/utils" 25 26 "yunion.io/x/onecloud/pkg/apis" 27 api "yunion.io/x/onecloud/pkg/apis/compute" 28 modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute" 29 "yunion.io/x/onecloud/pkg/mcclient/modules/identity" 30 "yunion.io/x/onecloud/pkg/mcclient/modules/logger" 31 "yunion.io/x/onecloud/pkg/mcclient/modules/monitor" 32 "yunion.io/x/onecloud/pkg/mcclient/modules/webconsole" 33 34 "yunion.io/x/cloudmux/pkg/cloudprovider" 35 "yunion.io/x/cloudmux/pkg/multicloud" 36 ) 37 38 type SInstance struct { 39 multicloud.SInstanceBase 40 CloudpodsTags 41 42 host *SHost 43 api.ServerDetails 44 } 45 46 func (self *SInstance) GetName() string { 47 return self.Name 48 } 49 50 func (self *SInstance) GetHostname() string { 51 return self.Hostname 52 } 53 54 func (self *SInstance) GetId() string { 55 return self.Id 56 } 57 58 func (self *SInstance) GetGlobalId() string { 59 return self.Id 60 } 61 62 func (self *SInstance) GetStatus() string { 63 return self.Status 64 } 65 66 func (self *SInstance) Refresh() error { 67 ins, err := self.host.zone.region.GetInstance(self.Id) 68 if err != nil { 69 return err 70 } 71 return jsonutils.Update(self, ins) 72 } 73 74 func (self *SInstance) GetCreatedAt() time.Time { 75 return self.CreatedAt 76 } 77 78 func (self *SInstance) GetExpiredAt() time.Time { 79 return self.ExpiredAt 80 } 81 82 func (self *SInstance) GetIHost() cloudprovider.ICloudHost { 83 return self.host 84 } 85 86 func (self *SInstance) GetIHostId() string { 87 return self.HostId 88 } 89 90 func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { 91 disks, err := self.host.zone.region.GetDisks("", self.Id) 92 if err != nil { 93 return nil, err 94 } 95 ret := []cloudprovider.ICloudDisk{} 96 for i := range disks { 97 disks[i].region = self.host.zone.region 98 ret = append(ret, &disks[i]) 99 } 100 return ret, nil 101 } 102 103 func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { 104 if len(self.Eip) > 0 { 105 eips, err := self.host.zone.region.GetEips(self.Id) 106 if err != nil { 107 return nil, err 108 } 109 for i := range eips { 110 eips[i].region = self.host.zone.region 111 return &eips[i], nil 112 } 113 return nil, cloudprovider.ErrNotFound 114 } 115 return nil, nil 116 } 117 118 func (self *SInstance) GetVcpuCount() int { 119 return self.VcpuCount 120 } 121 122 func (self *SInstance) GetVmemSizeMB() int { 123 return self.VmemSize 124 } 125 126 func (self *SInstance) GetBootOrder() string { 127 return self.BootOrder 128 } 129 130 func (self *SInstance) GetVga() string { 131 return self.Vga 132 } 133 134 func (self *SInstance) GetVdi() string { 135 return self.Vdi 136 } 137 138 func (self *SInstance) GetOsType() cloudprovider.TOsType { 139 return cloudprovider.TOsType(self.OsType) 140 } 141 142 func (self *SInstance) GetFullOsName() string { 143 return self.OsName 144 } 145 146 func (self *SInstance) GetBios() cloudprovider.TBiosType { 147 return cloudprovider.ToBiosType(self.Bios) 148 } 149 150 func (ins *SInstance) GetOsDist() string { 151 val, ok := ins.Metadata["os_distribution"] 152 if ok { 153 return val 154 } 155 return "" 156 } 157 158 func (ins *SInstance) GetOsVersion() string { 159 val, ok := ins.Metadata["os_version"] 160 if ok { 161 return val 162 } 163 return "" 164 } 165 166 func (ins *SInstance) GetOsLang() string { 167 val, ok := ins.Metadata["os_language"] 168 if ok { 169 return val 170 } 171 return "" 172 } 173 174 func (ins *SInstance) GetOsArch() string { 175 return ins.OsArch 176 } 177 178 func (self *SInstance) GetMachine() string { 179 return self.Machine 180 } 181 182 func (self *SInstance) GetInstanceType() string { 183 return self.InstanceType 184 } 185 186 func (self *SInstance) GetSecurityGroupIds() ([]string, error) { 187 ret := []string{} 188 for _, sec := range self.Secgroups { 189 ret = append(ret, sec.Id) 190 } 191 return ret, nil 192 } 193 194 func (self *SInstance) GetProjectId() string { 195 return self.TenantId 196 } 197 198 func (self *SInstance) AssignSecurityGroup(id string) error { 199 input := api.GuestAssignSecgroupInput{} 200 input.SecgroupId = id 201 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "assign-secgroup", input) 202 return err 203 } 204 205 func (self *SInstance) SetSecurityGroups(ids []string) error { 206 if self.Hypervisor == api.HYPERVISOR_ESXI { 207 return nil 208 } 209 input := api.GuestSetSecgroupInput{} 210 input.SecgroupIds = ids 211 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "set-secgroup", input) 212 return err 213 } 214 215 func (self *SInstance) GetHypervisor() string { 216 return api.HYPERVISOR_CLOUDPODS 217 } 218 219 func (self *SInstance) StartVM(ctx context.Context) error { 220 if self.Status == api.VM_RUNNING { 221 return nil 222 } 223 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "start", nil) 224 return err 225 } 226 227 func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { 228 if self.Status == api.VM_READY { 229 return nil 230 } 231 input := api.ServerStopInput{} 232 input.IsForce = opts.IsForce 233 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "stop", input) 234 return err 235 } 236 237 func (self *SInstance) DeleteVM(ctx context.Context) error { 238 if self.DisableDelete != nil && *self.DisableDelete { 239 input := api.ServerUpdateInput{} 240 disableDelete := false 241 input.DisableDelete = &disableDelete 242 self.host.zone.region.cli.update(&modules.Servers, self.Id, input) 243 } 244 return self.host.zone.region.cli.delete(&modules.Servers, self.Id) 245 } 246 247 func (self *SInstance) UpdateVM(ctx context.Context, name string) error { 248 if self.Name != name { 249 input := api.ServerUpdateInput{} 250 input.Name = name 251 self.host.zone.region.cli.update(&modules.Servers, self.Id, input) 252 return cloudprovider.WaitMultiStatus(self, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3) 253 } 254 return nil 255 } 256 257 func (self *SInstance) UpdateUserData(userData string) error { 258 input := api.ServerUserDataInput{} 259 input.UserData = userData 260 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "user-data", input) 261 return err 262 } 263 264 func (self *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { 265 input := api.ServerRebuildRootInput{} 266 input.ImageId = opts.ImageId 267 input.Password = opts.Password 268 if len(opts.PublicKey) > 0 { 269 keypairId, err := self.host.zone.region.syncKeypair(self.Name, opts.PublicKey) 270 if err != nil { 271 return "", errors.Wrapf(err, "syncKeypair") 272 } 273 input.KeypairId = keypairId 274 } 275 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "rebuild-root", input) 276 if err != nil { 277 return "", err 278 } 279 return self.DisksInfo[0].Id, nil 280 } 281 282 func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error { 283 input := api.ServerDeployInput{} 284 input.Password = password 285 if len(publicKey) > 0 { 286 keypairId, err := self.host.zone.region.syncKeypair(name, publicKey) 287 if err != nil { 288 return errors.Wrapf(err, "syncKeypair") 289 } 290 input.KeypairId = keypairId 291 } 292 293 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "deploy", input) 294 if err != nil { 295 return errors.Wrapf(err, "deploy") 296 } 297 return cloudprovider.WaitMultiStatus(self, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3) 298 } 299 300 func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error { 301 input := api.ServerChangeConfigInput{} 302 input.VmemSize = fmt.Sprintf("%dM", opts.MemoryMB) 303 input.VcpuCount = opts.Cpu 304 input.InstanceType = opts.InstanceType 305 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "change-config", input) 306 return err 307 } 308 309 func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { 310 s := self.host.zone.region.cli.s 311 resp, err := webconsole.WebConsole.DoServerConnect(s, self.Id, nil) 312 if err != nil { 313 return nil, errors.Wrapf(err, "DoServerConnect") 314 } 315 result := &cloudprovider.ServerVncOutput{ 316 Protocol: "cloudpods", 317 InstanceId: self.Id, 318 InstanceName: self.Name, 319 Hypervisor: api.HYPERVISOR_CLOUDPODS, 320 } 321 err = resp.Unmarshal(&result) 322 if err != nil { 323 return nil, errors.Wrapf(err, "resp.Unmarshal") 324 } 325 resp, err = identity.ServicesV3.GetSpecific(s, "common", "config", nil) 326 if err != nil { 327 return nil, errors.Wrapf(err, "GetSpecific") 328 } 329 result.ApiServer, _ = resp.GetString("config", "default", "api_server") 330 return result, nil 331 } 332 333 func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { 334 input := api.ServerAttachDiskInput{} 335 input.DiskId = diskId 336 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "attachdisk", input) 337 return err 338 } 339 340 func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { 341 input := api.ServerDetachDiskInput{} 342 input.DiskId = diskId 343 input.KeepDisk = true 344 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "detachdisk", input) 345 return err 346 } 347 348 func (self *SInstance) MigrateVM(hostId string) error { 349 input := api.GuestMigrateInput{} 350 input.PreferHost = hostId 351 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "migrate", input) 352 return err 353 } 354 355 func (self *SInstance) LiveMigrateVM(hostId string) error { 356 input := api.GuestLiveMigrateInput{} 357 input.PreferHost = hostId 358 skipCpuCheck := true 359 input.SkipCpuCheck = &skipCpuCheck 360 _, err := self.host.zone.region.perform(&modules.Servers, self.Id, "live-migrate", input) 361 return err 362 } 363 364 func (self *SInstance) GetError() error { 365 if utils.IsInStringArray(self.Status, []string{api.VM_DISK_FAILED, api.VM_SCHEDULE_FAILED, api.VM_NETWORK_FAILED}) { 366 return fmt.Errorf("vm create failed with status %s", self.Status) 367 } 368 if self.Status == api.VM_DEPLOY_FAILED { 369 params := map[string]interface{}{"obj_id": self.Id, "success": false} 370 actions := []apis.OpsLogDetails{} 371 self.host.zone.region.list(&logger.Actions, params, &actions) 372 if len(actions) > 0 { 373 return fmt.Errorf(actions[0].Notes) 374 } 375 return fmt.Errorf("vm create failed with status %s", self.Status) 376 } 377 return nil 378 } 379 380 func (self *SInstance) CreateInstanceSnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudInstanceSnapshot, error) { 381 return nil, cloudprovider.ErrNotImplemented 382 } 383 384 func (self *SInstance) GetInstanceSnapshot(idStr string) (cloudprovider.ICloudInstanceSnapshot, error) { 385 return nil, cloudprovider.ErrNotImplemented 386 } 387 388 func (self *SInstance) GetInstanceSnapshots() ([]cloudprovider.ICloudInstanceSnapshot, error) { 389 return nil, cloudprovider.ErrNotImplemented 390 } 391 392 func (self *SInstance) ResetToInstanceSnapshot(ctx context.Context, idStr string) error { 393 return cloudprovider.ErrNotImplemented 394 } 395 396 func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) { 397 return self.host.zone.region.SaveImage(self.Id, opts.Name, opts.Notes) 398 } 399 400 func (self *SRegion) SaveImage(id, imageName, notes string) (*SImage, error) { 401 input := api.ServerSaveImageInput{} 402 input.GenerateName = imageName 403 input.Notes = notes 404 resp, err := self.perform(&modules.Servers, id, "save-image", input) 405 if err != nil { 406 return nil, err 407 } 408 imageId, err := resp.GetString("image_id") 409 if err != nil { 410 return nil, err 411 } 412 caches, err := self.GetStoragecaches() 413 if err != nil { 414 return nil, errors.Wrapf(err, "GetStoragecaches") 415 } 416 if len(caches) == 0 { 417 return nil, fmt.Errorf("no storage cache found") 418 } 419 caches[0].region = self 420 image, err := self.GetImage(imageId) 421 if err != nil { 422 return nil, err 423 } 424 image.cache = &caches[0] 425 return image, nil 426 } 427 428 func (self *SInstance) AllocatePublicIpAddress() (string, error) { 429 return "", cloudprovider.ErrNotImplemented 430 } 431 432 func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { 433 servers, err := self.zone.region.GetInstances(self.Id) 434 if err != nil { 435 return nil, err 436 } 437 ret := []cloudprovider.ICloudVM{} 438 for i := range servers { 439 servers[i].host = self 440 ret = append(ret, &servers[i]) 441 } 442 return ret, nil 443 } 444 445 func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) { 446 ins, err := self.zone.region.GetInstance(id) 447 if err != nil { 448 return nil, err 449 } 450 ins.host = self 451 return ins, nil 452 } 453 454 func (self *SRegion) GetInstance(id string) (*SInstance, error) { 455 ins := &SInstance{} 456 return ins, self.cli.get(&modules.Servers, id, nil, ins) 457 } 458 459 func (self *SRegion) GetInstances(hostId string) ([]SInstance, error) { 460 params := map[string]interface{}{} 461 if len(hostId) > 0 { 462 params["host_id"] = hostId 463 } 464 ret := []SInstance{} 465 return ret, self.list(&modules.Servers, params, &ret) 466 } 467 468 func (self *SRegion) CreateInstance(hostId, hypervisor string, opts *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) { 469 input := api.ServerCreateInput{ 470 ServerConfigs: &api.ServerConfigs{}, 471 } 472 input.Name = opts.Name 473 input.Hostname = opts.Hostname 474 input.Description = opts.Description 475 input.InstanceType = opts.InstanceType 476 input.VcpuCount = opts.Cpu 477 input.VmemSize = opts.MemoryMB 478 input.Password = opts.Password 479 input.PublicIpBw = opts.PublicIpBw 480 input.PublicIpChargeType = string(opts.PublicIpChargeType) 481 input.ProjectId = opts.ProjectId 482 input.Metadata = opts.Tags 483 input.UserData = opts.UserData 484 input.PreferHost = hostId 485 input.Hypervisor = hypervisor 486 if len(input.UserData) > 0 { 487 input.EnableCloudInit = true 488 } 489 input.Secgroups = opts.ExternalSecgroupIds 490 if opts.BillingCycle != nil { 491 input.Duration = opts.BillingCycle.String() 492 } 493 input.Disks = append(input.Disks, &api.DiskConfig{ 494 Index: 0, 495 ImageId: opts.ExternalImageId, 496 DiskType: api.DISK_TYPE_SYS, 497 SizeMb: opts.SysDisk.SizeGB * 1024, 498 Backend: opts.SysDisk.StorageType, 499 Storage: opts.SysDisk.StorageExternalId, 500 }) 501 for idx, disk := range opts.DataDisks { 502 input.Disks = append(input.Disks, &api.DiskConfig{ 503 Index: idx + 1, 504 DiskType: api.DISK_TYPE_DATA, 505 SizeMb: disk.SizeGB * 1024, 506 Backend: disk.StorageType, 507 Storage: disk.StorageExternalId, 508 }) 509 } 510 input.Networks = append(input.Networks, &api.NetworkConfig{ 511 Index: 0, 512 Network: opts.ExternalNetworkId, 513 Address: opts.IpAddr, 514 }) 515 ins := &SInstance{} 516 return ins, self.create(&modules.Servers, input, ins) 517 } 518 519 type SMetricData struct { 520 Id string `json:"id"` 521 Time time.Time `json:"time"` 522 Value float64 `json:"value"` 523 } 524 525 func (cli *SCloudpodsClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 526 brandArr := []string{"OneCloud", "VMware"} 527 metrics := []SMetricData{} 528 for i := 0; i < len(brandArr); i++ { 529 params := map[string]interface{}{ 530 "metric_name": opts.MetricType, 531 "tags": map[string]interface{}{ 532 "brand": brandArr[i], 533 }, 534 } 535 onecloudObj, err := monitor.UnifiedMonitorManager.Get(cli.s, "simple-query", jsonutils.Marshal(params)) 536 if err != nil { 537 return nil, err 538 } 539 tmp := []SMetricData{} 540 onecloudObj.Unmarshal(&tmp, "values") 541 metrics = append(metrics, tmp...) 542 } 543 544 idWithMetric := map[string][]cloudprovider.MetricValue{} 545 for _, v := range metrics { 546 if _, isExist := idWithMetric[v.Id]; !isExist { 547 idWithMetric[v.Id] = []cloudprovider.MetricValue{{ 548 Timestamp: v.Time, 549 Value: v.Value}} 550 } else { 551 idWithMetric[v.Id] = append(idWithMetric[v.Id], cloudprovider.MetricValue{ 552 Timestamp: v.Time, 553 Value: v.Value}) 554 } 555 } 556 557 res := []cloudprovider.MetricValues{} 558 for _, metric := range metrics { 559 res = append(res, cloudprovider.MetricValues{ 560 Id: metric.Id, 561 MetricType: opts.MetricType, 562 Values: idWithMetric[metric.Id], 563 }) 564 } 565 return res, nil 566 }