From 935cd5dcac751f9717fb804880d90c7b9b7d7aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Mon, 24 Dec 2018 21:00:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8hypervisor?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/climc/shell/cloudaccounts.go | 2 +- pkg/compute/models/guests.go | 14 +- pkg/util/openstack/disk.go | 288 ++++++++++++ pkg/util/openstack/hostv2.go | 71 ++- pkg/util/openstack/hostv3.go | 30 +- pkg/util/openstack/instance.go | 543 ++++++++++++++++++++++ pkg/util/openstack/instancenic.go | 53 +++ pkg/util/openstack/network.go | 157 +++++++ pkg/util/openstack/openstack.go | 59 ++- pkg/util/openstack/port.go | 69 +++ pkg/util/openstack/provider/provider.go | 21 +- pkg/util/openstack/region.go | 257 +++++----- pkg/util/openstack/securitygroup.go | 185 ++++++++ pkg/util/openstack/shell/disk.go | 33 ++ pkg/util/openstack/shell/instance.go | 45 ++ pkg/util/openstack/shell/instancenic.go | 20 + pkg/util/openstack/shell/network.go | 33 ++ pkg/util/openstack/shell/securitygroup.go | 32 ++ pkg/util/openstack/shell/service.go | 20 - pkg/util/openstack/shell/snapshot.go | 34 ++ pkg/util/openstack/shell/storage.go | 27 ++ pkg/util/openstack/shell/version.go | 2 +- pkg/util/openstack/shell/vpc.go | 32 ++ pkg/util/openstack/snapshot.go | 148 ++++++ pkg/util/openstack/storage.go | 111 +++++ pkg/util/openstack/storagecache.go | 85 ++++ pkg/util/openstack/vpc.go | 217 +++++++++ pkg/util/openstack/wire.go | 99 ++++ pkg/util/openstack/zone.go | 113 +++-- pkg/webconsole/session/remote_console.go | 40 +- 30 files changed, 2610 insertions(+), 230 deletions(-) create mode 100644 pkg/util/openstack/disk.go create mode 100644 pkg/util/openstack/instance.go create mode 100644 pkg/util/openstack/instancenic.go create mode 100644 pkg/util/openstack/network.go create mode 100644 pkg/util/openstack/port.go create mode 100644 pkg/util/openstack/securitygroup.go create mode 100644 pkg/util/openstack/shell/disk.go create mode 100644 pkg/util/openstack/shell/instance.go create mode 100644 pkg/util/openstack/shell/instancenic.go create mode 100644 pkg/util/openstack/shell/network.go create mode 100644 pkg/util/openstack/shell/securitygroup.go delete mode 100644 pkg/util/openstack/shell/service.go create mode 100644 pkg/util/openstack/shell/snapshot.go create mode 100644 pkg/util/openstack/shell/storage.go create mode 100644 pkg/util/openstack/shell/vpc.go create mode 100644 pkg/util/openstack/snapshot.go create mode 100644 pkg/util/openstack/storage.go create mode 100644 pkg/util/openstack/storagecache.go create mode 100644 pkg/util/openstack/vpc.go create mode 100644 pkg/util/openstack/wire.go diff --git a/cmd/climc/shell/cloudaccounts.go b/cmd/climc/shell/cloudaccounts.go index 8c004dae37..1d744f6ceb 100644 --- a/cmd/climc/shell/cloudaccounts.go +++ b/cmd/climc/shell/cloudaccounts.go @@ -34,7 +34,7 @@ func init() { NAME string `help:"Name of cloud account"` ACCOUNT string `help:"Account to access the cloud account"` SECRET string `help:"Secret to access the cloud account, clientId/clientScret for Azure"` - PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud"` + PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud|OpenStack"` AccessURL string `helo:"hello" metavar:"Azure choices: "` Desc string `help:"Description"` Enabled bool `help:"Enabled the account automatically"` diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index e52f0d1740..3bfef2176a 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -129,11 +129,12 @@ const ( HYPERVISOR_HYPERV = "hyperv" HYPERVISOR_XEN = "xen" - HYPERVISOR_ALIYUN = "aliyun" - HYPERVISOR_QCLOUD = "qcloud" - HYPERVISOR_AZURE = "azure" - HYPERVISOR_AWS = "aws" - HYPERVISOR_HUAWEI = "huawei" + HYPERVISOR_ALIYUN = "aliyun" + HYPERVISOR_QCLOUD = "qcloud" + HYPERVISOR_AZURE = "azure" + HYPERVISOR_AWS = "aws" + HYPERVISOR_HUAWEI = "huawei" + HYPERVISOR_OPENSTACK = "openstack" // HYPERVISOR_DEFAULT = HYPERVISOR_KVM HYPERVISOR_DEFAULT = HYPERVISOR_KVM @@ -151,6 +152,7 @@ var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_AWS, HYPERVISOR_QCLOUD, HYPERVISOR_HUAWEI, + HYPERVISOR_OPENSTACK, } var PUBLIC_CLOUD_HYPERVISORS = []string{ @@ -173,6 +175,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{ HYPERVISOR_AWS: HOST_TYPE_AWS, HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD, HYPERVISOR_HUAWEI: HOST_TYPE_HUAWEI, + HYPERVISOR_OPENSTACK: HOST_TYPE_OPENSTACK, } var HOSTTYPE_HYPERVISOR = map[string]string{ @@ -185,6 +188,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{ HOST_TYPE_AWS: HYPERVISOR_AWS, HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD, HOST_TYPE_HUAWEI: HYPERVISOR_HUAWEI, + HOST_TYPE_OPENSTACK: HYPERVISOR_OPENSTACK, } type SGuestManager struct { diff --git a/pkg/util/openstack/disk.go b/pkg/util/openstack/disk.go new file mode 100644 index 0000000000..1dc6d9db3a --- /dev/null +++ b/pkg/util/openstack/disk.go @@ -0,0 +1,288 @@ +package openstack + +import ( + "context" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +const ( + DISK_STATUS_CREATING = "creating" // The volume is being created. + + DISK_STATUS_ATTACHING = "attaching" // The volume is attaching to an instance. + DISK_STATUS_DETACHING = "detaching" // The volume is detaching from an instance. + DISK_STATUS_EXTENDING = "extending" // The volume is being extended. + DISK_STATUS_DELETING = "deleting" // The volume is being deleted. + + DISK_STATUS_RETYPING = "retyping" // The volume is changing type to another volume type. + DISK_STATUS_AVAILABLE = "available" // The volume is ready to attach to an instance. + DISK_STATUS_RESERVED = "reserved" // The volume is reserved for attaching or shelved. + DISK_STATUS_IN_USE = "in-use" // The volume is attached to an instance. + DISK_STATUS_MAINTENANCE = "maintenance" // The volume is locked and being migrated. + DISK_STATUS_AWAITING_TRANSFER = "awaiting-transfer" // The volume is awaiting for transfer. + DISK_STATUS_BACKING_UP = "backing-up" // The volume is being backed up. + DISK_STATUS_RESTORING_BACKUP = "restoring-backup" // A backup is being restored to the volume. + DISK_STATUS_DOWNLOADING = "downloading" // The volume is downloading an image. + DISK_STATUS_UPLOADING = "uploading" // The volume is being uploaded to an image. + + DISK_STATUS_ERROR = "error" // A volume creation error occurred. + DISK_STATUS_ERROR_DELETING = "error_deleting" // A volume deletion error occurred. + DISK_STATUS_ERROR_BACKING_UP = "error_backing-up" // A backup error occurred. + DISK_STATUS_ERROR_RESTORING = "error_restoring" // A backup restoration error occurred. + DISK_STATUS_ERROR_EXTENDING = "error_extending" // An error occurred while attempting to extend a volume. + +) + +type Attachment struct { + ServerID string + AttachmentID string + HostName string + VolumeID string + Device string + ID string +} + +type Link struct { + Href string + Rel string +} + +type Metadata map[string]string + +type VolumeImageMetadata struct { + Checksum string + MinRAM int + DiskFormat string + ImageName string + ImageID string + ContainerFormat string + MinDisk int + Size int +} + +type SDisk struct { + storage *SStorage + + ID string + Name string + + MigrationStatus string + Attachments []Attachment + Links []Link + + AvailabilityZone string + //os-vol-host-attr:host difleming@lvmdriver-1#lvmdriver-1 + Encrypted bool + ReplicationStatus string + SnapshotID string + Size int + UserID string + //os-vol-tenant-attr:tenant_id bab7d5c60cd041a0a36f7c4b6e1dd978 + //os-vol-mig-status-attr:migstat null, + Metadata Metadata + + Status string + Description string + Multiattach string + SourceVolid string + ConsistencygroupID string + VolumeImageMetadata VolumeImageMetadata + //os-vol-mig-status-attr:name_id null, + Bootable bool + //CreatedAt time.Time + VolumeType string +} + +func (disk *SDisk) GetMetadata() *jsonutils.JSONDict { + data := jsonutils.NewDict() + + data.Add(jsonutils.NewString(models.HYPERVISOR_OPENSTACK), "hypervisor") + return data +} + +func (region *SRegion) GetDisks(category string) ([]SDisk, error) { + _, resp, err := region.CinderGet("/volumes/detail", "", nil) + if err != nil { + return nil, err + } + disks := []SDisk{} + if err := resp.Unmarshal(&disks, "volumes"); err != nil { + return nil, err + } + result := []SDisk{} + for _, disk := range disks { + if len(category) == 0 || disk.VolumeType == category { + result = append(result, disk) + } + } + return result, nil +} + +func (disk *SDisk) GetId() string { + return disk.ID +} + +func (disk *SDisk) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SDisk) Resize(ctx context.Context, sizeMb int64) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SDisk) GetName() string { + if len(disk.Name) > 0 { + return disk.Name + } + return disk.ID +} + +func (disk *SDisk) GetGlobalId() string { + return disk.ID +} + +func (disk *SDisk) IsEmulated() bool { + return false +} + +func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return disk.storage, nil +} + +func (disk *SDisk) GetStatus() string { + switch disk.Status { + case DISK_STATUS_CREATING: + return models.DISK_ALLOCATING + case DISK_STATUS_ATTACHING: + return models.DISK_ATTACHING + case DISK_STATUS_DETACHING: + return models.DISK_DETACHING + case DISK_STATUS_EXTENDING: + return models.DISK_RESIZING + case DISK_STATUS_RETYPING, DISK_STATUS_AVAILABLE, DISK_STATUS_RESERVED, DISK_STATUS_IN_USE, DISK_STATUS_MAINTENANCE, DISK_STATUS_AWAITING_TRANSFER, DISK_STATUS_BACKING_UP, DISK_STATUS_RESTORING_BACKUP, DISK_STATUS_DOWNLOADING, DISK_STATUS_UPLOADING: + return models.DISK_READY + case DISK_STATUS_DELETING: + return models.DISK_DEALLOC + default: + return models.DISK_UNKNOWN + } +} + +func (disk *SDisk) Refresh() error { + new, err := disk.storage.zone.region.GetDisk(disk.ID) + if err != nil { + return err + } + return jsonutils.Update(disk, new) +} + +func (disk *SDisk) ResizeDisk(newSize int64) error { + return disk.storage.zone.region.ResizeDisk(disk.ID, newSize) +} + +func (disk *SDisk) GetDiskFormat() string { + return "lvm" +} + +func (disk *SDisk) GetDiskSizeMB() int { + return disk.Size * 1024 +} + +func (disk *SDisk) GetIsAutoDelete() bool { + return false +} + +func (disk *SDisk) GetTemplateId() string { + return disk.VolumeImageMetadata.ImageID +} + +func (disk *SDisk) GetDiskType() string { + if disk.Bootable { + return models.DISK_TYPE_SYS + } + return models.DISK_TYPE_DATA +} + +func (disk *SDisk) GetFsFormat() string { + return "" +} + +func (disk *SDisk) GetIsNonPersistent() bool { + return false +} + +func (disk *SDisk) GetDriver() string { + return "scsi" +} + +func (disk *SDisk) GetCacheMode() string { + return "none" +} + +func (disk *SDisk) GetMountpoint() string { + return "" +} + +func (disk *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, desc string) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (region *SRegion) GetDisk(diskId string) (*SDisk, error) { + _, resp, err := region.CinderGet("/volumes/"+diskId, "", nil) + if err != nil { + return nil, err + } + disk := &SDisk{} + return disk, resp.Unmarshal(disk, "volume") +} + +func (disk *SRegion) DeleteDisk(diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SRegion) ResizeDisk(diskId string, sizeMb int64) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SRegion) ResetDisk(diskId, snapshotId string) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (disk *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + return disk.storage.zone.region.GetISnapshotById(snapshotId) +} + +func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + return disk.storage.zone.region.GetSnapshots(disk.ID) +} + +func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { + return "", disk.storage.zone.region.ResetDisk(disk.ID, snapshotId) +} + +func (disk *SDisk) GetBillingType() string { + return models.BILLING_TYPE_POSTPAID +} + +func (disk *SDisk) GetExpiredAt() time.Time { + return time.Time{} +} + +func (disk *SDisk) GetAccessPath() string { + return "" +} + +func (disk *SDisk) Rebuild(ctx context.Context) error { + return disk.storage.zone.region.RebuildDisk(disk.ID) +} + +func (region *SRegion) RebuildDisk(diskId string) error { + return cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/hostv2.go b/pkg/util/openstack/hostv2.go index 6ed3cc6539..ca6a3091a4 100644 --- a/pkg/util/openstack/hostv2.go +++ b/pkg/util/openstack/hostv2.go @@ -10,7 +10,7 @@ import ( ) type SResource struct { - CPU int + CPU int8 DiskGB int Host string MemoryMb int @@ -42,7 +42,7 @@ func (host *SHostV2) GetMetadata() *jsonutils.JSONDict { } func (host *SHostV2) GetIWires() ([]cloudprovider.ICloudWire, error) { - return nil, cloudprovider.ErrNotImplemented + return host.zone.GetIWires() } func (host *SHostV2) GetIStorages() ([]cloudprovider.ICloudStorage, error) { @@ -54,11 +54,25 @@ func (host *SHostV2) GetIStorageById(id string) (cloudprovider.ICloudStorage, er } func (host *SHostV2) GetIVMs() ([]cloudprovider.ICloudVM, error) { - return nil, cloudprovider.ErrNotImplemented + instances, err := host.zone.region.GetInstances(host.zone.ZoneName, host.HostName) + if err != nil { + return nil, err + } + iVMs := []cloudprovider.ICloudVM{} + for i := 0; i < len(instances); i++ { + instances[i].hostV2 = host + iVMs = append(iVMs, &instances[i]) + } + return iVMs, nil } func (host *SHostV2) GetIVMById(gid string) (cloudprovider.ICloudVM, error) { - return nil, cloudprovider.ErrNotImplemented + instance, err := host.zone.region.GetInstance(gid) + if err != nil { + return nil, err + } + instance.hostV2 = host + return instance, nil } func (host *SHostV2) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, @@ -98,11 +112,23 @@ func (host *SHostV2) GetSN() string { } func (host *SHostV2) GetCpuCount() int8 { + if len(host.Resource) == 0 { + if err := host.Refresh(); err != nil { + return 0 + } + } + for _, resource := range host.Resource { + for _, info := range resource { + if info.Project == "(total)" { + return info.CPU + } + } + } return 0 } func (host *SHostV2) GetNodeCount() int8 { - return 0 + return host.GetCpuCount() } func (host *SHostV2) GetCpuDesc() string { @@ -114,10 +140,34 @@ func (host *SHostV2) GetCpuMhz() int { } func (host *SHostV2) GetMemSizeMB() int { + if len(host.Resource) == 0 { + if err := host.Refresh(); err != nil { + return 0 + } + } + for _, resource := range host.Resource { + for _, info := range resource { + if info.Project == "(total)" { + return info.MemoryMb + } + } + } return 0 } func (host *SHostV2) GetStorageSizeMB() int { + if len(host.Resource) == 0 { + if err := host.Refresh(); err != nil { + return 0 + } + } + for _, resource := range host.Resource { + for _, info := range resource { + if info.Project == "(total)" { + return info.DiskGB * 1024 + } + } + } return 0 } @@ -159,12 +209,9 @@ func (host *SHostV2) IsEmulated() bool { } func (host *SHostV2) Refresh() error { - if host.Resource == nil || len(host.Resource) == 0 { - new, err := host.zone.GetIHostById(host.HostName) - if err != nil { - return err - } - return jsonutils.Update(host, new) + new, err := host.zone.GetIHostById(host.HostName) + if err != nil { + return err } - return nil + return jsonutils.Update(host, new) } diff --git a/pkg/util/openstack/hostv3.go b/pkg/util/openstack/hostv3.go index d6c876f36f..6a06e82053 100644 --- a/pkg/util/openstack/hostv3.go +++ b/pkg/util/openstack/hostv3.go @@ -64,7 +64,7 @@ func (host *SHostV3) GetMetadata() *jsonutils.JSONDict { } func (host *SHostV3) GetIWires() ([]cloudprovider.ICloudWire, error) { - return nil, cloudprovider.ErrNotImplemented + return host.zone.GetIWires() } func (host *SHostV3) GetIStorages() ([]cloudprovider.ICloudStorage, error) { @@ -76,11 +76,25 @@ func (host *SHostV3) GetIStorageById(id string) (cloudprovider.ICloudStorage, er } func (host *SHostV3) GetIVMs() ([]cloudprovider.ICloudVM, error) { - return nil, cloudprovider.ErrNotImplemented + instances, err := host.zone.region.GetInstances(host.zone.ZoneName, host.Service.Host) + if err != nil { + return nil, err + } + iVMs := []cloudprovider.ICloudVM{} + for i := 0; i < len(instances); i++ { + instances[i].hostV3 = host + iVMs = append(iVMs, &instances[i]) + } + return iVMs, nil } func (host *SHostV3) GetIVMById(gid string) (cloudprovider.ICloudVM, error) { - return nil, cloudprovider.ErrNotImplemented + instance, err := host.zone.region.GetInstance(gid) + if err != nil { + return nil, err + } + instance.hostV3 = host + return instance, nil } func (host *SHostV3) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, @@ -102,7 +116,7 @@ func (host *SHostV3) GetEnabled() bool { } func (host *SHostV3) GetAccessIp() string { - return "" + return host.HostIP } func (host *SHostV3) GetAccessMac() string { @@ -140,7 +154,7 @@ func (host *SHostV3) GetMemSizeMB() int { } func (host *SHostV3) GetStorageSizeMB() int { - return host.LocalGB + return host.LocalGB * 1024 } func (host *SHostV3) GetStorageType() string { @@ -191,5 +205,9 @@ func (host *SHostV3) IsEmulated() bool { } func (host *SHostV3) Refresh() error { - return nil + new, err := host.zone.region.GetIHostById(host.ID) + if err != nil { + return err + } + return jsonutils.Update(host, new) } diff --git a/pkg/util/openstack/instance.go b/pkg/util/openstack/instance.go new file mode 100644 index 0000000000..e65888eb8a --- /dev/null +++ b/pkg/util/openstack/instance.go @@ -0,0 +1,543 @@ +package openstack + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/billing" +) + +const ( + INSTANCE_STATUS_ACTIVE = "ACTIVE" //The server is active. + INSTANCE_STATUS_BUILD = "BUILD" //The server has not finished the original build process. + INSTANCE_STATUS_DELETED = "DELETED" //The server is permanently deleted. + INSTANCE_STATUS_ERROR = "ERROR" //The server is in error. + INSTANCE_STATUS_HARD_REBOOT = "HARD_REBOOT" //The server is hard rebooting. This is equivalent to pulling the power plug on a physical server, plugging it back in, and rebooting it. + INSTANCE_STATUS_MIGRATING = "MIGRATING" //The server is being migrated to a new host. + INSTANCE_STATUS_PASSWORD = "PASSWORD" //The password is being reset on the server. + INSTANCE_STATUS_PAUSED = "PAUSED" //In a paused state, the state of the server is stored in RAM.A paused server continues to run in frozen state. + INSTANCE_STATUS_REBOOT = "REBOOT" //The server is in a soft reboot state. A reboot command was passed to the operating system. + INSTANCE_STATUS_REBUILD = "REBUILD" //The server is currently being rebuilt from an image. + INSTANCE_STATUS_RESCUE = "RESCUE" //The server is in rescue mode. A rescue image is running with the original server image attached. + INSTANCE_STATUS_RESIZE = "RESIZE" //Server is performing the differential copy of data that changed during its initial copy. Server is down for this stage. + INSTANCE_STATUS_REVERT_RESIZE = "REVERT_RESIZE" //The resize or migration of a server failed for some reason. The destination server is being cleaned up and the original source server is restarting. + INSTANCE_STATUS_SHELVED = "SHELVED" // The server is in shelved state. Depending on the shelve offload time, the server will be automatically shelved offloaded. + INSTANCE_STATUS_SHELVED_OFFLOADED = "SHELVED_OFFLOADED" // The shelved server is offloaded (removed from the compute host) and it needs unshelved action to be used again. + INSTANCE_STATUS_SHUTOFF = "SHUTOFF" //The server is powered off and the disk image still persists. + INSTANCE_STATUS_SOFT_DELETED = "SOFT_DELETED" //The server is marked as deleted but the disk images are still available to restore. + INSTANCE_STATUS_SUSPENDED = "SUSPENDED" //The server is suspended, either by request or necessity. This status appears for only the XenServer/XCP, KVM, and ESXi hypervisors. Administrative users can suspend an instance if it is infrequently used or to perform system maintenance. When you suspend an instance, its VM state is stored on disk, all memory is written to disk, and the virtual machine is stopped. Suspending an instance is similar to placing a device in hibernation; memory and vCPUs become available to create other instances. + INSTANCE_STATUS_UNKNOWN = "UNKNOWN" //The state of the server is unknown. Contact your cloud provider. + INSTANCE_STATUS_VERIFY_RESIZE = "VERIFY_RESIZE" //System is awaiting confirmation that the server is operational after a move or resize. +) + +type SPrivate struct { + MacAddr string `json:"OS-EXT-IPS-MAC:mac_addr,omitempty"` + Addr string `json:"OS-EXT-IPS:type,omitempty"` + Version int +} + +type SecurityGroup struct { + ID string + Name string + Description string +} + +type SAddresses struct { + Private []SInstanceNic + Public []SInstanceNic +} + +type ExtraSpecs struct { + CpuPolicy string `json:"hw:cpu_policy,omitempty"` + MemPageSize int `json:"hw:mem_page_size,omitempty"` +} + +type SFlavor struct { + Disk int + Ephemeral int + ExtraSpecs ExtraSpecs + OriginalName string + RAM int + Swap string + Vcpus int8 +} + +type Resource struct { + ID string + Links []Link +} + +type Image struct { + ID string + Links []Link +} + +type VolumesAttached struct { + ID string + DeleteOnTermination bool +} + +type SInstance struct { + host cloudprovider.ICloudHost + hostV2 *SHostV2 + hostV3 *SHostV3 + + flavor *SFlavor + + DiskConfig string `json:"OS-DCF:diskConfig,omitempty"` + AvailabilityZone string `json:"OS-EXT-AZ:availability_zone,omitempty"` + Host string `json:"OS-EXT-SRV-ATTR:host,omitempty"` + Hostname string `json:"OS-EXT-SRV-ATTR:hostname,omitempty"` + HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname,omitempty"` + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name,omitempty"` + KernelID string `json:"OS-EXT-SRV-ATTR:kernel_id,omitempty"` + LaunchIndex int `json:"OS-EXT-SRV-ATTR:launch_index,omitempty"` + RamdiskID string `json:"OS-EXT-SRV-ATTR:ramdisk_id,omitempty"` + ReservationID string `json:"OS-EXT-SRV-ATTR:reservation_id,omitempty"` + RootDeviceName string `json:"OS-EXT-SRV-ATTR:root_device_name,omitempty"` + UserData string `json:"OS-EXT-SRV-ATTR:user_data,omitempty"` + PowerState int `json:"OS-EXT-STS:power_state,omitempty"` + TaskState string `json:"OS-EXT-STS:task_state,omitempty"` + VmState string `json:"OS-EXT-STS:vm_state,omitempty"` + //LaunchedAt time.Time `json:"OS-SRV-USG:launched_at,omitempty"` + TerminatedAt string `json:"OS-SRV-USG:terminated_at,omitempty"` + + AccessIPv4 string + AccessIPv6 string + Addresses SAddresses + ConfigDrive string + Created time.Time + Description string + Flavor Resource + HostID string + HostStatus string + ID string + image Image //有可能是字符串 + KeyName string + Links []Link + Locked bool + Metadata Metadata + Name string + VolumesAttached []VolumesAttached `json:"os-extended-volumes:volumes_attached,omitempty"` + Progress int + SecurityGroups []SecurityGroup + Status string + Tags []string + TenantID string + TrustedImageCertificates []string + Updated time.Time + UserID string +} + +func (region *SRegion) GetSecurityGroupsByInstance(instanceId string) ([]SecurityGroup, error) { + _, resp, err := region.Get("compute", fmt.Sprintf("/servers/%s/os-security-groups", instanceId), "", nil) + if err != nil { + return nil, err + } + secgroups := []SecurityGroup{} + return secgroups, resp.Unmarshal(&secgroups, "security_groups") +} + +func (region *SRegion) GetInstances(zoneName string, hostName string) ([]SInstance, error) { + _, maxVersion, _ := region.GetVersion("compute") + _, resp, err := region.Get("compute", "/servers/detail", maxVersion, nil) + if err != nil { + return nil, err + } + instances, result := []SInstance{}, []SInstance{} + servers, err := resp.Get("servers") + if err != nil { + return nil, err + } + if err := json.Unmarshal([]byte(servers.String()), &instances); err != nil { + return nil, err + } + for i := 0; i < len(instances); i++ { + if len(zoneName) == 0 || instances[i].AvailabilityZone == zoneName { + if len(hostName) == 0 || hostName == instances[i].Host { + result = append(result, instances[i]) + } + } + } + return result, nil +} + +func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) { + _, maxVersion, _ := region.GetVersion("compute") + _, resp, err := region.Get("compute", "/servers/"+instanceId, maxVersion, nil) + if err != nil { + return nil, err + } + server, err := resp.Get("server") + if err != nil { + return nil, err + } + instance := &SInstance{} + return instance, json.Unmarshal([]byte(server.String()), instance) +} + +func (instance *SInstance) GetMetadata() *jsonutils.JSONDict { + data := jsonutils.NewDict() + + secgroups, err := instance.getRegion().GetSecurityGroupsByInstance(instance.ID) + if err == nil { + secgroupIds := jsonutils.NewArray() + for _, secgroup := range secgroups { + secgroupIds.Add(jsonutils.NewString(secgroup.ID)) + } + data.Add(secgroupIds, "secgroupIds") + } + + if instance.flavor == nil { + if err := instance.fetchFlavor(); err != nil { + log.Errorf("fetch flavor for instance %s failed error: %v", instance.Name, err) + } + } + if instance.flavor != nil { + priceKey := fmt.Sprintf("%s::%s", instance.getZone().ZoneName, instance.flavor.OriginalName) + data.Add(jsonutils.NewString(priceKey), "price_key") + } + + data.Add(jsonutils.NewString(instance.getZone().GetGlobalId()), "zone_ext_id") + return data +} + +func (instance *SInstance) GetCreateTime() time.Time { + return instance.Created +} + +func (instance *SInstance) GetIHost() cloudprovider.ICloudHost { + if instance.hostV3 != nil { + return instance.hostV3 + } + return instance.hostV2 +} + +func (instance *SInstance) GetId() string { + return instance.ID +} + +func (instance *SInstance) GetName() string { + return instance.Name +} + +func (instance *SInstance) GetGlobalId() string { + return instance.ID +} + +func (instance *SInstance) IsEmulated() bool { + return false +} + +func (instance *SInstance) fetchFlavor() error { + _, resp, err := instance.getRegion().Get("compute", "/flavors/"+instance.Flavor.ID, "", nil) + if err != nil { + log.Errorf("fetch instance %s flavor error: %v", instance.Name, err) + return err + } + instance.flavor = &SFlavor{} + flavor, err := resp.Get("flavor") + if err != nil { + log.Errorf("fetch instance %s flavor error: %v", instance.Name, err) + return err + } + return json.Unmarshal([]byte(flavor.String()), instance.flavor) +} + +func (instance *SInstance) GetInstanceType() string { + if instance.flavor == nil { + if err := instance.fetchFlavor(); err != nil { + return "" + } + } + return instance.flavor.OriginalName +} + +func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks := []SDisk{} + for i := 0; i < len(instance.VolumesAttached); i++ { + disk, err := instance.getRegion().GetDisk(instance.VolumesAttached[i].ID) + if err != nil { + return nil, err + } + disks = append(disks, *disk) + } + iDisks := []cloudprovider.ICloudDisk{} + for i := 0; i < len(disks); i++ { + store, err := instance.getZone().getStorageByCategory(disks[i].VolumeType) + if err != nil { + return nil, err + } + disks[i].storage = store + iDisks = append(iDisks, &disks[i]) + } + return iDisks, nil +} + +func (instance *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { + nics := []cloudprovider.ICloudNic{} + for i := 0; i < len(instance.Addresses.Private); i++ { + instance.Addresses.Private[i].instance = instance + nics = append(nics, &instance.Addresses.Private[i]) + } + for i := 0; i < len(instance.Addresses.Public); i++ { + instance.Addresses.Public[i].instance = instance + nics = append(nics, &instance.Addresses.Public[i]) + } + return nics, nil +} + +func (instance *SInstance) GetVcpuCount() int8 { + if instance.flavor == nil { + if err := instance.fetchFlavor(); err != nil { + return 0 + } + } + return instance.flavor.Vcpus +} + +func (instance *SInstance) GetVmemSizeMB() int { + if instance.flavor == nil { + if err := instance.fetchFlavor(); err != nil { + return 0 + } + } + return instance.flavor.RAM +} + +func (instance *SInstance) GetBootOrder() string { + return "dcn" +} + +func (instance *SInstance) GetVga() string { + return "std" +} + +func (instance *SInstance) GetVdi() string { + return "vnc" +} + +func (instance *SInstance) GetOSType() string { + return "Linux" +} + +func (instance *SInstance) GetOSName() string { + return "Linux" +} + +func (instance *SInstance) GetBios() string { + return "BIOS" +} + +func (instance *SInstance) GetMachine() string { + return "pc" +} + +func (instance *SInstance) GetStatus() string { + switch instance.Status { + case INSTANCE_STATUS_ACTIVE, INSTANCE_STATUS_RESCUE: + return models.VM_RUNNING + case INSTANCE_STATUS_BUILD, INSTANCE_STATUS_PASSWORD: + return models.VM_DEPLOYING + case INSTANCE_STATUS_DELETED: + return models.VM_DELETING + case INSTANCE_STATUS_HARD_REBOOT, INSTANCE_STATUS_REBOOT: + return models.VM_STARTING + case INSTANCE_STATUS_MIGRATING: + return models.VM_MIGRATING + case INSTANCE_STATUS_PAUSED, INSTANCE_STATUS_SUSPENDED: + return models.VM_SUSPEND + case INSTANCE_STATUS_RESIZE, INSTANCE_STATUS_VERIFY_RESIZE: + return models.VM_CHANGE_FLAVOR + case INSTANCE_STATUS_SHELVED, INSTANCE_STATUS_SHELVED_OFFLOADED, INSTANCE_STATUS_SHUTOFF, INSTANCE_STATUS_SOFT_DELETED: + return models.VM_READY + default: + return models.VM_UNKNOWN + } +} + +func (instance *SInstance) Refresh() error { + new, err := instance.getRegion().GetInstance(instance.ID) + if err != nil { + return err + } + return json.Unmarshal([]byte(jsonutils.Marshal(new).String()), instance) +} + +func (instance *SInstance) UpdateVM(ctx context.Context, name string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) GetHypervisor() string { + return models.HYPERVISOR_OPENSTACK +} + +func (instance *SInstance) StartVM(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) { + _, maxVersion, _ := region.GetVersion("compute") + params := map[string]map[string]string{ + "remote_console": { + "protocol": "vnc", + "type": "novnc", + }, + } + _, resp, err := region.Post("compute", fmt.Sprintf("/servers/%s/remote-consoles", instanceId), maxVersion, jsonutils.Marshal(params)) + if err != nil { + return "", err + } + return resp.GetString("remote_console", "url") +} + +func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { + url, err := instance.getRegion().GetInstanceVNCUrl(instance.ID) + if err != nil { + return nil, err + } + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewString(url), "url") + ret.Add(jsonutils.NewString("openstack"), "protocol") + ret.Add(jsonutils.NewString(instance.ID), "instance_id") + return ret, nil +} + +func (instance *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) getZone() *SZone { + if instance.hostV3 != nil { + return instance.hostV3.zone + } + return instance.hostV2.zone +} + +func (instance *SInstance) getRegion() *SRegion { + return instance.getZone().region +} + +func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error { + return instance.getRegion().AttachDisk(instance.ID, diskId) +} + +func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error { + return instance.getRegion().DetachDisk(instance.ID, diskId) +} + +func (region *SRegion) CreateInstance(name string, imageId string, instanceType string, securityGroupId string, + zoneId string, desc string, passwd string, disks []SDisk, networkId string, ipAddr string, + keypair string, userData string, bc *billing.SBillingCycle) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (region *SRegion) doStartVM(instanceId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) doStopVM(instanceId string, isForce bool) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) doDeleteVM(instanceId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) StartVM(instanceId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) StopVM(instanceId string, isForce bool) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) DeleteVM(instanceId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, deleteKeypair bool, description string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) DeleteVM(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) DetachDisk(instanceId string, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) AttachDisk(instanceId string, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) AssignSecurityGroup(secgroupId string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) AssignSecurityGroups(secgroupIds []string) error { + return cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (instance *SInstance) GetBillingType() string { + return models.BILLING_TYPE_PREPAID +} + +func (instance *SInstance) GetExpiredAt() time.Time { + return time.Time{} +} + +func (instance *SInstance) UpdateUserData(userData string) error { + return cloudprovider.ErrNotSupported +} + +func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} + +func (instance *SInstance) Renew(bc billing.SBillingCycle) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) RenewInstances(instanceId []string, bc billing.SBillingCycle) error { + return cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/instancenic.go b/pkg/util/openstack/instancenic.go new file mode 100644 index 0000000000..81d465bd69 --- /dev/null +++ b/pkg/util/openstack/instancenic.go @@ -0,0 +1,53 @@ +package openstack + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SInstanceNic struct { + instance *SInstance + MacAddr string `json:"OS-EXT-IPS-MAC:mac_addr"` + Version int `json:"version"` + Addr string `json:"addr"` + Type string `json:"OS-EXT-IPS:type"` +} + +func (nic *SInstanceNic) GetIP() string { + return nic.Addr +} + +func (nic *SInstanceNic) GetMAC() string { + return nic.MacAddr +} + +func (nic *SInstanceNic) GetDriver() string { + return "virtio" +} + +func (nic *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork { + ports, err := nic.instance.getRegion().GetPorts(nic.MacAddr) + if err == nil { + for i := 0; i < len(ports); i++ { + for j := 0; j < len(ports[i].FixedIps); j++ { + if ports[i].FixedIps[j].IpAddress == nic.Addr { + network, err := nic.instance.getRegion().GetNetwork(ports[i].FixedIps[j].SubnetID) + if err != nil { + return nil + } + wires, err := nic.instance.getZone().GetIWires() + if err != nil { + return nil + } + for k := 0; k < len(wires); k++ { + wire := wires[i].(*SWire) + if net, _ := wire.GetINetworkById(network.ID); net != nil { + return net + } + } + return nil + } + } + } + } + return nil +} diff --git a/pkg/util/openstack/network.go b/pkg/util/openstack/network.go new file mode 100644 index 0000000000..fd2180c2f3 --- /dev/null +++ b/pkg/util/openstack/network.go @@ -0,0 +1,157 @@ +package openstack + +import ( + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/pkg/util/netutils" +) + +type AllocationPool struct { + Start string + End string +} + +type SNetwork struct { + wire *SWire + + Name string + EnableDhcp bool + NetworkID string + SegmentID string + ProjectID string + TenantID string + DnsNameservers []string + AllocationPools []AllocationPool + HostRoutes []string + IpVersion int + GatewayIP string + CIDR string + ID string + CreatedAt time.Time + Description string + Ipv6AddressMode string + Ipv6RaMode string + RevisionNumber int + ServiceTypes []string + SubnetpoolID string + Tags []string + UpdatedAt time.Time +} + +func (network *SNetwork) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (network *SNetwork) GetId() string { + return network.ID +} + +func (network *SNetwork) GetName() string { + if len(network.Name) > 0 { + return network.Name + } + return network.ID +} + +func (network *SNetwork) GetGlobalId() string { + return network.ID +} + +func (network *SNetwork) IsEmulated() bool { + return false +} + +func (network *SNetwork) GetStatus() string { + return models.NETWORK_STATUS_AVAILABLE +} + +func (network *SNetwork) Delete() error { + return network.wire.zone.region.DeleteNetwork(network.ID) +} + +func (network *SRegion) DeleteNetwork(networkId string) error { + return cloudprovider.ErrNotImplemented +} + +func (network *SNetwork) GetIWire() cloudprovider.ICloudWire { + return network.wire +} + +func (network *SNetwork) GetAllocTimeoutSeconds() int { + return 120 // 2 minutes +} + +func (network *SNetwork) GetGateway() string { + return network.GatewayIP +} + +func (network *SNetwork) GetIpStart() string { + if len(network.AllocationPools) >= 1 { + return network.AllocationPools[0].Start + } + return "" +} + +func (network *SNetwork) GetIpEnd() string { + if len(network.AllocationPools) >= 1 { + return network.AllocationPools[0].End + } + return "" +} + +func (network *SNetwork) GetIpMask() int8 { + pref, _ := netutils.NewIPV4Prefix(network.CIDR) + return pref.MaskLen +} + +func (network *SNetwork) GetIsPublic() bool { + return true +} + +func (network *SNetwork) GetServerType() string { + return models.SERVER_TYPE_GUEST +} + +func (region *SRegion) GetNetwork(networkId string) (*SNetwork, error) { + _, resp, err := region.Get("network", "/v2.0/subnets/"+networkId, "", nil) + if err != nil { + return nil, err + } + network := SNetwork{} + return &network, resp.Unmarshal(&network, "subnet") +} + +func (region *SRegion) GetNetworks(vpcId string) ([]SNetwork, error) { + _, resp, err := region.Get("network", "/v2.0/subnets", "", nil) + if err != nil { + return nil, err + } + networks := []SNetwork{} + if err := resp.Unmarshal(&networks, "subnets"); err != nil { + return nil, err + } + result := []SNetwork{} + for i := 0; i < len(networks); i++ { + if len(vpcId) == 0 || vpcId == networks[i].NetworkID { + result = append(result, networks[i]) + } + } + return result, nil +} + +func (network *SNetwork) Refresh() error { + log.Debugf("network refresh %s", network.Name) + new, err := network.wire.zone.region.GetNetwork(network.ID) + if err != nil { + return err + } + return jsonutils.Update(network, new) +} + +func (network *SRegion) CreateNetwork(zoneId string, vpcId string, name string, cidr string, desc string) (string, error) { + return "", cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/openstack.go b/pkg/util/openstack/openstack.go index 5ca1a5282a..ab0e61c041 100644 --- a/pkg/util/openstack/openstack.go +++ b/pkg/util/openstack/openstack.go @@ -3,8 +3,10 @@ package openstack import ( "fmt" "net/http" + "strings" "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/version" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/mcclient" @@ -13,6 +15,8 @@ import ( const ( CLOUD_PROVIDER_OPENSTACK = models.CLOUD_PROVIDER_OPENSTACK OPENSTACK_DEFAULT_REGION = "RegionOne" + + DEBUG = false ) type SOpenStackClient struct { @@ -30,7 +34,6 @@ type SOpenStackClient struct { func NewOpenStackClient(providerID string, providerName string, authURL string, username string, password string, project string) (*SOpenStackClient, error) { cli := &SOpenStackClient{providerID: providerID, providerName: providerName, authURL: authURL, username: username, password: password, project: project} - return cli, cli.fetchRegions() } @@ -55,31 +58,56 @@ func (cli *SOpenStackClient) fetchRegions() error { return nil } -func (cli *SOpenStackClient) Get(region string, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { - if body == nil { - body = jsonutils.NewDict() - } +func (cli *SOpenStackClient) Request(region, service, method string, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { header := http.Header{} if len(microversion) > 0 { header.Set("X-Openstack-Nova-API-Version", microversion) } session := cli.client.NewSession(region, "", "internal", cli.tokenCredential, "") - return session.JSONRequest("compute", "", "GET", url, header, body) + return session.JSONRequest(service, "", method, url, header, body) } -func (cli *SOpenStackClient) getComputeVersion(region string, service string) (string, string, error) { +func (cli *SOpenStackClient) getVersion(region string, service string) (string, string, error) { session := cli.client.NewSession(region, "", "internal", cli.tokenCredential, "") - _, resp, err := session.JSONRequest(service, "", "GET", "/", nil, nil) + uri, err := session.GetServiceURL(service, "internal") + if err != nil { + return "", "", err + } + url := uri + telnetID := cli.tokenCredential.GetTenantId() + if strings.Index(uri, telnetID) > 0 { + url = uri[0:strings.Index(uri, telnetID)] + } + _, resp, err := session.JSONRequest(url, "", "GET", "/", nil, nil) if err != nil { return "", "", err } minVersion, _ := resp.GetString("version", "min_version") maxVersion, _ := resp.GetString("version", "version") + if resp.Contains("versions") { + minVersion, maxVersion = "1000.0", "" + versions, _ := resp.GetArray("versions") + for _, _version := range versions { + if _minVersion, _ := _version.GetString("min_version"); len(_minVersion) > 0 { + if version.LT(_minVersion, minVersion) { + minVersion = _minVersion + } + } + if _maxVersion, _ := _version.GetString("version"); len(_maxVersion) > 0 { + if version.GT(_maxVersion, maxVersion) { + maxVersion = _maxVersion + } + } + } + if minVersion == "1000.0" { + minVersion, maxVersion = "", "" + } + } return minVersion, maxVersion, nil } func (cli *SOpenStackClient) connect() error { - cli.client = mcclient.NewClient(cli.authURL, 5, false, false, "", "") + cli.client = mcclient.NewClient(cli.authURL, 5, DEBUG, false, "", "") tokenCredential, err := cli.client.Authenticate(cli.username, cli.password, "", cli.project) if err != nil { return err @@ -97,6 +125,19 @@ func (cli *SOpenStackClient) GetRegion(regionId string) *SRegion { return nil } +func (cli *SOpenStackClient) GetIRegions() []cloudprovider.ICloudRegion { + return cli.iregions +} + +func (cli *SOpenStackClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { + for i := 0; i < len(cli.iregions); i++ { + if cli.iregions[i].GetGlobalId() == id { + return cli.iregions[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + func (cli *SOpenStackClient) GetRegions() []SRegion { regions := make([]SRegion, len(cli.iregions)) for i := 0; i < len(regions); i++ { diff --git a/pkg/util/openstack/port.go b/pkg/util/openstack/port.go new file mode 100644 index 0000000000..13b8923d2e --- /dev/null +++ b/pkg/util/openstack/port.go @@ -0,0 +1,69 @@ +package openstack + +import ( + "fmt" + "net/url" + "time" +) + +type DnsAssignment struct { + Hostname string + IpAddress string + Fqdn string +} + +type ExtraDhcpOpt struct { + OptValue string + IpVersion int + OptName string +} + +type FixedIPs struct { + IpAddress string + SubnetID string +} + +type SPort struct { + AdminStateUp bool + AllowedAddressPairs []string + CreatedAt time.Time + DataPlaneStatus string + Description string + DeviceID string + DeviceOwner string + DnsAssignment DnsAssignment + DnsDomain string + DnsName string + ExtraDhcpOpts []ExtraDhcpOpt + FixedIps []FixedIPs + ID string + IpAllocation string + MacAddress string + Name string + NetworkID string + ProjectID string + RevisionNumber int + SecurityGroups []string + Status string + Tags []string + TenantID string + UpdatedAt time.Time + QosPolicyID string + PortSecurityEnabled bool + UplinkStatusPropagation bool +} + +func (region *SRegion) GetPorts(macAddress string) ([]SPort, error) { + base := fmt.Sprintf("/v2.0/ports") + params := url.Values{} + if len(macAddress) > 0 { + params.Set("mac_address", macAddress) + } + url := fmt.Sprintf("%s?%s", base, params.Encode()) + _, resp, err := region.Get("network", url, "", nil) + if err != nil { + return nil, err + } + ports := []SPort{} + return ports, resp.Unmarshal(&ports, "ports") +} diff --git a/pkg/util/openstack/provider/provider.go b/pkg/util/openstack/provider/provider.go index 44befa6b23..b5cb52d08c 100644 --- a/pkg/util/openstack/provider/provider.go +++ b/pkg/util/openstack/provider/provider.go @@ -1,6 +1,8 @@ package provider import ( + "strings" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -19,8 +21,13 @@ func (self *SOpenStackProviderFactory) ValidateChangeBandwidth(instanceId string return nil } -func (self *SOpenStackProviderFactory) GetProvider(providerId, providerName, url, username, password string) (cloudprovider.ICloudProvider, error) { - client, err := openstack.NewOpenStackClient(providerId, providerName, url, username, username, password) +func (self *SOpenStackProviderFactory) GetProvider(providerId, providerName, url, account, password string) (cloudprovider.ICloudProvider, error) { + accountInfo := strings.Split(account, "/") + username, project := accountInfo[0], "" + if len(accountInfo) > 1 { + project = accountInfo[1] + } + client, err := openstack.NewOpenStackClient(providerId, providerName, url, username, password, project) if err != nil { return nil, err } @@ -40,6 +47,10 @@ func (self *SOpenStackProvider) IsPublicCloud() bool { return false } +func (self *SOpenStackProvider) GetVersion() string { + return "" +} + func (self *SOpenStackProvider) IsOnPremiseInfrastructure() bool { return false } @@ -53,7 +64,7 @@ func (self *SOpenStackProvider) GetName() string { } func (self *SOpenStackProvider) GetSysInfo() (jsonutils.JSONObject, error) { - return nil, nil + return jsonutils.NewDict(), nil } func (self *SOpenStackProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { @@ -61,11 +72,11 @@ func (self *SOpenStackProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, e } func (self *SOpenStackProvider) GetIRegions() []cloudprovider.ICloudRegion { - return nil + return self.client.GetIRegions() } func (self *SOpenStackProvider) GetIRegionById(extId string) (cloudprovider.ICloudRegion, error) { - return nil, cloudprovider.ErrNotSupported + return self.client.GetIRegionById(extId) } func (self *SOpenStackProvider) GetBalance() (float64, error) { diff --git a/pkg/util/openstack/region.go b/pkg/util/openstack/region.go index f603c01ecc..844bc24e73 100644 --- a/pkg/util/openstack/region.go +++ b/pkg/util/openstack/region.go @@ -5,9 +5,9 @@ import ( "net/http" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/pkg/util/secrules" ) type SRegion struct { @@ -18,7 +18,7 @@ type SRegion struct { izones []cloudprovider.ICloudZone ivpcs []cloudprovider.ICloudVpc - //storageCache *SStoragecache + storageCache *SStoragecache } func (region *SRegion) GetClient() *SOpenStackClient { @@ -67,34 +67,34 @@ func (region *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudp } func (region *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - // izones, err := self.GetIZones() - // if err != nil { - // return nil, err - // } - // for i := 0; i < len(izones); i++ { - // ihost, err := izones[i].GetIHostById(id) - // if err == nil { - // return ihost, nil - // } else if err != cloudprovider.ErrNotFound { - // return nil, err - // } - // } + izones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i++ { + ihost, err := izones[i].GetIHostById(id) + if err == nil { + return ihost, nil + } else if err != cloudprovider.ErrNotFound { + return nil, err + } + } return nil, cloudprovider.ErrNotFound } func (region *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - // izones, err := self.GetIZones() - // if err != nil { - // return nil, err - // } - // for i := 0; i < len(izones); i += 1 { - // istore, err := izones[i].GetIStorageById(id) - // if err == nil { - // return istore, nil - // } else if err != cloudprovider.ErrNotFound { - // return nil, err - // } - // } + izones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i++ { + istore, err := izones[i].GetIStorageById(id) + if err == nil { + return istore, nil + } else if err != cloudprovider.ErrNotFound { + return nil, err + } + } return nil, cloudprovider.ErrNotFound } @@ -115,41 +115,43 @@ func (region *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { } func (region *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - iStores := make([]cloudprovider.ICloudStorage, 0) + izones, err := region.GetIZones() + if err != nil { + return nil, err + } + iStorages := []cloudprovider.ICloudStorage{} + for i := 0; i < len(izones); i++ { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStorages = append(iStorages, iZoneStores...) + } + return iStorages, nil +} - // izones, err := self.GetIZones() - // if err != nil { - // return nil, err - // } - // for i := 0; i < len(izones); i += 1 { - // iZoneStores, err := izones[i].GetIStorages() - // if err != nil { - // return nil, err - // } - // iStores = append(iStores, iZoneStores...) - // } - return iStores, nil +func (region *SRegion) getStoragecache() *SStoragecache { + if region.storageCache == nil { + region.storageCache = &SStoragecache{region: region} + } + return region.storageCache } func (region *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - // storageCache := self.getStoragecache() - // if storageCache.GetGlobalId() == id { - // return self.storageCache, nil - // } + storageCache := region.getStoragecache() + if storageCache.GetGlobalId() == id { + return region.storageCache, nil + } return nil, cloudprovider.ErrNotFound } func (region *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - // ivpcs, err := self.GetIVpcs() - // if err != nil { - // return nil, err - // } - // for i := 0; i < len(ivpcs); i++ { - // if ivpcs[i].GetGlobalId() == id { - // return ivpcs[i], nil - // } - // } - return nil, cloudprovider.ErrNotFound + vpc, err := region.GetVpc(id) + if err != nil { + return nil, err + } + vpc.region = region + return vpc, nil } func (region *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { @@ -166,91 +168,104 @@ func (region *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) } func (region *SRegion) fetchZones() error { - zones, err := region.GetIZones() + _, resp, err := region.Get("compute", "/os-availability-zone", "", jsonutils.NewDict()) if err != nil { return err } - region.izones = zones - return nil -} - -func (region *SRegion) fetchInfrastructure() error { - if err := region.fetchZones(); err != nil { - return err - } - // err = region.fetchIVpc() - // if err != nil { - // return err - // } - // for i := 0; i < len(region.ivpcs); i++ { - // for j := 0; j < len(region.izones); j++ { - // zone := region.izones[j].(*SZone) - // vpc := region.ivpcs[i].(*SVpc) - // wire := SWire{zone: zone, vpc: vpc} - // zone.addWire(&wire) - // vpc.addWire(&wire) - // } - // } - return nil -} - -func (region *SRegion) Get(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { - return region.client.Get(region.Name, url, microversion, body) -} - -func (region *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { - _, resp, err := region.Get("/os-availability-zone", "", jsonutils.NewDict()) - if err != nil { - return nil, err - } zones := []SZone{} if err := resp.Unmarshal(&zones, "availabilityZoneInfo"); err != nil { - return nil, err + return err } - izones := []cloudprovider.ICloudZone{} + region.izones = []cloudprovider.ICloudZone{} for i := 0; i < len(zones); i++ { if zones[i].ZoneName == "internal" { continue } zones[i].region = region - izones = append(izones, &zones[i]) + region.izones = append(region.izones, &zones[i]) } - return izones, nil + return nil +} + +func (region *SRegion) fetchIVpcs() error { + vpcs, err := region.GetVpcs() + if err != nil { + return err + } + region.ivpcs = []cloudprovider.ICloudVpc{} + for i := 0; i < len(vpcs); i++ { + vpcs[i].region = region + region.ivpcs = append(region.ivpcs, &vpcs[i]) + } + return nil +} + +func (region *SRegion) fetchInfrastructure() error { + if len(region.izones) == 0 { + if err := region.fetchZones(); err != nil { + return err + } + } + if err := region.fetchIVpcs(); err != nil { + return err + } + for i := 0; i < len(region.ivpcs); i++ { + for j := 0; j < len(region.izones); j++ { + zone := region.izones[j].(*SZone) + vpc := region.ivpcs[i].(*SVpc) + wire := SWire{zone: zone, vpc: vpc} + zone.addWire(&wire) + vpc.addWire(&wire) + } + } + return nil +} + +func (region *SRegion) Get(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { + return region.client.Request(region.Name, service, "GET", url, microversion, body) +} + +func (region *SRegion) Post(service, url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { + return region.client.Request(region.Name, service, "POST", url, microversion, body) +} + +func (region *SRegion) CinderGet(url string, microversion string, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) { + for _, service := range []string{"volumev3", "volumev2", "volume"} { + header, resp, err := region.Get(service, url, microversion, body) + if err == nil { + return header, resp, nil + } + log.Debugf("failed to get %s by service %s error: %v, try another", url, service, err) + } + return nil, nil, fmt.Errorf("failed to get %s by cinder service", url) +} + +func (region *SRegion) ProjectId() string { + return region.client.tokenCredential.GetProjectId() +} + +func (region *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { + if region.izones == nil { + if err := region.fetchInfrastructure(); err != nil { + return nil, err + } + } + return region.izones, nil } func (region *SRegion) GetVersion(service string) (string, string, error) { - return region.client.getComputeVersion(region.Name, service) + return region.client.getVersion(region.Name, service) } func (region *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { - // if self.ivpcs == nil || self.iclassicVpcs == nil { - // if err := self.fetchInfrastructure(); err != nil { - // return nil, err - // } - // } - // for _, vpc := range self.ivpcs { - // log.Debugf("find vpc %s for region %s", vpc.GetName(), self.GetName()) - // } - // for _, vpc := range self.iclassicVpcs { - // log.Debugf("find classic vpc %s for region %s", vpc.GetName(), self.GetName()) - // } - // ivpcs := self.ivpcs - // if len(self.iclassicVpcs) > 0 { - // ivpcs = append(ivpcs, self.iclassicVpcs...) - // } - return nil, cloudprovider.ErrNotImplemented + if err := region.fetchInfrastructure(); err != nil { + return nil, err + } + return region.ivpcs, nil } func (region *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotSupported -} - -func (region *SRegion) DeleteSecurityGroup(vpcId, secgroupId string) error { - return cloudprovider.ErrNotImplemented -} - -func (region *SRegion) SyncSecurityGroup(secgroupId, vpcId, name, desc string, rules []secrules.SecurityRule) (string, error) { - return "", cloudprovider.ErrNotImplemented + return nil, cloudprovider.ErrNotImplemented } func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string) (cloudprovider.ICloudEIP, error) { @@ -261,14 +276,6 @@ func (region *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error return nil, cloudprovider.ErrNotSupported } -func (region *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { - return nil, cloudprovider.ErrNotImplemented -} - func (region *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { return nil, cloudprovider.ErrNotImplemented } diff --git a/pkg/util/openstack/securitygroup.go b/pkg/util/openstack/securitygroup.go new file mode 100644 index 0000000000..f737a7c6e8 --- /dev/null +++ b/pkg/util/openstack/securitygroup.go @@ -0,0 +1,185 @@ +package openstack + +import ( + "net" + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/pkg/util/secrules" + "yunion.io/x/pkg/utils" +) + +type SSecurityGroupRule struct { + Direction string + Ethertype string + ID string + PortRangeMax int + PortRangeMin int + Protocol string + RemoteGroupID string + RemoteIpPrefix string + SecurityGroupID string + ProjectID string + RevisionNumber int + Tags []string + TenantID string + CreatedAt time.Time + UpdatedAt time.Time + Description string +} + +type SSecurityGroup struct { + vpc *SVpc + + Description string + ID string + Name string + SecurityGroupRules []SSecurityGroupRule + ProjectID string + RevisionNumber int + CreatedAt time.Time + UpdatedAt time.Time + Tags []string + TenantID string +} + +func (region *SRegion) GetSecurityGroup(secgroupId string) (*SSecurityGroup, error) { + _, resp, err := region.Get("network", "/v2.0/security-groups/"+secgroupId, "", nil) + if err != nil { + return nil, err + } + secgroup := &SSecurityGroup{} + return secgroup, resp.Unmarshal(secgroup, "security_group") +} + +func (region *SRegion) GetSecurityGroups() ([]SSecurityGroup, error) { + _, resp, err := region.Get("network", "/v2.0/security-groups", "", nil) + if err != nil { + return nil, err + } + secgroups := []SSecurityGroup{} + return secgroups, resp.Unmarshal(&secgroups, "security_groups") +} + +func (secgroup *SSecurityGroup) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (secgroup *SSecurityGroup) GetVpcId() string { + return "normal" +} + +func (secgroup *SSecurityGroup) GetId() string { + return secgroup.ID +} + +func (secgroup *SSecurityGroup) GetGlobalId() string { + return secgroup.ID +} + +func (secgroup *SSecurityGroup) GetDescription() string { + return secgroup.Description +} + +func (secgroup *SSecurityGroup) GetName() string { + if len(secgroup.Name) > 0 { + return secgroup.Name + } + return secgroup.ID +} + +func (secgroup *SSecurityGroupRule) String() string { + rules := secgroup.toRules() + result := []string{} + for _, rule := range rules { + result = append(result, rule.String()) + } + return strings.Join(result, ";") +} + +func (secgrouprule *SSecurityGroupRule) toRules() []secrules.SecurityRule { + rules := []secrules.SecurityRule{} + // 暂时忽略IPv6安全组规则,忽略远端也是安全组的规则 + if secgrouprule.Ethertype != "IPv4" || len(secgrouprule.RemoteGroupID) > 0 { + return rules + } + rule := secrules.SecurityRule{ + Direction: secrules.DIR_IN, + Action: secrules.SecurityRuleAllow, + Description: secgrouprule.Description, + Priority: 1, + } + if utils.IsInStringArray(secgrouprule.Protocol, []string{"", "0", "any"}) { + rule.Protocol = secrules.PROTO_ANY + } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"6", "tcp"}) { + rule.Protocol = secrules.PROTO_TCP + } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"17", "udp"}) { + rule.Protocol = secrules.PROTO_UDP + } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"1", "icmp"}) { + rule.Protocol = secrules.PROTO_ICMP + } else { + return rules + } + if secgrouprule.Direction == "egress" { + rule.Direction = secrules.DIR_OUT + } + if len(secgrouprule.RemoteIpPrefix) == 0 { + secgrouprule.RemoteIpPrefix = "0.0.0.0/0" + } + _, ipnet, err := net.ParseCIDR(secgrouprule.RemoteIpPrefix) + if err != nil { + return rules + } + rule.IPNet = ipnet + if secgrouprule.PortRangeMax > 0 && secgrouprule.PortRangeMin > 0 { + if secgrouprule.PortRangeMax == secgrouprule.PortRangeMin { + rule.Ports = []int{secgrouprule.PortRangeMax} + } else { + rule.PortStart = secgrouprule.PortRangeMin + rule.PortEnd = secgrouprule.PortRangeMax + } + } + if err := rule.ValidateRule(); err != nil { + return rules + } + return []secrules.SecurityRule{rule} +} + +func (secgroup *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) { + rules := []secrules.SecurityRule{} + for _, rule := range secgroup.SecurityGroupRules { + subRules := rule.toRules() + rules = append(rules, subRules...) + } + return rules, nil +} + +func (secgroup *SSecurityGroup) GetStatus() string { + return "" +} + +func (secgroup *SSecurityGroup) IsEmulated() bool { + return false +} + +func (secgroup *SSecurityGroup) Refresh() error { + new, err := secgroup.vpc.region.GetSecurityGroup(secgroup.ID) + if err != nil { + return err + } + return jsonutils.Update(secgroup, new) +} + +func (secgroup *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (region *SRegion) DeleteSecurityGroup(vpcId, secGroupId string) error { + return cloudprovider.ErrNotImplemented +} + +func (region *SRegion) CreateSecurityGroup(name, description string) (*SSecurityGroup, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/shell/disk.go b/pkg/util/openstack/shell/disk.go new file mode 100644 index 0000000000..fd66d361c0 --- /dev/null +++ b/pkg/util/openstack/shell/disk.go @@ -0,0 +1,33 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type DiskListOptions struct { + Category string `help:"Storage type for disk"` + } + shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *openstack.SRegion, args *DiskListOptions) error { + disks, err := cli.GetDisks(args.Category) + if err != nil { + return err + } + printList(disks, 0, 0, 0, []string{}) + return nil + }) + + type DiskShowOptions struct { + ID string `help:"Storage type for disk"` + } + + shellutils.R(&DiskShowOptions{}, "disk-show", "Show disk", func(cli *openstack.SRegion, args *DiskShowOptions) error { + disk, err := cli.GetDisk(args.ID) + if err != nil { + return err + } + printObject(disk) + return nil + }) +} diff --git a/pkg/util/openstack/shell/instance.go b/pkg/util/openstack/shell/instance.go new file mode 100644 index 0000000000..fa00647313 --- /dev/null +++ b/pkg/util/openstack/shell/instance.go @@ -0,0 +1,45 @@ +package shell + +import ( + "fmt" + + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type InstanceListOptions struct { + ZoneID string `help:"Zone ID for filter instance list"` + Host string `help:"Host name for filter instance list"` + } + shellutils.R(&InstanceListOptions{}, "instance-list", "List instances", func(cli *openstack.SRegion, args *InstanceListOptions) error { + instances, err := cli.GetInstances(args.ZoneID, args.Host) + if err != nil { + return err + } + printList(instances, 0, 0, 0, nil) + return nil + }) + + type InstanceOptions struct { + ID string `help:"Instance ID"` + } + shellutils.R(&InstanceOptions{}, "instance-show", "Show instance", func(cli *openstack.SRegion, args *InstanceOptions) error { + instance, err := cli.GetInstance(args.ID) + if err != nil { + return err + } + printObject(instance) + return nil + }) + + shellutils.R(&InstanceOptions{}, "instance-vnc", "Show instance vnc url", func(cli *openstack.SRegion, args *InstanceOptions) error { + url, err := cli.GetInstanceVNCUrl(args.ID) + if err != nil { + return err + } + fmt.Println(url) + return nil + }) + +} diff --git a/pkg/util/openstack/shell/instancenic.go b/pkg/util/openstack/shell/instancenic.go new file mode 100644 index 0000000000..7d81ea71b3 --- /dev/null +++ b/pkg/util/openstack/shell/instancenic.go @@ -0,0 +1,20 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type InstanceNicListOptions struct { + Mac string `help:"Mac address for filter nics"` + } + shellutils.R(&InstanceNicListOptions{}, "instancenic-list", "List instance nics", func(cli *openstack.SRegion, args *InstanceNicListOptions) error { + instances, err := cli.GetPorts(args.Mac) + if err != nil { + return err + } + printList(instances, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/util/openstack/shell/network.go b/pkg/util/openstack/shell/network.go new file mode 100644 index 0000000000..cb17dc41c8 --- /dev/null +++ b/pkg/util/openstack/shell/network.go @@ -0,0 +1,33 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type NetworkListOptions struct { + VpcId string `help:"Vpc ID for filter network list"` + } + shellutils.R(&NetworkListOptions{}, "network-list", "List networks", func(cli *openstack.SRegion, args *NetworkListOptions) error { + networks, err := cli.GetNetworks(args.VpcId) + if err != nil { + return err + } + printList(networks, 0, 0, 0, nil) + return nil + }) + + type NetworkOptions struct { + ID string `help:"Network ID"` + } + shellutils.R(&NetworkOptions{}, "network-show", "Show network", func(cli *openstack.SRegion, args *NetworkOptions) error { + network, err := cli.GetNetwork(args.ID) + if err != nil { + return err + } + printObject(network) + return nil + }) + +} diff --git a/pkg/util/openstack/shell/securitygroup.go b/pkg/util/openstack/shell/securitygroup.go new file mode 100644 index 0000000000..ca85e82b7b --- /dev/null +++ b/pkg/util/openstack/shell/securitygroup.go @@ -0,0 +1,32 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type SecurityGroupListOptions struct { + } + shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "List security groups", func(cli *openstack.SRegion, args *SecurityGroupListOptions) error { + secgroup, err := cli.GetSecurityGroups() + if err != nil { + return err + } + printList(secgroup, 0, 0, 0, nil) + return nil + }) + + type SecurityGroupShowOptions struct { + ID string `help:"ID of security group"` + } + shellutils.R(&SecurityGroupShowOptions{}, "security-group-show", "Show security group", func(cli *openstack.SRegion, args *SecurityGroupShowOptions) error { + secgroup, err := cli.GetSecurityGroup(args.ID) + if err != nil { + return err + } + printObject(secgroup) + return nil + }) + +} diff --git a/pkg/util/openstack/shell/service.go b/pkg/util/openstack/shell/service.go deleted file mode 100644 index df3de02218..0000000000 --- a/pkg/util/openstack/shell/service.go +++ /dev/null @@ -1,20 +0,0 @@ -package shell - -import ( - "yunion.io/x/onecloud/pkg/util/openstack" - "yunion.io/x/onecloud/pkg/util/shellutils" -) - -func init() { - type ServiceOptions struct { - SERVICE string `help:"Service name" choices:"compute"` - } - shellutils.R(&ServiceOptions{}, "version-show", "Show a service version", func(cli *openstack.SRegion, args *ServiceOptions) error { - // minVersion, maxVersion, err := cli.GetVersion(args.SERVICE) - // if err != nil { - // return err - // } - // fmt.Println("min version: %s max version: %s", minVersion, maxVersion) - return nil - }) -} diff --git a/pkg/util/openstack/shell/snapshot.go b/pkg/util/openstack/shell/snapshot.go new file mode 100644 index 0000000000..79097fa691 --- /dev/null +++ b/pkg/util/openstack/shell/snapshot.go @@ -0,0 +1,34 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type SnapshotListOptions struct { + DiskId string `help:"Disk ID for filter snapshot"` + } + shellutils.R(&SnapshotListOptions{}, "snapshot-list", "List snapshots", func(cli *openstack.SRegion, args *SnapshotListOptions) error { + snapshots, err := cli.GetSnapshots(args.DiskId) + if err != nil { + return err + } + printList(snapshots, 0, 0, 0, []string{}) + return nil + }) + + type SnapshotShowOptions struct { + ID string `help:"ID of snapshot"` + } + + shellutils.R(&SnapshotShowOptions{}, "snapshot-show", "Show snapshot", func(cli *openstack.SRegion, args *SnapshotShowOptions) error { + snapshot, err := cli.GetISnapshotById(args.ID) + if err != nil { + return err + } + printObject(snapshot) + return nil + }) + +} diff --git a/pkg/util/openstack/shell/storage.go b/pkg/util/openstack/shell/storage.go new file mode 100644 index 0000000000..96135dc83a --- /dev/null +++ b/pkg/util/openstack/shell/storage.go @@ -0,0 +1,27 @@ +package shell + +import ( + "fmt" + + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type StorageListOptions struct { + REGION string `help:"Region Name"` + ZONE string `help:"Zone Name"` + } + shellutils.R(&StorageListOptions{}, "storage-list", "List storages", func(cli *openstack.SRegion, args *StorageListOptions) error { + zone, err := cli.GetIZoneById(fmt.Sprintf("%s/%s/%s", openstack.CLOUD_PROVIDER_OPENSTACK, args.REGION, args.ZONE)) + if err != nil { + return err + } + storages, err := zone.GetIStorages() + if err != nil { + return err + } + printList(storages, 0, 0, 0, []string{}) + return nil + }) +} diff --git a/pkg/util/openstack/shell/version.go b/pkg/util/openstack/shell/version.go index 639af21051..69f640995a 100644 --- a/pkg/util/openstack/shell/version.go +++ b/pkg/util/openstack/shell/version.go @@ -9,7 +9,7 @@ import ( func init() { type VersionOptions struct { - SERVICE string `help:"Service name" choices:"compute"` + SERVICE string `help:"Service name" choices:"compute|volume|volumev2|volumev3"` } shellutils.R(&VersionOptions{}, "version-show", "Show a service version", func(cli *openstack.SRegion, args *VersionOptions) error { minVersion, maxVersion, err := cli.GetVersion(args.SERVICE) diff --git a/pkg/util/openstack/shell/vpc.go b/pkg/util/openstack/shell/vpc.go new file mode 100644 index 0000000000..fceb295f74 --- /dev/null +++ b/pkg/util/openstack/shell/vpc.go @@ -0,0 +1,32 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/openstack" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VpcListOptions struct { + } + shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *openstack.SRegion, args *VpcListOptions) error { + vpcs, err := cli.GetVpcs() + if err != nil { + return err + } + printList(vpcs, 0, 0, 0, nil) + return nil + }) + + type VpcShowOptions struct { + ID string `help:"ID of vpc"` + } + shellutils.R(&VpcShowOptions{}, "vpc-show", "Show vpc", func(cli *openstack.SRegion, args *VpcShowOptions) error { + vpc, err := cli.GetVpc(args.ID) + if err != nil { + return err + } + printObject(vpc) + return nil + }) + +} diff --git a/pkg/util/openstack/snapshot.go b/pkg/util/openstack/snapshot.go new file mode 100644 index 0000000000..15999d880c --- /dev/null +++ b/pkg/util/openstack/snapshot.go @@ -0,0 +1,148 @@ +package openstack + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +const ( + SNAPSHOT_STATUS_CREATING = "creating" //The snapshot is being created. + SNAPSHOT_STATUS_AVAILABLE = "available" //The snapshot is ready to use. + SNAPSHOT_STATUS_BACKING_UP = "backing-up" //The snapshot is being backed up. + SNAPSHOT_STATUS_DELETING = "deleting" //The snapshot is being deleted. + SNAPSHOT_STATUS_ERROR = "error" //A snapshot creation error occurred. + SNAPSHOT_STATUS_DELETED = "deleted" //The snapshot has been deleted. + SNAPSHOT_STATUS_UNMANAGING = "unmanaging" //The snapshot is being unmanaged. + SNAPSHOT_STATUS_RESTORING = "restoring" //The snapshot is being restored to a volume. + SNAPSHOT_STATUS_ERROR_DELETING = "error_deleting" //A snapshot deletion error occurred. +) + +type SSnapshot struct { + region *SRegion + + ID string + VolumeID string + + Status string + Metadata Metadata + + //os-extended-snapshot-attributes:progress 100% + Name string + UserID string + //os-extended-snapshot-attributes:project_id bab7d5c60cd041a0a36f7c4b6e1dd978 + //CreatedAt time.Time + Size int32 + + Description string + //UpdatedAt time.Time +} + +func (region *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + _, resp, err := region.CinderGet("/snapshots/"+snapshotId, "", nil) + if err != nil { + return nil, err + } + snapshot := SSnapshot{} + if err := resp.Unmarshal(&snapshot, "snapshot"); err != nil { + return nil, err + } + return &snapshot, nil +} + +func (snapshot *SSnapshot) GetStatus() string { + switch snapshot.Status { + case SNAPSHOT_STATUS_CREATING: + return models.SNAPSHOT_CREATING + case SNAPSHOT_STATUS_AVAILABLE: + return models.SNAPSHOT_READY + case SNAPSHOT_STATUS_BACKING_UP: + return models.SNAPSHOT_ROLLBACKING + case SNAPSHOT_STATUS_DELETED, SNAPSHOT_STATUS_DELETING: + return models.SNAPSHOT_DELETING + default: + return models.SNAPSHOT_UNKNOWN + } +} + +func (snapshot *SSnapshot) IsEmulated() bool { + return false +} + +func (snapshot *SSnapshot) Refresh() error { + _snapshot, err := snapshot.region.GetISnapshotById(snapshot.ID) + if err != nil { + return err + } + return jsonutils.Update(snapshot, _snapshot) +} + +func (region *SRegion) GetSnapshots(diskId string) ([]cloudprovider.ICloudSnapshot, error) { + _, resp, err := region.CinderGet("/snapshots/detail", "", nil) + if err != nil { + return nil, err + } + snapshots := []SSnapshot{} + if err := resp.Unmarshal(&snapshots, "snapshots"); err != nil { + return nil, err + } + iSnapshots := []cloudprovider.ICloudSnapshot{} + for i := 0; i < len(snapshots); i++ { + if len(diskId) == 0 || snapshots[i].VolumeID == diskId { + snapshots[i].region = region + iSnapshots = append(iSnapshots, &snapshots[i]) + } + } + return iSnapshots, nil +} + +func (region *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + return region.GetSnapshots("") +} + +func (snapshot *SSnapshot) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (snapshot *SSnapshot) GetSize() int32 { + return snapshot.Size +} + +func (snapshot *SSnapshot) GetDiskId() string { + return snapshot.VolumeID +} + +func (snapshot *SSnapshot) GetId() string { + return snapshot.ID +} + +func (snapshot *SSnapshot) GetGlobalId() string { + return snapshot.ID +} + +func (snapshot *SSnapshot) GetName() string { + return snapshot.Name +} + +func (snapshot *SSnapshot) Delete() error { + return snapshot.region.DeleteSnapshot(snapshot.ID) +} + +func (snapshot *SSnapshot) GetDiskType() string { + if len(snapshot.VolumeID) > 0 { + if disk, err := snapshot.region.GetDisk(snapshot.VolumeID); err == nil { + if disk.Bootable { + return models.DISK_TYPE_SYS + } + } + } + return models.DISK_TYPE_DATA +} + +func (snapshot *SRegion) DeleteSnapshot(snapshotId string) error { + return cloudprovider.ErrNotImplemented +} + +func (snapshot *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) { + return "", cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/storage.go b/pkg/util/openstack/storage.go new file mode 100644 index 0000000000..2662b4727b --- /dev/null +++ b/pkg/util/openstack/storage.go @@ -0,0 +1,111 @@ +package openstack + +import ( + "strings" + + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SStorage struct { + zone *SZone + Name string + ID string +} + +func (storage *SStorage) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (storage *SStorage) GetId() string { + return storage.ID +} + +func (storage *SStorage) GetName() string { + return storage.Name +} + +func (storage *SStorage) GetGlobalId() string { + return storage.ID +} + +func (storage *SStorage) IsEmulated() bool { + return true +} + +func (storage *SStorage) GetIZone() cloudprovider.ICloudZone { + return storage.zone +} + +func (storage *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := storage.zone.region.GetDisks(storage.Name) + if err != nil { + return nil, err + } + iDisks := []cloudprovider.ICloudDisk{} + for i := 0; i < len(disks); i++ { + disks[i].storage = storage + iDisks = append(iDisks, &disks[i]) + } + return iDisks, nil +} + +func (storage *SStorage) GetStorageType() string { + return strings.ToLower(storage.Name) +} + +func (storage *SStorage) GetMediumType() string { + if strings.Contains(storage.Name, "SSD") { + return models.DISK_TYPE_SSD + } + return models.DISK_TYPE_ROTATE +} + +func (storage *SStorage) GetCapacityMB() int { + return 0 // unlimited +} + +func (storage *SStorage) GetStorageConf() jsonutils.JSONObject { + conf := jsonutils.NewDict() + return conf +} + +func (storage *SStorage) GetManagerId() string { + return storage.zone.region.client.providerID +} + +func (storage *SStorage) GetStatus() string { + return models.STORAGE_ONLINE +} + +func (storage *SStorage) Refresh() error { + // do nothing + return nil +} + +func (storage *SStorage) GetEnabled() bool { + return true +} + +func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return storage.zone.region.getStoragecache() +} + +func (storage *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (storage *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { + disk, err := storage.zone.region.GetDisk(idStr) + if err != nil { + return nil, err + } + disk.storage = storage + return disk, nil +} + +func (storage *SStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/openstack/storagecache.go b/pkg/util/openstack/storagecache.go new file mode 100644 index 0000000000..6747cab66d --- /dev/null +++ b/pkg/util/openstack/storagecache.go @@ -0,0 +1,85 @@ +package openstack + +import ( + "fmt" + + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SStoragecache struct { + region *SRegion + + iimages []cloudprovider.ICloudImage +} + +func (cache *SStoragecache) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (cache *SStoragecache) GetId() string { + return fmt.Sprintf("%s-%s", cache.region.client.providerID, cache.region.GetId()) +} + +func (cache *SStoragecache) GetName() string { + return fmt.Sprintf("%s-%s", cache.region.client.providerName, cache.region.GetId()) +} + +func (cache *SStoragecache) GetStatus() string { + return "available" +} + +func (cache *SStoragecache) Refresh() error { + return nil +} + +func (cache *SStoragecache) GetGlobalId() string { + return fmt.Sprintf("%s-%s", cache.region.client.providerID, cache.region.GetGlobalId()) +} + +func (cache *SStoragecache) IsEmulated() bool { + return false +} + +func (cache *SStoragecache) GetManagerId() string { + return cache.region.client.providerID +} + +func (cache *SStoragecache) fetchImages() error { + return cloudprovider.ErrNotImplemented +} + +func (cache *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { + if cache.iimages == nil { + if err := cache.fetchImages(); err != nil { + return nil, err + } + } + return cache.iimages, nil +} + +func (cache *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (cache *SStoragecache) GetPath() string { + return "" +} + +func (cache *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist, osVersion string, extId string, isForce bool) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (cache *SStoragecache) uploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, isForce bool) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (cache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (cache *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/vpc.go b/pkg/util/openstack/vpc.go new file mode 100644 index 0000000000..513527affb --- /dev/null +++ b/pkg/util/openstack/vpc.go @@ -0,0 +1,217 @@ +package openstack + +import ( + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +const ( + VPC_STATUS_ACTIVE = "ACTIVE" + VPC_STATUS_DOWN = "DOWN" + VPC_STATUS_BUILD = "BUILD" + VPC_STATUS_ERROR = "ERROR" +) + +type SVpc struct { + region *SRegion + + iwires []cloudprovider.ICloudWire + secgroups []cloudprovider.ICloudSecurityGroup + + AdminStateUp bool + AvailabilityZoneHints []string + AvailabilityZones []string + CreatedAt time.Time + DnsDomain string + ID string + Ipv4AddressScope string + Ipv6AddressScope string + L2Adjacency bool + Mtu int + Name string + PortSecurityEnabled bool + ProjectID string + QosPolicyID string + RevisionNumber int + //router:external bool + Shared bool + Status string + Subnets []string + TenantID string + UpdatedAt time.Time + VlanTransparent bool + Fescription string + IsDefault bool +} + +func (vpc *SVpc) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (vpc *SVpc) GetId() string { + return vpc.ID +} + +func (vpc *SVpc) GetName() string { + if len(vpc.Name) > 0 { + return vpc.Name + } + return vpc.ID +} + +func (vpc *SVpc) GetGlobalId() string { + return vpc.ID +} + +func (vpc *SVpc) IsEmulated() bool { + return false +} + +func (vpc *SVpc) GetIsDefault() bool { + return vpc.IsDefault +} + +func (vpc *SVpc) GetCidrBlock() string { + return "" +} + +func (vpc *SVpc) GetStatus() string { + switch vpc.Status { + case VPC_STATUS_ACTIVE: + return models.VPC_STATUS_AVAILABLE + case VPC_STATUS_BUILD, VPC_STATUS_DOWN: + return models.VPC_STATUS_PENDING + case VPC_STATUS_ERROR: + return models.VPC_STATUS_FAILED + default: + return models.VPC_STATUS_UNKNOWN + } + return strings.ToLower(vpc.Status) +} + +func (vpc *SVpc) Delete() error { + return vpc.region.DeleteVpc(vpc.ID) +} + +func (region *SRegion) DeleteVpc(vpcId string) error { + return cloudprovider.ErrNotImplemented +} + +func (vpc *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) { + secgroups, err := vpc.region.GetSecurityGroups() + if err != nil { + return nil, err + } + iSecgroups := []cloudprovider.ICloudSecurityGroup{} + for i := 0; i < len(secgroups); i++ { + secgroups[i].vpc = vpc + iSecgroups = append(iSecgroups, &secgroups[i]) + } + return iSecgroups, nil +} + +func (vpc *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { + rts := []cloudprovider.ICloudRouteTable{} + return rts, nil +} + +func (vpc *SVpc) fetchWires() error { + if len(vpc.region.izones) == 0 { + if err := vpc.region.fetchZones(); err != nil { + return err + } + } + wire := SWire{zone: vpc.region.izones[0].(*SZone), vpc: vpc} + vpc.iwires = []cloudprovider.ICloudWire{&wire} + return nil +} + +func (vpc *SVpc) getWire() *SWire { + if vpc.iwires == nil { + vpc.fetchWires() + } + return vpc.iwires[0].(*SWire) +} + +func (vpc *SVpc) fetchNetworks() error { + networks, err := vpc.region.GetNetworks(vpc.ID) + if err != nil { + return err + } + for i := 0; i < len(networks); i++ { + wire := vpc.getWire() + networks[i].wire = wire + wire.addNetwork(&networks[i]) + } + return nil +} + +func (vpc *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) { + if vpc.iwires == nil { + err := vpc.fetchNetworks() + if err != nil { + return nil, err + } + } + for i := 0; i < len(vpc.iwires); i++ { + if vpc.iwires[i].GetGlobalId() == wireId { + return vpc.iwires[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (vpc *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) { + if vpc.iwires == nil { + err := vpc.fetchNetworks() + if err != nil { + return nil, err + } + } + return vpc.iwires, nil +} + +func (vpc *SVpc) GetManagerId() string { + return vpc.region.client.providerID +} + +func (vpc *SVpc) GetRegion() cloudprovider.ICloudRegion { + return vpc.region +} + +func (region *SRegion) GetVpc(vpcId string) (*SVpc, error) { + _, resp, err := region.Get("network", "/v2.0/networks/"+vpcId, "", nil) + if err != nil { + return nil, err + } + vpc := SVpc{} + return &vpc, resp.Unmarshal(&vpc, "network") +} + +func (region *SRegion) GetVpcs() ([]SVpc, error) { + _, resp, err := region.Get("network", "/v2.0/networks", "", nil) + if err != nil { + return nil, err + } + vpcs := []SVpc{} + return vpcs, resp.Unmarshal(&vpcs, "networks") +} + +func (vpc *SVpc) Refresh() error { + new, err := vpc.region.GetVpc(vpc.ID) + if err != nil { + return err + } + return jsonutils.Update(vpc, new) +} + +func (vpc *SVpc) addWire(wire *SWire) { + if vpc.iwires == nil { + vpc.iwires = make([]cloudprovider.ICloudWire, 0) + } + vpc.iwires = append(vpc.iwires, wire) +} diff --git a/pkg/util/openstack/wire.go b/pkg/util/openstack/wire.go new file mode 100644 index 0000000000..8673f2d7dc --- /dev/null +++ b/pkg/util/openstack/wire.go @@ -0,0 +1,99 @@ +package openstack + +import ( + "fmt" + + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SWire struct { + zone *SZone + vpc *SVpc + + inetworks []cloudprovider.ICloudNetwork +} + +func (wire *SWire) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (wire *SWire) GetId() string { + return fmt.Sprintf("%s-%s", wire.vpc.GetId(), wire.zone.GetId()) +} + +func (wire *SWire) GetName() string { + return wire.GetId() +} + +func (wire *SWire) IsEmulated() bool { + return true +} + +func (wire *SWire) GetStatus() string { + return "available" +} + +func (wire *SWire) Refresh() error { + return nil +} + +func (wire *SWire) GetGlobalId() string { + return fmt.Sprintf("%s-%s", wire.vpc.GetGlobalId(), wire.zone.GetGlobalId()) +} + +func (wire *SWire) GetIVpc() cloudprovider.ICloudVpc { + return wire.vpc +} + +func (wire *SWire) GetIZone() cloudprovider.ICloudZone { + return wire.zone +} + +func (wire *SWire) GetBandwidth() int { + return 10000 +} + +func (wire *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (wire *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) { + networks, err := wire.GetINetworks() + if err != nil { + return nil, err + } + for i := 0; i < len(networks); i++ { + if networks[i].GetGlobalId() == netid { + return networks[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (wire *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) { + if wire.inetworks == nil { + err := wire.vpc.fetchNetworks() + if err != nil { + return nil, err + } + } + return wire.inetworks, nil +} + +func (wire *SWire) addNetwork(network *SNetwork) { + if wire.inetworks == nil { + wire.inetworks = []cloudprovider.ICloudNetwork{} + } + find := false + for i := 0; i < len(wire.inetworks); i++ { + if wire.inetworks[i].GetGlobalId() == network.GetGlobalId() { + find = true + break + } + } + if !find { + wire.inetworks = append(wire.inetworks, network) + } +} diff --git a/pkg/util/openstack/zone.go b/pkg/util/openstack/zone.go index 37c091d5a9..4610148d75 100644 --- a/pkg/util/openstack/zone.go +++ b/pkg/util/openstack/zone.go @@ -4,6 +4,7 @@ import ( "fmt" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudcommon/version" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -16,6 +17,9 @@ type ZoneState struct { type SZone struct { region *SRegion + iwires []cloudprovider.ICloudWire + istorages []cloudprovider.ICloudStorage + ZoneName string ZoneState ZoneState } @@ -56,31 +60,82 @@ func (zone *SZone) GetIRegion() cloudprovider.ICloudRegion { return zone.region } -func (zone *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - // if self.istorages == nil { - // self.fetchStorages() - // } - // return self.istorages, nil - return nil, cloudprovider.ErrNotImplemented +func (zone *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) { + return zone.iwires, nil } -func (zone *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (zone *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) { - _, maxVersion, err := zone.region.GetVersion("compute") +func (zone *SZone) getStorageByCategory(category string) (*SStorage, error) { + storages, err := zone.GetIStorages() if err != nil { return nil, err } - if version.GT(maxVersion, "2.43") { + for i := 0; i < len(storages); i++ { + storage := storages[i].(*SStorage) + if storage.Name == category { + return storage, nil + } + } + return nil, fmt.Errorf("No such storage %s", category) +} + +func (zone *SZone) addWire(wire *SWire) { + if zone.iwires == nil { + zone.iwires = []cloudprovider.ICloudWire{} + } + zone.iwires = append(zone.iwires, wire) +} + +func (zone *SZone) fetchStorages() error { + zone.istorages = []cloudprovider.ICloudStorage{} + + for _, service := range []string{"volumev3", "volumev2", "volume"} { + _, resp, err := zone.region.Get(service, "/types", "", nil) + if err == nil { + storages := []SStorage{} + if err := resp.Unmarshal(&storages, "volume_types"); err != nil { + return err + } + for i := 0; i < len(storages); i++ { + storages[i].zone = zone + zone.istorages = append(zone.istorages, &storages[i]) + } + return nil + } + log.Debugf("failed to get volume types by service %s error: %v, try another", service, err) + } + return fmt.Errorf("failed to find storage types by cinder service") +} + +func (zone *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + if zone.istorages == nil { + zone.fetchStorages() + } + return zone.istorages, nil +} + +func (zone *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + if zone.istorages == nil { + zone.fetchStorages() + } + for i := 0; i < len(zone.istorages); i++ { + if zone.istorages[i].GetGlobalId() == id { + return zone.istorages[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (zone *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) { + // 2.28 Hypervisor CPU字段是字符串,会解析失败 + _, maxVersion, err := zone.region.GetVersion("compute") + if err == nil && version.GE(maxVersion, "2.28") { return zone.GetIHostsV3() } return zone.GetIHostsV2() } func (zone *SZone) GetIHostsV2() ([]cloudprovider.ICloudHost, error) { - _, resp, err := zone.region.Get("/os-hosts", "", nil) + _, resp, err := zone.region.Get("compute", "/os-hosts", "", nil) if err != nil { return nil, err } @@ -99,7 +154,8 @@ func (zone *SZone) GetIHostsV2() ([]cloudprovider.ICloudHost, error) { } func (zone *SZone) GetIHostsV3() ([]cloudprovider.ICloudHost, error) { - _, resp, err := zone.region.Get("/os-hypervisors/detail", "2.28", nil) + _, maxVersion, err := zone.region.GetVersion("compute") + _, resp, err := zone.region.Get("compute", "/os-hypervisors/detail", maxVersion, nil) if err != nil { return nil, err } @@ -117,29 +173,28 @@ func (zone *SZone) GetIHostsV3() ([]cloudprovider.ICloudHost, error) { func (zone *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) { _, maxVersion, err := zone.region.GetVersion("compute") - if err != nil { - return nil, err - } - if version.GT(maxVersion, "2.43") { + if err == nil && version.GE(maxVersion, "2.43") { return zone.GetIHostByIdV3(id) } return zone.GetIHostByIdV2(id) } func (zone *SZone) GetIHostByIdV3(id string) (cloudprovider.ICloudHost, error) { - - return nil, cloudprovider.ErrNotImplemented -} - -func (zone *SZone) GetIHostByIdV2(id string) (*SHostV2, error) { - _, resp, err := zone.region.Get("/os-hosts/"+id, "", nil) + _, maxVersion, err := zone.region.GetVersion("compute") + _, resp, err := zone.region.Get("compute", "/os-hypervisors/"+id, maxVersion, nil) if err != nil { return nil, err } - host := SHostV2{zone: zone, HostName: id, Resource: []map[string]SResource{}} - if err := resp.Unmarshal(&host.Resource, "host"); err != nil { + host := &SHostV3{zone: zone} + return host, resp.Unmarshal(&host, "hypervisor") +} + +func (zone *SZone) GetIHostByIdV2(id string) (*SHostV2, error) { + _, maxVersion, err := zone.region.GetVersion("compute") + _, resp, err := zone.region.Get("compute", "/os-hosts/"+id, maxVersion, nil) + if err != nil { return nil, err } - host.zone = zone - return &host, nil + host := &SHostV2{zone: zone, HostName: id, Resource: []map[string]SResource{}} + return host, resp.Unmarshal(&(host.Resource), "host") } diff --git a/pkg/webconsole/session/remote_console.go b/pkg/webconsole/session/remote_console.go index f3a4f40ba2..3059f8e916 100644 --- a/pkg/webconsole/session/remote_console.go +++ b/pkg/webconsole/session/remote_console.go @@ -10,11 +10,12 @@ import ( ) const ( - VNC = "vnc" - ALIYUN = "aliyun" - QCLOUD = "qcloud" - SPICE = "spice" - WMKS = "wmks" + VNC = "vnc" + ALIYUN = "aliyun" + QCLOUD = "qcloud" + OPENSTACK = "openstack" + SPICE = "spice" + WMKS = "wmks" ) type RemoteConsoleInfo struct { @@ -80,18 +81,23 @@ func (info *RemoteConsoleInfo) ShowInfo() string { } func (info *RemoteConsoleInfo) GetConnectParams() (string, error) { - if info.Protocol == ALIYUN { - return info.getAliyunUrl() + switch info.Protocol { + case ALIYUN: + return info.getAliyunURL() + case QCLOUD: + return info.getQcloudURL() + case OPENSTACK: + return info.getOpenStackURL() + default: + return "", fmt.Errorf("Can't convert protocol %s to connect params", info.Protocol) } - - if info.Protocol == QCLOUD { - return info.getQcloudUrl() - } - - return "", fmt.Errorf("Can't convert protocol %s to connect params", info.Protocol) } -func (info *RemoteConsoleInfo) getQcloudUrl() (string, error) { +func (info *RemoteConsoleInfo) getOpenStackURL() (string, error) { + return info.Url, nil +} + +func (info *RemoteConsoleInfo) getQcloudURL() (string, error) { base := "https://img.qcloud.com/qcloud/app/active_vnc/index.html" params := url.Values{ "InstanceVncUrl": {info.Url}, @@ -99,7 +105,7 @@ func (info *RemoteConsoleInfo) getQcloudUrl() (string, error) { return fmt.Sprintf("%s?%s", base, params.Encode()), nil } -func (info *RemoteConsoleInfo) getAliyunUrl() (string, error) { +func (info *RemoteConsoleInfo) getAliyunURL() (string, error) { isWindows := "False" if info.OsName == "Windows" { isWindows = "True" @@ -112,6 +118,6 @@ func (info *RemoteConsoleInfo) getAliyunUrl() (string, error) { "password": {info.Password}, "protocol": {info.Protocol}, } - queryUrl := params.Encode() - return fmt.Sprintf("%s?%s", base, queryUrl), nil + queryURL := params.Encode() + return fmt.Sprintf("%s?%s", base, queryURL), nil }