From 6751b387c6cd95bfa02b7ed5801967315369e655 Mon Sep 17 00:00:00 2001 From: Sean Oh Date: Wed, 15 Nov 2023 20:33:14 +0900 Subject: [PATCH] [+NHN Cloud conn. Driver] Add NHN Driver Codes --- .gitignore | 1 + build_all_driver_lib.sh | 2 +- .../nhncloud-plugin/NHNCloudDriver-lib.go | 221 ++ .../nhncloud-plugin/build_driver_lib.sh | 16 + .../drivers/nhncloud/NHNCloudDriver.go | 219 ++ .../cloud-driver/drivers/nhncloud/README.md | 135 ++ .../nhncloud/connect/NHN_CloudConnection.go | 144 ++ .../nhncloud/main/NHN_REST_test/1.export.env | 12 + .../main/NHN_REST_test/2.get_flavors.sh | 4 + .../NHN_REST_test/2.get_flavors_detail.sh | 4 + .../nhncloud/main/NHN_REST_test/2.get_nlbs.sh | 11 + .../main/NHN_REST_test/3.get_image.sh | 19 + .../main/NHN_REST_test/3.get_images.sh | 6 + .../main/NHN_REST_test/4.get_os-keypairs.sh | 4 + .../5.get_os-availability-zone.sh | 5 + .../6.create_lb_pool_members-post.sh | 19 + .../nhncloud/main/Test_ClusterHandler.go | 386 ++++ .../drivers/nhncloud/main/Test_DiskHandler.go | 252 +++ .../nhncloud/main/Test_ImageHandler.go | 285 +++ .../nhncloud/main/Test_KeyPairHandler.go | 281 +++ .../nhncloud/main/Test_MyImageHandler.go | 221 ++ .../drivers/nhncloud/main/Test_NLBHandler.go | 371 ++++ .../nhncloud/main/Test_RegionZoneHandler.go | 270 +++ .../nhncloud/main/Test_SecurityHandler.go | 540 +++++ .../drivers/nhncloud/main/Test_VMHandler.go | 390 ++++ .../nhncloud/main/Test_VMSpecHandler.go | 283 +++ .../drivers/nhncloud/main/Test_VPCHandler.go | 306 +++ .../nhncloud/main/conf/config.yaml.sample | 24 + .../drivers/nhncloud/main/goget-sdk.sh | 6 + .../main/pmks_test/ClusterHandler_test.go | 363 +++ .../main/pmks_test/conf/calllog_conf.yaml | 18 + .../main/pmks_test/conf/log_conf.yaml | 19 + .../drivers/nhncloud/main/test_cluster.sh | 3 + .../drivers/nhncloud/main/test_disk.sh | 3 + .../drivers/nhncloud/main/test_image.sh | 3 + .../drivers/nhncloud/main/test_keypair.sh | 3 + .../drivers/nhncloud/main/test_myimage.sh | 3 + .../drivers/nhncloud/main/test_nlb.sh | 3 + .../drivers/nhncloud/main/test_region-zone.sh | 3 + .../drivers/nhncloud/main/test_security.sh | 3 + .../drivers/nhncloud/main/test_vm.sh | 3 + .../drivers/nhncloud/main/test_vmspec.sh | 3 + .../drivers/nhncloud/main/test_vpc.sh | 3 + .../nhncloud/resources/ClusterHandler.go | 756 +++++++ .../nhncloud/resources/CommonNhnCloudFunc.go | 294 +++ .../drivers/nhncloud/resources/DiskHandler.go | 629 ++++++ .../nhncloud/resources/ImageHandler.go | 151 ++ .../nhncloud/resources/KeyPairHandler.go | 229 ++ .../nhncloud/resources/MyImageHandler.go | 434 ++++ .../drivers/nhncloud/resources/NLBHandler.go | 1959 +++++++++++++++++ .../nhncloud/resources/RegionZoneHandler.go | 294 +++ .../nhncloud/resources/SecurityHandler.go | 732 ++++++ .../drivers/nhncloud/resources/VMHandler.go | 1000 +++++++++ .../nhncloud/resources/VMSpecHandler.go | 210 ++ .../drivers/nhncloud/resources/VPCHandler.go | 368 ++++ .../nhncloud/resources/nhn_rest_utils.go | 309 +++ 56 files changed, 12234 insertions(+), 1 deletion(-) create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/NHNCloudDriver-lib.go create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/build_driver_lib.sh create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/NHNCloudDriver.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/README.md create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/connect/NHN_CloudConnection.go create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/1.export.env create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors_detail.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_nlbs.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_image.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_images.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/4.get_os-keypairs.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/5.get_os-availability-zone.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/6.create_lb_pool_members-post.sh create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ClusterHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_DiskHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ImageHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_KeyPairHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_MyImageHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_NLBHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_RegionZoneHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_SecurityHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMSpecHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VPCHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml.sample create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/goget-sdk.sh create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/ClusterHandler_test.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/calllog_conf.yaml create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/log_conf.yaml create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_cluster.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_disk.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_image.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_keypair.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_myimage.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_nlb.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_region-zone.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_security.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vm.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vmspec.sh create mode 100755 cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vpc.sh create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ClusterHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/CommonNhnCloudFunc.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/DiskHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ImageHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/KeyPairHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/MyImageHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/NLBHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/RegionZoneHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/SecurityHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMSpecHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VPCHandler.go create mode 100644 cloud-control-manager/cloud-driver/drivers/nhncloud/resources/nhn_rest_utils.go diff --git a/.gitignore b/.gitignore index 323fb2106..08970148b 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ cloud-control-manager/cloud-driver/drivers/openstack/main/conf/config.yaml cloud-control-manager/cloud-driver/drivers/tencent/main/conf/testConfigTencent.yaml cloud-control-manager/cloud-driver/drivers/ncp/main/config/config.yaml cloud-control-manager/cloud-driver/drivers/ncpvpc/main/config/config.yaml +cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml diff --git a/build_all_driver_lib.sh b/build_all_driver_lib.sh index c21d6f2fa..feafa04f7 100755 --- a/build_all_driver_lib.sh +++ b/build_all_driver_lib.sh @@ -2,7 +2,7 @@ source setup.env #DRIVERS=( aws-plugin azure-plugin openstack-plugin gcp-plugin alibaba-plugin cloudit-plugin docker-plugin ncp-plugin ncpvpc-plugin) -DRIVERS=( aws-plugin azure-plugin gcp-plugin alibaba-plugin openstack-plugin cloudit-plugin docker-plugin mock-plugin tencent-plugin ibmcloud-vpc-plugin ncp-plugin ncpvpc-plugin ) +DRIVERS=( aws-plugin azure-plugin gcp-plugin alibaba-plugin openstack-plugin cloudit-plugin docker-plugin mock-plugin tencent-plugin ibmcloud-vpc-plugin ncp-plugin ncpvpc-plugin nhncloud-plugin ) DRIVER_PATH=$CBSPIDER_ROOT/cloud-control-manager/cloud-driver/drivers DRIVERLIB_PATH=$CBSPIDER_ROOT/cloud-driver-libs diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/NHNCloudDriver-lib.go b/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/NHNCloudDriver-lib.go new file mode 100644 index 000000000..7ba2bbb55 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/NHNCloudDriver-lib.go @@ -0,0 +1,221 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI Team, 2022.08. + +package main + +import ( + // "github.com/davecgh/go-spew/spew" + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + ostack "github.com/cloud-barista/nhncloud-sdk-go/openstack" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + icon "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/connect" + + // nhncon "github.com/cloud-barista/nhncloud/nhncloud/connect" + nhncon "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud/connect" + + // nhnrs "github.com/cloud-barista/nhncloud/nhncloud/resources" + nhnrs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud/resources" +) + +type NhnCloudDriver struct{} + +func (NhnCloudDriver) GetDriverVersion() string { + return "NHNCLOUD DRIVER Version 1.0" +} + +func (NhnCloudDriver) GetDriverCapability() idrv.DriverCapabilityInfo { + var drvCapabilityInfo idrv.DriverCapabilityInfo + + drvCapabilityInfo.ImageHandler = true + drvCapabilityInfo.VPCHandler = true + drvCapabilityInfo.SecurityHandler = true + drvCapabilityInfo.KeyPairHandler = true + drvCapabilityInfo.VNicHandler = false + drvCapabilityInfo.PublicIPHandler = false + drvCapabilityInfo.VMHandler = true + drvCapabilityInfo.VMSpecHandler = true + drvCapabilityInfo.NLBHandler = true + drvCapabilityInfo.ClusterHandler = true + drvCapabilityInfo.MyImageHandler = true + drvCapabilityInfo.DiskHandler = true + + drvCapabilityInfo.SINGLE_VPC = true + + return drvCapabilityInfo +} + +func (driver *NhnCloudDriver) ConnectCloud(connectionInfo idrv.ConnectionInfo) (icon.CloudConnection, error) { + // 1. get info of credential and region for Test A Cloud from connectionInfo. + // 2. create a client object(or service object) of Test A Cloud with credential info. + // 3. create CloudConnection Instance of "connect/TDA_CloudConnection". + // 4. return CloudConnection Interface of TDA_CloudConnection. + + // Initialize Logger + nhnrs.InitLog() + + VMClient, err := getVMClient(connectionInfo) + if err != nil { + return nil, err + } + + ImageClient, err := getImageClient(connectionInfo) + if err != nil { + return nil, err + } + + NetworkClient, err := getNetworkClient(connectionInfo) + if err != nil { + return nil, err + } + + VolumeClient, err := getVolumeClient(connectionInfo) + if err != nil { + return nil, err + } + + ClusterClient, err := getClusterClient(connectionInfo) + if err != nil { + return nil, err + } + + iConn := nhncon.NhnCloudConnection{ + CredentialInfo: connectionInfo.CredentialInfo, // Note) Need in RegionZoneHandler + RegionInfo: connectionInfo.RegionInfo, + VMClient: VMClient, + ImageClient: ImageClient, + NetworkClient: NetworkClient, + VolumeClient: VolumeClient, + ClusterClient: ClusterClient, + } + return &iConn, nil +} + +func getVMClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewComputeV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getImageClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewImageServiceV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getNetworkClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewNetworkV2(providerClient, nhnsdk.EndpointOpts{ + Name: "neutron", + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getVolumeClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewBlockStorageV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + return client, err +} + +func getClusterClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewContainerInfraV1(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + return client, err +} + +var CloudDriver NhnCloudDriver diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/build_driver_lib.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/build_driver_lib.sh new file mode 100755 index 000000000..a5cd84f27 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud-plugin/build_driver_lib.sh @@ -0,0 +1,16 @@ +#!/bin/bash +source $CBSPIDER_ROOT/setup.env + +DRIVERLIB_PATH=$CBSPIDER_ROOT/cloud-driver-libs +DRIVERFILENAME=nhncloud-driver-v1.0 + +go mod download # cb-spider's go.mod and go.sum will be applied. + +function build() { + rm -rf $DRIVERLIB_PATH/${DRIVERFILENAME}.so + env GO111MODULE=on go build -buildmode=plugin -o ${DRIVERFILENAME}.so NHNCloudDriver-lib.go || return 1 + chmod +x ${DRIVERFILENAME}.so || return 1 + mv ./${DRIVERFILENAME}.so $DRIVERLIB_PATH || return 1 +} + +build \ No newline at end of file diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/NHNCloudDriver.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/NHNCloudDriver.go new file mode 100644 index 000000000..d30e7197d --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/NHNCloudDriver.go @@ -0,0 +1,219 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI Team, 2022.08. + +package nhncloud + +import ( + // "github.com/davecgh/go-spew/spew" + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + ostack "github.com/cloud-barista/nhncloud-sdk-go/openstack" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + icon "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/connect" + + // nhncon "github.com/cloud-barista/nhncloud/nhncloud/connect" + nhncon "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud/connect" + + // nhnrs "github.com/cloud-barista/nhncloud/nhncloud/resources" + nhnrs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud/resources" +) + +type NhnCloudDriver struct{} + +func (NhnCloudDriver) GetDriverVersion() string { + return "NHNCLOUD DRIVER Version 1.0" +} + +func (NhnCloudDriver) GetDriverCapability() idrv.DriverCapabilityInfo { + var drvCapabilityInfo idrv.DriverCapabilityInfo + + drvCapabilityInfo.ImageHandler = true + drvCapabilityInfo.VPCHandler = true + drvCapabilityInfo.SecurityHandler = true + drvCapabilityInfo.KeyPairHandler = true + drvCapabilityInfo.VNicHandler = false + drvCapabilityInfo.PublicIPHandler = false + drvCapabilityInfo.VMHandler = true + drvCapabilityInfo.VMSpecHandler = true + drvCapabilityInfo.NLBHandler = true + drvCapabilityInfo.ClusterHandler = true + drvCapabilityInfo.MyImageHandler = true + drvCapabilityInfo.DiskHandler = true + + drvCapabilityInfo.SINGLE_VPC = true + + return drvCapabilityInfo +} + +func (driver *NhnCloudDriver) ConnectCloud(connectionInfo idrv.ConnectionInfo) (icon.CloudConnection, error) { + // 1. get info of credential and region for Test A Cloud from connectionInfo. + // 2. create a client object(or service object) of Test A Cloud with credential info. + // 3. create CloudConnection Instance of "connect/TDA_CloudConnection". + // 4. return CloudConnection Interface of TDA_CloudConnection. + + // Initialize Logger + nhnrs.InitLog() + + VMClient, err := getVMClient(connectionInfo) + if err != nil { + return nil, err + } + + ImageClient, err := getImageClient(connectionInfo) + if err != nil { + return nil, err + } + + NetworkClient, err := getNetworkClient(connectionInfo) + if err != nil { + return nil, err + } + + VolumeClient, err := getVolumeClient(connectionInfo) + if err != nil { + return nil, err + } + + ClusterClient, err := getClusterClient(connectionInfo) + if err != nil { + return nil, err + } + + iConn := nhncon.NhnCloudConnection{ + CredentialInfo: connectionInfo.CredentialInfo, // Note) Need in RegionZoneHandler + RegionInfo: connectionInfo.RegionInfo, + VMClient: VMClient, + ImageClient: ImageClient, + NetworkClient: NetworkClient, + VolumeClient: VolumeClient, + ClusterClient: ClusterClient, + } + return &iConn, nil +} + +func getVMClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewComputeV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getImageClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewImageServiceV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getNetworkClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewNetworkV2(providerClient, nhnsdk.EndpointOpts{ + Name: "neutron", + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + + return client, err +} + +func getVolumeClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewBlockStorageV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + return client, err +} + +func getClusterClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + return nil, err + } + + client, err := ostack.NewContainerInfraV1(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + return nil, err + } + return client, err +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/README.md b/cloud-control-manager/cloud-driver/drivers/nhncloud/README.md new file mode 100644 index 000000000..07113ab9a --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/README.md @@ -0,0 +1,135 @@ +### NHN Cloud 연동 driver Build 및 CB-Spider에 적용 방법 + +#### # 연동 Driver 관련 기본적인 사항은 아래 link 참고 + + - [Cloud Driver Developer Guide](https://github.com/cloud-barista/cb-spider/wiki/Cloud-Driver-Developer-Guide) +


+ +#### # CB-Spider에 NHN Cloud 연동 driver 적용 방법 + +​ O CB-Spider 코드가 clone된 상태에서 setup 파일 적용 +``` +$CBSPIDER_ROOT/ source setup.env +``` + +​ O Dynamic plugin mode로 CB-Spider build 실행 + +``` +cd $CBSPIDER_ROOT + +make dyna + +``` + - Build 과정이 완료되면, $CBSPIDER_ROOT/bin/에 binary 파일로 'cb-spider-dyna' 가 생김 + +


+ +​ O CB-Spider server 구동(Dynamic plugin 방식, 1024 포트로 REST API Server 구동됨) + +``` +cd bin + +./start-dyna.sh +``` + + - CB-Spider server가 구동된 후, NHN Cloud 연동 driver 등록 과정을 거치고 사용 + +


+ +#### # CB-Spider에 NHN Cloud 연동 driver 테스트 방법 + +​ O NHN Cloud connection config 파일에 NHN Cloud Credential 정보(Username, TenantId 등) 기입 후 실행
+ +``` +$CBSPIDER_ROOT/api-runtime/rest-runtime/test/connect-config/ ./13.nhncloud-conn-config.sh +``` +


+ +#### # CB-Spider REST API 이용 NHN Cloud 연동 driver 모든 기능 테스트 + +​ O NHN Cloud 각 자원 생성, 자원정보 조회, VM instance Start 및 Terminate 테스트 등 + +- Curl command를 이용하는 테스트 script +``` +$CBSPIDER_ROOT/api-runtime/rest-runtime/test/full-test/13.nhncloud-test.sh +$CBSPIDER_ROOT/api-runtime/rest-runtime/test/full-test/nhncloud-full_test.sh +``` +


+ +#### # NHN Cloud driver 자체 test 파일을 이용한 기능 테스트 + +​ O CB-Spider 환경 파일 적용 +``` +$CBSPIDER_ROOT/ source setup.env +``` + +​ O 아래의 config 파일에 NHN Cloud Credential 정보 기입 +``` +$HOME/go/src/github.com/cloud-barista/nhncloud/nhncloud/main/conf/config.yaml +``` + +​ O 아래의 위치에 있는 ~.sh 파일을 실행해서 NHN Cloud driver 각 handler 세부 기능 테스트 +``` +$HOME/go/src/github.com/cloud-barista/nhncloud/nhncloud/main/ +``` +


+ +#### # NHN Cloud driver로 생성된 VM에 로그인하는 방법 + +​ O Cloud-Barista NHN Cloud driver를 이용해 생성된 VM에 로그인하는 방법 + + - KeyPair 생성시 생성되는 private key를 저장해놓고, 그 private 키를 이용해 'cb-user' 계정으로 SSH를 이용해 로그인하는 방법을 제공함. + + - Private key 파일을 이용한 VM 접속 방법 + +``` +ssh -i /private_key_파일_경로/private_key_파일명(~~.pem) cb-user@해당_VM의_public_ip +``` +


+ +#### # NHN Cloud driver 사용시 참고 및 주의 사항 +​ O NHN Cloud driver를 CB-Spider에 연동하여 이용할 때, Spider에서 log level을 아래와 같이 설정하면 Spider server 구동 후 NHN Cloud driver의 동작 상태에 대한 상세 log 정보를 확인할 수 있음. + - CB-Spider log 설정 파일에서 loglevel을 'info'나 'debug' 로 설정 + - CB-Spider log 설정 파일 위치 : ./cb-spider/conf/log_conf.yaml + +​ O NHN Cloud infra가 논리적 네트워크 기반이지만, NHN Cloud에서 VPC 및 Subnet 생성/삭제 API는 지원하지 않고 조회 API만 제공하므로 본 driver에서는 아래와 같은 제약 사항이 있음. + - 현재는 사용자가 원하는 이름으로 VPC와 Subnet을 생성하면, driver 내부적으로 NHN Cloud에서 project별, region별로 기본적으로 생성되어 제공하는 'Default Network' VPC와 'Default Network' Subnet을 사용하게됨. + - Cloud-Barista를 통해 생성시, VPC와 Subnet의 이름(NameId)은 사용자가 원하는 이름으로 사용 가능하고, driver 내부적으로 VPC와 Subnet의 SystemId는 기본적으로 생성되어 있는 'Default Network' VPC와 'Default Network' Subnet의 SystemId가 적용됨. + - 'Default Network' VPC CIDR : 192.168.0.0/16, 'Default Network' Subnet CIDR : 192.168.0.0/24 + - (주의) 위와 같은 제약에 대한 사용상의 문제를 최소화하기 위해, 하나의 project 내의 동일한 region에서는 위의 VPC를 이용해 하나의 VPC만 생성하도록 제한하고있음. + - (참고) 추후 NHN Cloud에서 VPC/Subnet 생성/삭제 API를 제공하면, driver에서 사용자가 원하는 CIDR 대역의 VPC와 Subnet이 생성 가능한 완전한 기능을 지원하도록 보완할 예정임. + +​ O NHN Cloud driver를 통해 VM instance 생성시, VMSpec type별로 지원하는 root disk type와 volume 크기가 다름.(아래는 CB-Spider 기준으로 지정 가능한 option임.) + - u2.~~~ type의 VMSpec은 RootDiskType으로 default인 'General_HDD'만을 지원하고, RootDiskSize는 VMSpec별로 지정된 size를 지원함. + - 가용 RootDiskType : ""(Blank, Not specified), 'default', 'General_HDD', 'TYPE1' + - 가용 RootDiskSize : VMSpec 조회시 spec별 지원 disk size 확인 + - u2.~~~ type 외의 VMSpec을 지정할 경우에는 RootDiskType과 RootDiskSize를 지정해야함. + - 가용 RootDiskType : ""(Blank, Not specified), 'default', 'General_HDD', 'General_SSD', 'TYPE1', 'TYPE2' + - 가용 RootDiskSize : 20 ~ 1000(GB) + - 이 외의 잘못된 RootDiskType이나 RootDiskSize를 지정하면 오류 메시지로 안내함. + - u2.~~~ type 외의 VMSpec 일때 RootDiskType과 RootDiskSize를 지정하지 않아도 정상적으로 VM이 생성되는데, 지정하지않을 경우 default 값으로써 RootDiskType은 'General_HDD' type이 지정되고, RootDiskSize로 20G가 지정됨. + - VMSpec type별로 가용한 RootDiskType/RootDiskSize 설정 option 및 그에 대한 disk 생성 결과 정리 + - https://github.com/cloud-barista/cb-spider/issues/598#issuecomment-1097610395 + + - (참고) NHN Cloud driver를 통해 VMSpec 조회시, u2 type의 VMSpec은 LocalDiskSize가 나타나고, u2 type을 제외한 VMSpec type은 LocalDiskSize가 '0'으로 나타남. + + - 반드시 참고해야할 사항(NHN Cloud에서 u2 type 선택시 명시되는 주의 사항) + - NHN Cloud에서 u2 type의 VM instance는 root volume을 local disk로 제공함. + - 따라서, 하드웨어 장애 시 사용자 instance는 생성 당시의 상태로 제공될 수 있으며, 이때 데이터의 복구는 불가능하니 별도로 백업하거나 이중화할 것을 권장함. + - U2 instance는 NHN Cloud 서비스 이용약관 제34조에 따른 손해배상 대상에서 제외됨. + +​ O NHN Cloud driver를 통해 Security Group을 생성하거나 Security Rule을 추가시 inbound/outbound에 대해 IPProtocol : "ALL", FromPort: "-1", ToPort: "-1"을 입력하면, 모든 Protocol에 대해 모든 영역 port가 open됨. + - Security Rule을 제거시에 동일하게 설정하면, 모든 Protocol에 대해 모든 영역 port가 open된 Rule이 제거됨. + +​ O NHN Cloud driver를 통해 VPC/Subnet을 제어할 경우, 응답 시간이 소요되므로 참고해야함. + - NHN Cloud API를 통해 VPC/Subnet 정보 조회 등의 제어시 타 CSP에 비해 소요시간이 큼. + + ​O NHN Cloud driver를 통해 MyImage 생성시, 다음 사항을 참고 + - Snapshot 대상의 VM에 RootDisk 외에 attach된 disk가 있을 경우, attach된 disk는 제외하고 RootDisk만으로 MyImage가 생성됨. + + ​O NHN Cloud driver를 통해 MyImage를 이용한 VM 생성시, 다음 사항을 참고 + - u2.~~~ type의 VMSpec으로 생성되었던 VM을 기준으로 생성된 MyImage는 그 VM의 local disk가 MyImage로 생성됨. + - (주의) 이와 같이, u2 type의 VMSpec으로 생성된 VM의 MyImage를 이용해 신규 VM을 생성할 경우, 그 신규 VM도 u2 type의 VMSpec을 이용해야함. + +​ O 미국 region infra는 사용 불가 (한국 2개 region, 일본 1개 region만 사용 가능) + - NHN Cloud에서 미국 region은 API endpoint를 제공하지 않으므로 미국 region은 console을 통해서만 사용 가능 diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/connect/NHN_CloudConnection.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/connect/NHN_CloudConnection.go new file mode 100644 index 000000000..487064e0b --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/connect/NHN_CloudConnection.go @@ -0,0 +1,144 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI Team, 2021.12. +// by ETRI Team, 2022.08. + +package connect + +import ( + "fmt" + "github.com/sirupsen/logrus" + "github.com/davecgh/go-spew/spew" + + cblog "github.com/cloud-barista/cb-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + + // nhnrs "github.com/cloud-barista/nhncloud/nhncloud/resources" + nhnrs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud/resources" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("CB-SPIDER") +} + +type NhnCloudConnection struct { + CredentialInfo idrv.CredentialInfo + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + ImageClient *nhnsdk.ServiceClient + NetworkClient *nhnsdk.ServiceClient + VolumeClient *nhnsdk.ServiceClient + ClusterClient *nhnsdk.ServiceClient +} + +func (cloudConn *NhnCloudConnection) CreateVMHandler() (irs.VMHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateVMHandler()!") + vmHandler := nhnrs.NhnCloudVMHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, ImageClient: cloudConn.ImageClient, NetworkClient: cloudConn.NetworkClient, VolumeClient: cloudConn.VolumeClient} + + return &vmHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateImageHandler() (irs.ImageHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateImageHandler()!") + imageHandler := nhnrs.NhnCloudImageHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, ImageClient: cloudConn.ImageClient} + + return &imageHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateVMSpecHandler() (irs.VMSpecHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateVMSpecHandler()!") + vmSpecHandler := nhnrs.NhnCloudVMSpecHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient} + + return &vmSpecHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateKeyPairHandler() (irs.KeyPairHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateKeyPairHandler()!") + keypairHandler := nhnrs.NhnCloudKeyPairHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient} + + return &keypairHandler, nil +} + +func (cloudConn NhnCloudConnection) CreateSecurityHandler() (irs.SecurityHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateSecurityHandler()!") + securityHandler := nhnrs.NhnCloudSecurityHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, NetworkClient: cloudConn.NetworkClient} + + return &securityHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateVPCHandler() (irs.VPCHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateVPCHandler()!") + vpcHandler := nhnrs.NhnCloudVPCHandler{RegionInfo: cloudConn.RegionInfo, NetworkClient: cloudConn.NetworkClient} + + return &vpcHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateNLBHandler() (irs.NLBHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateNLBHandler()!") + nlbHandler := nhnrs.NhnCloudNLBHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, NetworkClient: cloudConn.NetworkClient} //Caution!! : No NLBClient + + return &nlbHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateDiskHandler() (irs.DiskHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateDiskHandler()!") + cblogger.Info("\n### cloudConn.RegionInfo : ") + spew.Dump(cloudConn.RegionInfo) + cblogger.Info("\n") + + diskHandler := nhnrs.NhnCloudDiskHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, VolumeClient: cloudConn.VolumeClient} + + return &diskHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateClusterHandler() (irs.ClusterHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateClusterHandler()!") + clusterHandler := nhnrs.NhnCloudClusterHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, ClusterClient: cloudConn.ClusterClient} + + return &clusterHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateMyImageHandler() (irs.MyImageHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateMyImageHandler()!") + myimageHandler := nhnrs.NhnCloudMyImageHandler{RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient, ImageClient: cloudConn.ImageClient, NetworkClient: cloudConn.NetworkClient, VolumeClient: cloudConn.VolumeClient} + + return &myimageHandler, nil +} + +func (cloudConn *NhnCloudConnection) CreateAnyCallHandler() (irs.AnyCallHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateAnyCallHandler()!") + + return nil, fmt.Errorf("NHN Cloud Driver does not support CreateAnyCallHandler yet.") +} + +func (cloudConn *NhnCloudConnection) CreateRegionZoneHandler() (irs.RegionZoneHandler, error) { + cblogger.Info("NhnCloud Cloud Driver: called CreateRegionZoneHandler()!") + + regionZoneHandler := nhnrs.NhnCloudRegionZoneHandler{CredentialInfo: cloudConn.CredentialInfo, RegionInfo: cloudConn.RegionInfo, VMClient: cloudConn.VMClient} + return ®ionZoneHandler, nil +} + +func (cloudConn *NhnCloudConnection) IsConnected() (bool, error) { + if cloudConn == nil { + return false, nil + } + + return true, nil +} + +func (cloudConn *NhnCloudConnection) Close() error { + + return nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/1.export.env b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/1.export.env new file mode 100755 index 000000000..8fe683f4b --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/1.export.env @@ -0,0 +1,12 @@ +#!/bin/bash +export OS_TOKEN="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +# If Region : KR1 +export OS_COMPUTE_API="https://kr1-api-instance.infrastructure.cloud.toast.com/v2/" +export OS_IMAGE_API="https://kr1-api-image.infrastructure.cloud.toast.com/v2/" +# COMPUTE_API, IMAGE_API => /v2/ + +export OS_NETWORK_API="https://kr1-api-network.infrastructure.cloud.toast.com/" +# Caution!! + +export TENANT_ID="~~~~~~~~~~~~~~~~~~~" diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors.sh new file mode 100755 index 000000000..e811c09e9 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source ./1.export.env + +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/flavors diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors_detail.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors_detail.sh new file mode 100755 index 000000000..e09091843 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_flavors_detail.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source ./1.export.env + +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/flavors/detail diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_nlbs.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_nlbs.sh new file mode 100755 index 000000000..866449a25 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/2.get_nlbs.sh @@ -0,0 +1,11 @@ +#!/bin/bash +source ./1.export.env + +# curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_NETWORK_API/$TENANT_ID/lbaas/loadbalancers : (X) Caution!! + +echo -e "\n### LoadBalancers" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_NETWORK_API/v2.0/lbaas/loadbalancers + +echo -e "\n\n### Listeners" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_NETWORK_API/v2.0/lbaas/listeners +echo -e "\n" \ No newline at end of file diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_image.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_image.sh new file mode 100755 index 000000000..ca24dbb79 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_image.sh @@ -0,0 +1,19 @@ +#!/bin/bash +source ./1.export.env + +echo "### OS_COMPUTE_API" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/images/5396655e-166a-4875-80d2-ed8613aa054f + +echo -e "\n" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/images/1c868787-6207-4ff2-a1e7-ae1331d6829b + +#curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_IMAGE_API/$TENANT_ID/images/1c868787-6207-4ff2-a1e7-ae1331d6829ba # (X) +echo -e "\n\n" + +echo "### OS_IMAGE_API" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_IMAGE_API/images/5396655e-166a-4875-80d2-ed8613aa054f + +echo -e "\n" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_IMAGE_API/images/1c868787-6207-4ff2-a1e7-ae1331d6829b + +echo -e "\n" diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_images.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_images.sh new file mode 100755 index 000000000..deda74c8a --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/3.get_images.sh @@ -0,0 +1,6 @@ +#!/bin/bash +source ./1.export.env + +#curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_IMAGE_API/$TENANT_ID/images # (X) + +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_IMAGE_API/images diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/4.get_os-keypairs.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/4.get_os-keypairs.sh new file mode 100755 index 000000000..e3a5522b5 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/4.get_os-keypairs.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source ./1.export.env + +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/os-keypairs diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/5.get_os-availability-zone.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/5.get_os-availability-zone.sh new file mode 100755 index 000000000..8529b1cff --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/5.get_os-availability-zone.sh @@ -0,0 +1,5 @@ +#!/bin/bash +source ./1.export.env + +# echo "curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/os-availability-zone" +curl -s -H "X-Auth-Token: $OS_TOKEN" $OS_COMPUTE_API/$TENANT_ID/os-availability-zone diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/6.create_lb_pool_members-post.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/6.create_lb_pool_members-post.sh new file mode 100755 index 000000000..e49f50b9b --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/NHN_REST_test/6.create_lb_pool_members-post.sh @@ -0,0 +1,19 @@ +#!/bin/bash +source ./1.export.env + +# XXXXX : Pool ID +curl -v -s -X POST 'https://kr1-api-network.infrastructure.cloud.toast.com/v2.0/lbaas/pools/XXXXX/members' -H "X-Auth-Token: $OS_TOKEN" --header 'Content-Type: application/json' \ +--data-raw ' +{ + "poolId": "XXXXX", + "member": { + "weight": 1, + "admin_state_up": true, + "subnet_id": "XXXXX", + "address": "192.168.0.45", + "protocol_port": 8080 + } +}' ; echo + +# poolId <= 대소문자 주의!! +# 최종 결론 : poolId는 불필요함. NHN Cloud 매뉴얼이 잘못됨. diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ClusterHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ClusterHandler.go new file mode 100644 index 000000000..ae42eeef9 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ClusterHandler.go @@ -0,0 +1,386 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.0512. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func testErr() error { + + return errors.New("") +} + +// Test Cluster +func handleCluster() { + cblogger.Debug("Start Cluster Resource Test") + + ResourceHandler, err := getResourceHandler("Cluster") + if err != nil { + panic(err) + } + //config := readConfigFile() + + clusterHandler := ResourceHandler.(irs.ClusterHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ Cluster Management Test ]") + fmt.Println("1. Create Cluster") + fmt.Println("2. List Cluster") + fmt.Println("3. Get Cluster") + fmt.Println("4. Delete Cluster") + fmt.Println("5. Add NodeGroup") + fmt.Println("6. List NodeGroup") + fmt.Println("7. Get NodeGroup") + fmt.Println("8. Set NodeGroup AutoScaling") + fmt.Println("9. Change NodeGroup Scale") + fmt.Println("10. Remove NodeGroup") + fmt.Println("11. Upgrade Cluster") + fmt.Println("0. Quit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + var commandNum int + + clusterIId := irs.IID{ // For Get()/Delete() Test + NameId: "nhn-cluster-01", + SystemId: "b80c6c3d-7db7-4aaa-9aa7-0891c7e95f89", + } + + nodeGroupIId := irs.IID{ // For Get()/ChangeNodeGroupScaling()/Remove() Test + NameId: "nhn-nodeGroup-01", + SystemId: "b80c6c3d-7db7-4aaa-9aa7-0891c7e95f89", + } + + networkInfo := irs.NetworkInfo{ + VpcIID: irs.IID{ + NameId: "nhn-vpc-01", + SystemId: "b80c6c3d-7db7-4aaa-9aa7-0891c7e95f89", + }, + SubnetIIDs: []irs.IID{ + { + NameId: "nhn-subnet-01", + SystemId: "b80c6c3d-7db7-4aaa-9aa7-0891c7e95f89", + }, + }, + SecurityGroupIIDs: []irs.IID{ + { + NameId: "nhn-sg-01", + SystemId: "b80c6c3d-7db7-4aaa-9aa7-0891c7e95f89", + }, + }, + } + + nodeReqInfolist := []irs.NodeGroupInfo{ + { + IId: irs.IID{ + NameId: "nhn-nodeGroup-01", + }, + + // ImageIID IID + // VMSpecName string + // RootDiskType string // "SSD(gp2)", "Premium SSD", ... + // RootDiskSize string // "", "default", "50", "1000" (GB) + // KeyPairIID IID + }, + } + + addNodeReqInfo := irs.NodeGroupInfo{ // For AddNodeGroup() Test + IId: irs.IID{ + NameId: "nhn-nodeGroup-02", + }, + + // ImageIID IID + // VMSpecName string + // RootDiskType string // "SSD(gp2)", "Premium SSD", ... + // RootDiskSize string // "", "default", "50", "1000" (GB) + // KeyPairIID IID + } + + clusterReqInfo := irs.ClusterInfo{ + IId: irs.IID{ + NameId: "nhn-cluster-01", + }, + Version: "1.22.12", // Kubernetes Version, ex) 1.23.3 + Network: networkInfo, + NodeGroupList: nodeReqInfolist, + // Addons AddonsInfo + } + + desiredNodeSize := 10 + minNodeSize := 5 + maxNodeSize := 20 + setNodeGroupAutoScaling := true + newClusterVersion := "1.23.9" + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + cblogger.Info("Start CreateCluster() ...") + result, err := clusterHandler.CreateCluster(clusterReqInfo) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# Cluster 생성 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("CreateCluster Test Finished!!") + + case 2: + cblogger.Info("Start ListCluster() ...") + result, err := clusterHandler.ListCluster() + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# Cluster list 조회 결과 : \n") + spew.Dump(result) + + cblogger.Infof("=========== Cluster list 수 : [%d] ================", len(result)) + } + + cblogger.Info("ListCluster Test Finished!!") + + case 3: + cblogger.Info("Start GetCluster() ...") + result, err := clusterHandler.GetCluster(clusterIId) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# Cluster 조회 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("GetCluster Test Finished!!") + + case 4: + cblogger.Info("Start DeleteCluster() ...") + result, err := clusterHandler.DeleteCluster(clusterIId) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# Cluster 삭제 결과 : ") + spew.Dump(result) + } + + cblogger.Info("DeleteCluster Test Finished!!") + + case 5: + cblogger.Info("Start AddNodeGroup() ...") + result, err := clusterHandler.AddNodeGroup(clusterIId, addNodeReqInfo) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# AddNodeGroup 결과 : ") + spew.Dump(result) + } + + cblogger.Info("AddNodeGroup Test Finished!!") + + case 6: + cblogger.Info("Start ListNodeGroup() ...") + result, err := clusterHandler.ListNodeGroup(clusterIId) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# Cluster list 조회 결과 : \n") + spew.Dump(result) + + cblogger.Infof("=========== NodeGroup list 수 : [%d] ================", len(result)) + } + + cblogger.Info("ListNodeGroup Test Finished!!") + + case 7: + cblogger.Info("Start GetNodeGroup() ...") + result, err := clusterHandler.GetNodeGroup(clusterIId, nodeGroupIId) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# GetNodeGroup 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("GetNodeGroup Test Finished!!") + + case 8: + cblogger.Info("Start SetNodeGroupAutoScaling() ...") + result, err := clusterHandler.SetNodeGroupAutoScaling(clusterIId, nodeGroupIId, setNodeGroupAutoScaling) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# SetNodeGroupAutoScaling 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("SetNodeGroupAutoScaling Test Finished!!") + + case 9: + cblogger.Info("Start ChangeNodeGroupScaling() ...") + result, err := clusterHandler.ChangeNodeGroupScaling(clusterIId, nodeGroupIId, desiredNodeSize, minNodeSize, maxNodeSize) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# ChangeNodeGroupScaling 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("ChangeNodeGroupScaling Test Finished!!") + + case 10: + cblogger.Info("Start RemoveNodeGroup() ...") + result, err := clusterHandler.RemoveNodeGroup(clusterIId, nodeGroupIId) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# RemoveNodeGroup 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("RemoveNodeGroup Test Finished!!") + + case 11: + cblogger.Info("Start RemoveNodeGroup() ...") + result, err := clusterHandler.UpgradeCluster(clusterIId, newClusterVersion) + if err != nil { + cblogger.Error(err) + } else { + cblogger.Info("# RemoveNodeGroup 결과 : \n") + spew.Dump(result) + } + + cblogger.Info("RemoveNodeGroup Test Finished!!") + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleCluster() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "KeyPair": + resourceHandler, err = cloudConnection.CreateKeyPairHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "Cluster": + resourceHandler, err = cloudConnection.CreateClusterHandler() + } + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_DiskHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_DiskHandler.go new file mode 100644 index 000000000..2fef2f764 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_DiskHandler.go @@ -0,0 +1,252 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2022.08. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func handleDisk() { + cblogger.Debug("Start Disk Resource Test") + + resourceHandler, err := getResourceHandler("Disk") + if err != nil { + cblogger.Error(err) + return + } + diskHandler := resourceHandler.(irs.DiskHandler) + //config := readConfigFile() + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ DiskHandler Test ]") + cblogger.Info("1. ListDisk()") + cblogger.Info("2. GetDisk()") + cblogger.Info("3. CreateDisk()") + cblogger.Info("4. DeleteDisk()") + cblogger.Info("5. ChangeDiskSize()") + cblogger.Info("6. AttachDisk()") + cblogger.Info("7. DetachDisk()") + cblogger.Info("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + var commandNum int + + diskIId := irs.IID{ + NameId: "nhn-disk-01", + SystemId: "8c011f4f-c9ec-4700-b63c-9b2dba4fb20d", + } + + createReqInfo := irs.DiskInfo{ + IId: irs.IID{ + NameId: "nhn-disk-02", + }, + DiskType: "default", + // DiskType: "General_HDD", + // DiskType: "General_SSD", + DiskSize: "default", + // DiskSize: "50", + } + + vmIId := irs.IID{ // To attach disk + NameId: "nhn-vm-03", + SystemId: "b2d959f2-4755-4822-8d25-26651d9bc572", + } + + newDiskSize := "100" + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + case 1: + cblogger.Info("Start ListDisk() ...") + if listResult, err := diskHandler.ListDisk(); err != nil { + cblogger.Error(err) + } else { + spew.Dump(listResult) + cblogger.Info("# 출력 결과 수 : ", len(listResult)) + } + cblogger.Info("Finish ListDisk()") + case 2: + cblogger.Info("Start GetDisk() ...") + if diskInfo, err := diskHandler.GetDisk(diskIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish GetDisk()") + case 3: + cblogger.Info("Start CreateDisk() ...") + if diskInfo, err := diskHandler.CreateDisk(createReqInfo); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish CreateDisk()") + case 4: + cblogger.Info("Start DeleteDisk() ...") + if delResult, err := diskHandler.DeleteDisk(diskIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(delResult) + } + cblogger.Info("Finish DeleteDisk()") + case 5: + cblogger.Info("Start ChangeDiskSize() ...") + if diskInfo, err := diskHandler.ChangeDiskSize(diskIId, newDiskSize); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish ChangeDiskSize()") + case 6: + cblogger.Info("Start AttachDisk() ...") + if diskInfo, err := diskHandler.AttachDisk(diskIId, vmIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish AttachDisk()") + case 7: + cblogger.Info("Start DetachDisk() ...") + if result, err := diskHandler.DetachDisk(diskIId, vmIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(result) + } + cblogger.Info("Finish DetachDisk()") + } + } + } +} + +func testErr() error { + + return errors.New("") +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + handleDisk() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "Disk": + resourceHandler, err = cloudConnection.CreateDiskHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ImageHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ImageHandler.go new file mode 100644 index 000000000..30d0c6744 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_ImageHandler.go @@ -0,0 +1,285 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.12. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func handleImage() { + cblogger.Debug("Start ImageHandler Resource Test") + + ResourceHandler, err := getResourceHandler("Image") + if err != nil { + panic(err) + } + + handler := ResourceHandler.(irs.ImageHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ Image Management Test ]") + fmt.Println("1. Image List") + fmt.Println("2. Image Get") + fmt.Println("3. Image Create (TBD)") + fmt.Println("4. Image Delete (TBD)") + fmt.Println("0. Quit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + var commandNum int + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + imageReqInfo := irs.ImageReqInfo{ + IId: irs.IID{NameId: "Ubuntu Server 18.04.6 LTS (2021.12.21)", SystemId: "5396655e-166a-4875-80d2-ed8613aa054f"}, + //NHN Cloud : Ubuntu Server 18.04.6 LTS (2021.12.21) + + //IId: irs.IID{NameId: "CentOS 6.10 (2018.10.23)", SystemId: "1c868787-6207-4ff2-a1e7-ae1331d6829b"}, + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + cblogger.Infof("Image list 조회 테스트") + + result, err := handler.ListImage() + if err != nil { + cblogger.Error(err) + cblogger.Error("Image list 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================================") + cblogger.Info("Image list 조회 결과") + //cblogger.Info(result) + cblogger.Info("출력 결과 수 : ", len(result)) + + fmt.Println("\n") + spew.Dump(result) + + cblogger.Info("출력 결과 수 : ", len(result)) + + //조회및 삭제 테스트를 위해 리스트의 첫번째 정보의 ID를 요청ID로 자동 갱신함. + if result != nil { + imageReqInfo.IId = result[0].IId // 조회 및 삭제를 위해 생성된 ID로 변경 + } + } + + cblogger.Info("\nListImage Test Finished") + + case 2: + cblogger.Infof("[%s] Image 조회 테스트", imageReqInfo.IId) + + result, err := handler.GetImage(imageReqInfo.IId) + if err != nil { + cblogger.Error(err) + cblogger.Error("[%s] Image 조회 실패 : ", imageReqInfo.IId.SystemId, err) + } else { + fmt.Println("\n==================================================================================================================") + cblogger.Infof("[%s] Image 조회 결과 : \n[%s]", imageReqInfo.IId.SystemId, result) + + fmt.Println("\n") + spew.Dump(result) + } + + cblogger.Info("\nGetImage Test Finished") + + // case 3: + // cblogger.Infof("[%s] Image 생성 테스트", imageReqInfo.IId.NameId) + // result, err := handler.CreateImage(imageReqInfo) + // if err != nil { + // cblogger.Infof(imageReqInfo.IId.NameId, " Image 생성 실패 : ", err) + // } else { + // cblogger.Infof("Image 생성 결과 : ", result) + // imageReqInfo.IId = result.IId // 조회 및 삭제를 위해 생성된 ID로 변경 + // spew.Dump(result) + // } + + // case 4: + // cblogger.Infof("[%s] Image 삭제 테스트", imageReqInfo.IId.NameId) + // result, err := handler.DeleteImage(imageReqInfo.IId) + // if err != nil { + // cblogger.Infof("[%s] Image 삭제 실패 : ", imageReqInfo.IId.NameId, err) + // } else { + // cblogger.Infof("[%s] Image 삭제 결과 : [%s]", imageReqInfo.IId.NameId, result) + // } + } + } + } +} + +func testErr() error { + + return errors.New("") +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleImage() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_KeyPairHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_KeyPairHandler.go new file mode 100644 index 000000000..10da966d8 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_KeyPairHandler.go @@ -0,0 +1,281 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.0512. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func testErr() error { + + return errors.New("") +} + +// Test KeyPair +func handleKeyPair() { + cblogger.Debug("Start KeyPair Resource Test") + + ResourceHandler, err := getResourceHandler("KeyPair") + if err != nil { + panic(err) + } + //config := readConfigFile() + + keyPairHandler := ResourceHandler.(irs.KeyPairHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ KeyPair Management Test ]") + fmt.Println("1. List KeyPair") + fmt.Println("2. Get KeyPair") + fmt.Println("3. Create KeyPair") + fmt.Println("4. Delete KeyPair") + fmt.Println("0. Quit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + keyPairName := "nhn-key-01" + var commandNum int + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + result, err := keyPairHandler.ListKey() + if err != nil { + cblogger.Error(err) + cblogger.Error("KeyPair list 조회 실패 : ", err) + } else { + cblogger.Info("KeyPair list 조회 결과") + //cblogger.Info(result) + spew.Dump(result) + + cblogger.Infof("=========== KeyPair list 수 : [%d] ================", len(result)) + } + + cblogger.Info("\nListKey Test Finished") + + case 2: + cblogger.Infof("[%s] KeyPair 조회 테스트", keyPairName) + result, err := keyPairHandler.GetKey(irs.IID{NameId: keyPairName}) + if err != nil { + cblogger.Error(err) + cblogger.Error(keyPairName, " KeyPair 조회 실패 : ", err) + } else { + cblogger.Infof("[%s] KeyPair 조회 결과 : \n[%s]", keyPairName, result) + spew.Dump(result) + } + + cblogger.Info("\nGetKey Test Finished") + + case 3: + cblogger.Infof("[%s] KeyPair 생성 테스트", keyPairName) + keyPairReqInfo := irs.KeyPairReqInfo{ + IId: irs.IID{NameId: keyPairName}, + //Name: keyPairName, + } + result, err := keyPairHandler.CreateKey(keyPairReqInfo) + if err != nil { + cblogger.Error(err) + cblogger.Error(keyPairName, " KeyPair 생성 실패 : ", err) + } else { + cblogger.Infof("[%s] KeyPair 생성 결과 : \n[%s]", keyPairName, result) + spew.Dump(result) + } + + cblogger.Info("\nCreateKey Test Finished") + + case 4: + cblogger.Infof("[%s] KeyPair 삭제 테스트", keyPairName) + result, err := keyPairHandler.DeleteKey(irs.IID{NameId: keyPairName}) + if err != nil { + cblogger.Error(err) + cblogger.Error(keyPairName, " KeyPair 삭제 실패 : ", err) + } else { + cblogger.Infof("[%s] KeyPair 삭제 결과 : [%s]", keyPairName, result) + spew.Dump(result) + } + + cblogger.Info("\nDeleteKey Test Finished") + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleKeyPair() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + }, + } + + // NOTE Just for test + //cblogger.Info(config.Ncp.NcpAccessKeyID) + //cblogger.Info(config.Ncp.NcpSecretKey) + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "KeyPair": + resourceHandler, err = cloudConnection.CreateKeyPairHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_MyImageHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_MyImageHandler.go new file mode 100644 index 000000000..1a4673cdc --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_MyImageHandler.go @@ -0,0 +1,221 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2022.08. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func handleMyImage() { + cblogger.Debug("Start MyImage Resource Test") + + resourceHandler, err := getResourceHandler("MyImage") + if err != nil { + cblogger.Error(err) + return + } + diskHandler := resourceHandler.(irs.MyImageHandler) + //config := readConfigFile() + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ MyImageHandler Test ]") + cblogger.Info("1. ListMyImage()") + cblogger.Info("2. GetMyImage()") + cblogger.Info("3. SnapshotVM()") + cblogger.Info("4. DeleteMyImage()") + cblogger.Info("5. ChangeMyImageSize()") + cblogger.Info("6. AttachMyImage()") + cblogger.Info("7. DetachMyImage()") + cblogger.Info("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + var commandNum int + + myImageIId := irs.IID{ + NameId: "nhn-disk-01", + SystemId: "8fed0323-34a5-418a-99d3-999a6c322649", + // SystemId: "c44fe242-b51a-4d7b-be28-7b8028de3847", + } + + snapshotReqInfo := irs.MyImageInfo{ + IId: irs.IID{ + NameId: "nhn-myimage-01", + }, + SourceVM: irs.IID{ + NameId: "nhn-vm-1", + SystemId: "fb333037-8652-4636-83ab-c21b91f7be75", + }, + } + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + case 1: + cblogger.Info("Start ListMyImage() ...") + if listResult, err := diskHandler.ListMyImage(); err != nil { + cblogger.Error(err) + } else { + spew.Dump(listResult) + cblogger.Info("# 출력 결과 수 : ", len(listResult)) + } + cblogger.Info("Finish ListMyImage()") + case 2: + cblogger.Info("Start GetMyImage() ...") + if diskInfo, err := diskHandler.GetMyImage(myImageIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish GetMyImage()") + case 3: + cblogger.Info("Start SnapshotVM() ...") + if diskInfo, err := diskHandler.SnapshotVM(snapshotReqInfo); err != nil { + cblogger.Error(err) + } else { + spew.Dump(diskInfo) + } + cblogger.Info("Finish SnapshotVM()") + case 4: + cblogger.Info("Start DeleteMyImage() ...") + if delResult, err := diskHandler.DeleteMyImage(myImageIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(delResult) + } + cblogger.Info("Finish DeleteMyImage()") + } + } + } +} + +func testErr() error { + + return errors.New("") +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + handleMyImage() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "MyImage": + resourceHandler, err = cloudConnection.CreateMyImageHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_NLBHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_NLBHandler.go new file mode 100644 index 000000000..aa6e6d83e --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_NLBHandler.go @@ -0,0 +1,371 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2022.07. + +package main + +import ( + "os" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + cblog "github.com/cloud-barista/cb-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +// Test VMSpec +func handleNLB() { + cblogger.Debug("Start NLBHandler Resource Test") + + ResourceHandler, err := getResourceHandler("NLB") + if err != nil { + panic(err) + } + + nlbHandler := ResourceHandler.(irs.NLBHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ NLB Resource Test ]") + cblogger.Info("1. ListNLB()") + cblogger.Info("2. GetNLB()") + cblogger.Info("3. CreateNLB()") + cblogger.Info("4. DeleteNLB()") + cblogger.Info("5. ChangeListener()") + cblogger.Info("6. ChangeVMGroupInfo()") + cblogger.Info("7. AddVMs()") + cblogger.Info("8. RemoveVMs()") + cblogger.Info("9. GetVMGroupHealthInfo()") + cblogger.Info("10. ChangeHealthCheckerInfo()") + cblogger.Info("11. Exit") + fmt.Println("============================================================================================") + + config := readConfigFile() + cblogger.Infof("\n # NHN Cloud Region : [%s]", config.NhnCloud.Region) + cblogger.Info("\n # Num : ") + + nlbIId := irs.IID{ + NameId: "new_lb-1", + SystemId: "8637825b-d053-48c6-bdf3-7d3fd355f086", + } + + nlbCreateReqInfo := irs.NLBInfo{ + IId: irs.IID{ + NameId: "new_nlb-1", + }, + VpcIID: irs.IID{ + NameId: "vpc-000001", + }, + Listener: irs.ListenerInfo{ + Protocol: "TCP", + Port: "8080", + }, + VMGroup: irs.VMGroupInfo{ + Protocol: "TCP", + Port: "8080", + VMs: &[]irs.IID{ + {NameId: "nhn-vm-1"}, + {NameId: "nhn-vm-2"}, + }, + }, + HealthChecker: irs.HealthCheckerInfo{ + Protocol: "TCP", + Port: "8080", + Interval: -1, + Timeout: -1, + Threshold: -1, + }, + // HealthChecker: irs.HealthCheckerInfo{ + // Protocol: "TCP", + // Port: "8080", + // Interval: 30, + // Timeout: 5, + // Threshold: 3, + // }, + } + + updateListener := irs.ListenerInfo{ + Protocol: "TCP", + Port: "8087", + } + + updateVMGroups := irs.VMGroupInfo{ + Protocol: "TCP", + Port: "8087", + } + + addVMs := []irs.IID{ + {NameId: "nhn-vm-3"}, + {NameId: "nhn-vm-4"}, + } + + removeVMs := []irs.IID{ + {NameId: "nhn-vm-4"}, + // {NameId: "nhn-vm-3"}, + // {NameId: "nhn-vm-4"}, + } + + updateHealthCheckerInfo := irs.HealthCheckerInfo{ + Protocol: "HTTP", + Port: "8080", + Interval: 7, + Timeout: 5, + Threshold: 4, + } + + var commandNum int + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 1: + cblogger.Info("Start ListNLB() ...") + if list, err := nlbHandler.ListNLB(); err != nil { + cblogger.Error(err) + } else { + spew.Dump(list) + cblogger.Info("출력 결과 수 : ", len(list)) + } + cblogger.Info("Finish ListNLB()") + case 2: + cblogger.Info("Start GetNLB() ...") + if nlbInfo, err := nlbHandler.GetNLB(nlbIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(nlbInfo) + } + cblogger.Info("Finish GetNLB()") + case 3: + cblogger.Info("Start CreateNLB() ...") + if nlbInfo, err := nlbHandler.CreateNLB(nlbCreateReqInfo); err != nil { + cblogger.Error(err) + } else { + spew.Dump(nlbInfo) + } + cblogger.Info("Finish CreateNLB()") + case 4: + cblogger.Info("Start DeleteNLB() ...") + if nlbStatus, err := nlbHandler.DeleteNLB(nlbIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(nlbStatus) + } + cblogger.Info("Finish DeleteNLB()") + case 5: + cblogger.Info("Start ChangeListener() ...") + if nlbInfo, err := nlbHandler.ChangeListener(nlbIId, updateListener); err != nil { + cblogger.Error(err) + } else { + spew.Dump(nlbInfo) + } + cblogger.Info("Finish ChangeListener()") + case 6: + cblogger.Info("Start ChangeVMGroupInfo() ...") + if info, err := nlbHandler.ChangeVMGroupInfo(nlbIId, updateVMGroups); err != nil { + cblogger.Error(err) + } else { + spew.Dump(info) + } + cblogger.Info("Finish ChangeVMGroupInfo()") + case 7: + cblogger.Info("Start AddVMs() ...") + if info, err := nlbHandler.AddVMs(nlbIId, &addVMs); err != nil { + cblogger.Error(err) + } else { + spew.Dump(info) + } + cblogger.Info("Finish AddVMs()") + case 8: + cblogger.Info("Start RemoveVMs() ...") + if result, err := nlbHandler.RemoveVMs(nlbIId, &removeVMs); err != nil { + cblogger.Error(err) + } else { + spew.Dump(result) + } + cblogger.Info("Finish RemoveVMs()") + case 9: + cblogger.Info("Start GetVMGroupHealthInfo() ...") + if info, err := nlbHandler.GetVMGroupHealthInfo(nlbIId); err != nil { + cblogger.Error(err) + } else { + spew.Dump(info) + } + cblogger.Info("Finish GetVMGroupHealthInfo()") + case 10: + cblogger.Info("Start ChangeHealthCheckerInfo() ...") + if info, err := nlbHandler.ChangeHealthCheckerInfo(nlbIId, updateHealthCheckerInfo); err != nil { + cblogger.Error(err) + } else { + spew.Dump(info) + } + cblogger.Info("Finish ChangeHealthCheckerInfo()") + case 11: + cblogger.Info("Exit") + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleNLB() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "NLB": + resourceHandler, err = cloudConnection.CreateNLBHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_RegionZoneHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_RegionZoneHandler.go new file mode 100644 index 000000000..616f5308e --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_RegionZoneHandler.go @@ -0,0 +1,270 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2023.09. + +package main + +import ( + "os" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +// Test RegionZone +func handleRegionZone() { + cblogger.Debug("Start RegionZoneHandler Resource Test") + + ResourceHandler, err := getResourceHandler("RegionZone") + if err != nil { + panic(err) + } + handler := ResourceHandler.(irs.RegionZoneHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ RegionZone Resource Test ]") + fmt.Println("1. ListRegionZone()") + fmt.Println("2. GetRegionZone()") + fmt.Println("3. ListOrgRegion()") + fmt.Println("4. ListOrgZone()") + fmt.Println("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + config := readConfigFile() + reqRegion := config.NhnCloud.Region // Region Code Ex) KR, HK, SGN, JPN, DEN, USWN + cblogger.Info("config.NhnCloud.Region : ", reqRegion) + + var commandNum int + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 1: + fmt.Println("Start ListRegionZone() ...") + result, err := handler.ListRegionZone() + if err != nil { + cblogger.Error("RegionZone list 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================") + cblogger.Debug("RegionZone list 조회 성공!!") + spew.Dump(result) + cblogger.Debug(result) + cblogger.Infof("RegionZone list 개수 : [%d]", len(result)) + } + fmt.Println("\n# ListRegionZone() Test Finished") + + case 2: + fmt.Println("Start ListOrgRegion() ...") + result, err := handler.GetRegionZone(reqRegion) + if err != nil { + cblogger.Error("Region(Org) list 정보 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================") + cblogger.Debug("Region Info 조회 성공!!") + spew.Dump(result) + cblogger.Debug(result) + } + fmt.Println("\n# GetRegionZone() Test Finished") + + case 3: + fmt.Println("Start ListOrgRegion() ...") + result, err := handler.ListOrgRegion() + if err != nil { + cblogger.Error("Region(Org) list 정보 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================") + cblogger.Debug("Region(Org) list 조회 성공!!") + spew.Dump(result) + cblogger.Debug(result) + } + fmt.Println("\n# ListOrgRegion() Test Finished") + + case 4: + fmt.Println("Start ListOrgZone() ...") + result, err := handler.ListOrgZone() + if err != nil { + cblogger.Error("Zone(Org) list 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================") + cblogger.Debug("Zone(Org) list 조회 성공") + spew.Dump(result) + cblogger.Debug(result) + } + fmt.Println("\n# ListOrgZone() Test Finished") + + case 0: + fmt.Println("Exit") + return + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleRegionZone() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "NLB": + resourceHandler, err = cloudConnection.CreateNLBHandler() + case "RegionZone": + resourceHandler, err = cloudConnection.CreateRegionZoneHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_SecurityHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_SecurityHandler.go new file mode 100644 index 000000000..8ca2902e8 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_SecurityHandler.go @@ -0,0 +1,540 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.12. + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func handleSecurity() { + cblogger.Debug("Start Security Resource Test") + + ResourceHandler, err := getResourceHandler("Security") + if err != nil { + panic(err) + } + + handler := ResourceHandler.(irs.SecurityHandler) + + //config := readConfigFile() + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ Security Management Test ]") + fmt.Println("1. List Security") + fmt.Println("2. Get Security") + fmt.Println("3. Create Security") + fmt.Println("4. Add Rules") + fmt.Println("5. Remove Rules") + fmt.Println("6. Delete Security") + fmt.Println("0. Quit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + var commandNum int + + securityName := "nhn-sg-04" + //securityId := "67167e2e-2390-48d6-8f27-78c9293b26f3" + securityId := "2f2e7af6-6ecd-4db2-a96b-20cf4987062a" + vpcId := "1364a68d-4a7c-4a91-ac71-d29c7f4d47fc" + // vpcNameId := "nhn-vpc-01" + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + result, err := handler.ListSecurity() + if err != nil { + cblogger.Error(err) + cblogger.Error("SecurityGroup list 조회 실패 : ", err) + } else { + cblogger.Info("SecurityGroup list 조회 결과") + //cblogger.Info(result) + spew.Dump(result) + + cblogger.Infof("=========== S/G list 수 : [%d] ================", len(result)) + if result != nil { + securityId = result[0].IId.SystemId // 조회 및 삭제를 위해 생성된 ID로 변경 + } + } + + cblogger.Info("\nListSecurity Test Finished") + + case 2: + cblogger.Infof("[%s] SecurityGroup 정보 조회 테스트", securityId) + result, err := handler.GetSecurity(irs.IID{SystemId: securityId}) + // result, err := handler.GetSecurity(irs.IID{NameId: securityName}) + if err != nil { + cblogger.Error(err) + cblogger.Error(securityId, " SecurityGroup 조회 실패 : ", err) + } else { + cblogger.Infof("[%s] SecurityGroup 조회 결과 : [%v]", securityId, result) + spew.Dump(result) + } + + cblogger.Info("\nGetSecurity Test Finished") + + case 3: + cblogger.Infof("[%s] Security 생성 테스트", securityName) + + securityReqInfo := irs.SecurityReqInfo{ + IId: irs.IID{NameId: securityName}, + VpcIID: irs.IID{SystemId: vpcId}, + // VpcIID: irs.IID{NameId: vpcNameId}, + SecurityRules: &[]irs.SecurityRuleInfo{ //보안 정책 설정 + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "20", + // ToPort: "22", + // CIDR: "0.0.0.0/0", + // }, + + { + Direction: "inbound", + IPProtocol: "tcp", + FromPort: "80", + ToPort: "80", + CIDR: "192.168.0.0/16", + }, + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "192.168.0.0/16", + // }, + + { + Direction: "inbound", + IPProtocol: "udp", + FromPort: "8080", + ToPort: "8080", + CIDR: "0.0.0.0/0", + }, + // { + // Direction: "inbound", + // IPProtocol: "icmp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "443", + // ToPort: "443", + // CIDR: "0.0.0.0/0", + // }, + + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "8443", + // ToPort: "9999", + // CIDR: "192.168.0.0/16", + // }, + + + // // All traffic 허용 rule + { + Direction: "inbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + + { + Direction: "outbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + + }, + } + + result, err := handler.CreateSecurity(securityReqInfo) + if err != nil { + cblogger.Infof(securityName, " Security 생성 실패 : ", err) + } else { + cblogger.Infof("[%s] Security 생성 결과 : [%v]", securityName, result) + spew.Dump(result) + } + + case 4: + cblogger.Infof("[%s] Security Rule 추가 테스트", securityName) + + securityRuleReqInfo := &[]irs.SecurityRuleInfo{ + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "20", + // ToPort: "22", + // CIDR: "0.0.0.0/0", + // }, + + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "80", + // ToPort: "80", + // CIDR: "192.168.0.0/16", + // }, + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "192.168.0.0/16", + // }, + + // { + // Direction: "inbound", + // IPProtocol: "udp", + // FromPort: "8080", + // ToPort: "8080", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "inbound", + // IPProtocol: "icmp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "443", + // ToPort: "443", + // CIDR: "0.0.0.0/0", + // }, + + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "8443", + // ToPort: "9999", + // CIDR: "192.168.0.0/16", + // }, + + + // // All traffic 허용 rule + { + Direction: "inbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + { + Direction: "outbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + } + + result, err := handler.AddRules(irs.IID{SystemId: securityId}, securityRuleReqInfo) + if err != nil { + cblogger.Infof(securityName, " Security Rule Add failed : ", err) + } else { + cblogger.Infof("[%s] Security Rule 추가 결과 : [%v]", securityName, result) + spew.Dump(result) + } + cblogger.Info("\nAddRules Test Finished") + + case 5: + cblogger.Infof("[%s] Security Rule 제거 테스트", securityName) + + securityRuleReqInfo := &[]irs.SecurityRuleInfo{ + // { + + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "20", + // ToPort: "22", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "80", + // ToPort: "80", + // CIDR: "192.168.0.0/16", + // }, + // { + // Direction: "inbound", + // IPProtocol: "tcp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "192.168.0.0/16", + // }, + // { + // Direction: "inbound", + // IPProtocol: "udp", + // FromPort: "8080", + // ToPort: "8080", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "inbound", + // IPProtocol: "icmp", + // FromPort: "-1", + // ToPort: "-1", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "443", + // ToPort: "443", + // CIDR: "0.0.0.0/0", + // }, + // { + // Direction: "outbound", + // IPProtocol: "tcp", + // FromPort: "8443", + // ToPort: "9999", + // CIDR: "192.168.0.0/16", + // }, + + + // // All traffic 허용 rule + { + Direction: "inbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + { + Direction: "outbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + } + + result, err := handler.RemoveRules(irs.IID{SystemId: securityId}, securityRuleReqInfo) + if err != nil { + cblogger.Infof(securityName, " Security Rule Remove failed : ", err) + } else { + cblogger.Infof("[%s] Security Rule 제거 결과 : [%v]", securityName, result) + spew.Dump(result) + } + cblogger.Info("\nRemoveRules Test Finished") + + case 6: + cblogger.Infof("[%s] Security 삭제 테스트", securityId) + result, err := handler.DeleteSecurity(irs.IID{SystemId: securityId}) + if err != nil { + cblogger.Infof(securityId, " Security 삭제 실패 : ", err) + } else { + cblogger.Infof("[%s] Security 삭제 결과 : [%s]", securityId, result) + } + } + } + } +} + +func testErr() error { + + return errors.New("") +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + /* + err := testErr() + spew.Dump(err) + if err != nil { + cblogger.Info("에러 발생") + awsErr, ok := err.(awserr.Error) + spew.Dump(awsErr) + spew.Dump(ok) + if ok { + if "404" == awsErr.Code() { + cblogger.Info("404!!!") + } else { + cblogger.Info("404 아님") + } + } + } + */ + + handleSecurity() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMHandler.go new file mode 100644 index 000000000..13ed69548 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMHandler.go @@ -0,0 +1,390 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.12. +// by ETRI, 2022.03. updated + +package main + +import ( + "os" + "errors" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +func testErr() error { + + return errors.New("") +} + +// Test VM Lifecycle Management (Create/Suspend/Resume/Reboot/Terminate) +func handleVM() { + cblogger.Debug("Start VMHandler Resource Test") + + ResourceHandler, err := getResourceHandler("VM") + if err != nil { + panic(err) + } + + vmHandler := ResourceHandler.(irs.VMHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ VM Management Test ]") + fmt.Println("1. Start(Create) VM") + fmt.Println("2. Get VM Info") + fmt.Println("3. Suspend VM") + fmt.Println("4. Resume VM") + fmt.Println("5. Reboot VM") + + fmt.Println("6. Terminate VM") + fmt.Println("7. Get VMStatus") + fmt.Println("8. List VMStatus") + fmt.Println("9. List VM") + fmt.Println("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + //config := readConfigFile() + vmID := irs.IID{SystemId: "31cd5054-cf7f-4904-bc10-fdef5365baf3"} + + var commandNum int + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 0: + return + + case 1: + vmReqInfo := irs.VMReqInfo{ + IId: irs.IID{NameId: "nhn-vm-2"}, + + //KR1 + ImageIID: irs.IID{NameId: "Ubuntu Server 18.04.6 LTS (2021.12.21)", SystemId: "5396655e-166a-4875-80d2-ed8613aa054f"}, + // Ubuntu Server 18.04.6 LTS (2021.12.21) + + // ImageIID: irs.IID{NameId: "CentOS 6.10 (2018.10.23)", SystemId: "1c868787-6207-4ff2-a1e7-ae1331d6829b"}, + // CentOS 6.10 (2018.10.23) + + // VMSpecName: "u2.c2m4", //vCPU: 2, Mem: 4GB + VMSpecName: "m2.c4m8", //vCPU: 4, Mem: 8GB + + RootDiskType: "General_SSD", + // RootDiskType: "General_HDD", + // RootDiskType: "default", + + RootDiskSize: "20", // Except for u2.~~~ type VMSpec + // RootDiskSize: "default", // When u2.~~~ type VMSpec + + DataDiskIIDs: []irs.IID{ // Disk volume list to Attach + { + SystemId: "eface614-e6c0-40ee-8237-e1d28edb1bb4", + }, + }, + + KeyPairIID: irs.IID{NameId: "nhn-key-01-c9584r9jcupvtimg81l0"}, + //KeyPairIID: irs.IID{SystemId: "nhn-key-01"}, + + // $$$ Needs NHN Cloud VPC 'SystemId' + VpcIID: irs.IID{ + NameId: "Default Network", + SystemId: "ae945890-0433-467e-9366-b9e3611e01f3", + }, + + SubnetIID: irs.IID{ + NameId: "Default Network", + SystemId: "fa6ddcee-9761-433a-af9e-6334b3a33f25", + }, + + SecurityGroupIIDs: []irs.IID{{SystemId: "79965cd0-b9e9-42ef-9c66-201f824273cb"},{SystemId: "67167e2e-2390-48d6-8f27-78c9293b26f3"}}, + } + + vmInfo, err := vmHandler.StartVM(vmReqInfo) + if err != nil { + //panic(err) + cblogger.Error(err) + cblogger.Info("VM 생성 실패 : ", err) + } else { + cblogger.Info("VM 생성 완료!!", vmInfo) + spew.Dump(vmInfo) + } + //cblogger.Info(vm) + + cblogger.Info("\nCreateVM Test Finished") + + case 2: + vmInfo, err := vmHandler.GetVM(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] VM info. 조회 실패 : ", err) + } else { + cblogger.Infof("[%s] VM info. 조회 결과", vmID) + cblogger.Info(vmInfo) + spew.Dump(vmInfo) + } + + cblogger.Info("\nGetVM Test Finished") + + case 3: + cblogger.Info("Start Suspend the VM ...") + result, err := vmHandler.SuspendVM(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] VM Suspend 실패 : [%s]", vmID, result) + } else { + cblogger.Infof("[%s] VM Suspend 실행 성공 : [%s]", vmID, result) + } + + cblogger.Info("\nSuspendVM Test Finished") + + case 4: + cblogger.Info("Start Resume the VM ...") + result, err := vmHandler.ResumeVM(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] VM Resume 실패 : [%s]", vmID, result) + } else { + cblogger.Infof("[%s] VM Resume 실행 성공 : [%s]", vmID, result) + } + + cblogger.Info("\nResumeVM Test Finished") + + case 5: + cblogger.Info("Start Reboot the VM ...") + result, err := vmHandler.RebootVM(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] VM Reboot 실패 : [%s]", vmID, result) + } else { + cblogger.Infof("[%s] VM Reboot 실행 성공 : [%s]", vmID, result) + } + + cblogger.Info("\nRebootVM Test Finished") + + case 6: + cblogger.Info("Start Terminate VM ...") + result, err := vmHandler.TerminateVM(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] Terminate VM 실패 : [%s]", vmID, result) + } else { + cblogger.Infof("[%s] Terminate VM 실행 성공 : [%s]", vmID, result) + } + + cblogger.Info("\nTerminateVM Test Finished") + + case 7: + cblogger.Info("Start Get VM Status...") + vmStatus, err := vmHandler.GetVMStatus(vmID) + if err != nil { + cblogger.Error(err) + cblogger.Errorf("[%s] Get VM Status 실패 : ", vmID) + } else { + cblogger.Infof("[%s] Get VM Status 실행 성공 : [%s]", vmID, vmStatus) + } + + cblogger.Info("\nGet VMStatus Test Finished") + + case 8: + cblogger.Info("Start ListVMStatus ...") + vmStatusInfos, err := vmHandler.ListVMStatus() + if err != nil { + cblogger.Error(err) + cblogger.Error("ListVMStatus 실패 : ") + } else { + cblogger.Info("ListVMStatus 실행 성공") + //cblogger.Info(vmStatusInfos) + spew.Dump(vmStatusInfos) + } + + cblogger.Info("\nListVM Status Test Finished") + + case 9: + cblogger.Info("Start ListVM ...") + vmList, err := vmHandler.ListVM() + if err != nil { + cblogger.Error(err) + cblogger.Error("ListVM 실패 : ", err) + } else { + cblogger.Info("ListVM 실행 성공") + cblogger.Info("=========== VM 목록 ================") + // cblogger.Info(vmList) + spew.Dump(vmList) + cblogger.Infof("=========== VM 목록 수 : [%d] ================", len(vmList)) + if len(vmList) > 0 { + vmID = vmList[0].IId + } + } + + cblogger.Info("\nListVM Test Finished") + + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleVM() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VMID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMSpecHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMSpecHandler.go new file mode 100644 index 000000000..3fbf2b072 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VMSpecHandler.go @@ -0,0 +1,283 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.12. + +package main + +import ( + "os" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +// Test VMSpec +func handleVMSpec() { + cblogger.Debug("Start VMSpecHandler Resource Test") + + ResourceHandler, err := getResourceHandler("VMSpec") + if err != nil { + panic(err) + } + + handler := ResourceHandler.(irs.VMSpecHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ VMSpec Resource Test ]") + fmt.Println("1. ListVMSpec()") + fmt.Println("2. GetVMSpec()") + fmt.Println("3. ListOrgVMSpec()") + fmt.Println("4. GetOrgVMSpec()") + fmt.Println("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + reqVMSpec := "m2.c4m8" + // reqVMSpec := "g2.t4.c16m128" //NHN : 16 vCore, 12GB + // NHN : u2.c2m4 (2vCPU, 4GB) + + config := readConfigFile() + + cblogger.Info("config.NhnCloud.Region : ", config.NhnCloud.Region) + cblogger.Info("reqVMSpec : ", reqVMSpec) + + var commandNum int + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 1: + fmt.Println("Start ListVMSpec() ...") + + result, err := handler.ListVMSpec() + if err != nil { + cblogger.Error(err) + cblogger.Error("\nVMSpec list 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================================") + cblogger.Debug("VMSpec list 조회 성공!!") + spew.Dump(result) + //cblogger.Debug(result) + cblogger.Infof("전체 VMSpec list 개수 : [%d]", len(result)) + } + + fmt.Println("\nListVMSpec() Test Finished") + + case 2: + fmt.Println("Start GetVMSpec() ...") + + result, err := handler.GetVMSpec(reqVMSpec) + if err != nil { + cblogger.Error(err) + cblogger.Error(reqVMSpec, " VMSpec 정보 조회 실패 : ", err) + } else { + fmt.Println("\n==================================================================================================================") + cblogger.Debugf("VMSpec[%s] 정보 조회 성공!!", reqVMSpec) + spew.Dump(result) + cblogger.Debug(result) + //cblogger.Infof(result) + } + + fmt.Println("\nGetVMSpec() Test Finished") + + case 3: + fmt.Println("Start ListOrgVMSpec() ...") + result, err := handler.ListOrgVMSpec() + if err != nil { + cblogger.Error(err) + cblogger.Error("VMSpec Org list 조회 실패 : ", err) + } else { + cblogger.Debug("VMSpec Org list 조회 성공") + spew.Dump(result) + cblogger.Debug(result) + } + + fmt.Println("\nListOrgVMSpec() Test Finished") + + case 4: + fmt.Println("Start GetOrgVMSpec() ...") + result, err := handler.GetOrgVMSpec(reqVMSpec) + if err != nil { + cblogger.Error(err) + cblogger.Error(reqVMSpec, " VMSpec Org 정보 조회 실패 : ", err) + } else { + cblogger.Debugf("VMSpec[%s] Org 정보 조회 성공", reqVMSpec) + spew.Dump(result) + cblogger.Debug(result) + } + + fmt.Println("\nGetOrgVMSpec() Test Finished") + + case 0: + fmt.Println("Exit") + return + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleVMSpec() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VPCHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VPCHandler.go new file mode 100644 index 000000000..4aa90ba3d --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/Test_VPCHandler.go @@ -0,0 +1,306 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Tester Example. +// +// by ETRI, 2021.12. + +package main + +import ( + "os" + "fmt" + "github.com/davecgh/go-spew/spew" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cblog "github.com/cloud-barista/cb-log" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +var cblogger *logrus.Logger + +func init() { + // cblog is a global variable. + cblogger = cblog.GetLogger("NHN Cloud Resource Test") + cblog.SetLevel("info") +} + +// Test VMSpec +func handleVPC() { + cblogger.Debug("Start VPCHandler Resource Test") + + ResourceHandler, err := getResourceHandler("VPC") + if err != nil { + panic(err) + } + + handler := ResourceHandler.(irs.VPCHandler) + + for { + fmt.Println("\n============================================================================================") + fmt.Println("[ VPC Resource Test ]") + fmt.Println("1. CreateVPC()") + fmt.Println("2. ListVPC()") + fmt.Println("3. GetVPC()") + fmt.Println("4. DeleteVPC()") + fmt.Println("0. Exit") + fmt.Println("\n Select a number above!! : ") + fmt.Println("============================================================================================") + + vpcReqName := "nhn-vpc-1" + vpcIId := irs.IID{NameId: vpcReqName, SystemId: "ae945890-0433-467e-9366-b9e3611e01f3"} + cblogger.Info("reqVPCName : ", vpcReqName) + + subnetReqName := "subnet-1" + + var subnetInfoList []irs.SubnetInfo + info := irs.SubnetInfo{ + IId: irs.IID{ + NameId: subnetReqName, + }, + IPv4_CIDR: "172.16.0.0/24", + //IPv4_CIDR: "172.16.0.0/24", + } + subnetInfoList = append(subnetInfoList, info) + + + // vpcReqInfo := irs.VPCReqInfo{ + // IId: irs.IID{NameId: reqVPCName, SystemId: vpcId}, + // } + + vpcReqInfo := irs.VPCReqInfo{ + IId: vpcIId, + IPv4_CIDR: "172.16.0.0/12", + // IPv4_CIDR: "172.16.0.0/22", + // IPv4_CIDR: "172.16.0.0/16", + SubnetInfoList: subnetInfoList, + } + + //NHN Cloud VPC CIDR은 아래의 사설 주소 범위로 입력되어야 함. + // 10.0.0.0/8 + // 172.16.0.0/12 + // 192.168.0.0/16 + + // CIDR은 링크 로컬 주소 범위(169.254.0.0/16)로 입력할 수 없음. + // /24보다 큰 CIDR 블록은 입력할 수 없음. + // VPC은 최대 3개까지 생성 가능 + + var commandNum int + + inputCnt, err := fmt.Scan(&commandNum) + if err != nil { + panic(err) + } + + if inputCnt == 1 { + switch commandNum { + case 1: + fmt.Println("Start CreateVPC() ...") + + vpcInfo, err := handler.CreateVPC(vpcReqInfo) + if err != nil { + //panic(err) + cblogger.Error(err) + cblogger.Error("VPC 생성 실패 : ", err) + } else { + cblogger.Info("VPC 생성 완료!!", vpcInfo) + spew.Dump(vpcInfo) + cblogger.Debug(vpcInfo) + } + + fmt.Println("\nCreateVPC() Test Finished") + + case 2: + fmt.Println("Start ListVPC() ...") + result, err := handler.ListVPC() + if err != nil { + cblogger.Error(err) + cblogger.Error("VPC list 조회 실패 : ", err) + } else { + cblogger.Info("VPC list 조회 성공!!") + spew.Dump(result) + cblogger.Debug(result) + cblogger.Infof("전체 list 개수 : [%d]", len(result)) + } + + fmt.Println("\nListVMSpec() Test Finished") + + case 3: + fmt.Println("Start GetVPC() ...") + if vpcInfo, err := handler.GetVPC(vpcIId); err != nil { + cblogger.Error(err) + cblogger.Error("VPC 정보 조회 실패 : ", err) + } else { + cblogger.Info("VPC 정보 조회 성공!!") + spew.Dump(vpcInfo) + } + fmt.Println("\nGetVPC() Test Finished") + + case 4: + fmt.Println("Start DeleteVPC() ...") + if result, err := handler.DeleteVPC(vpcIId); err != nil { + cblogger.Error(err) + cblogger.Error("VPC 삭제 실패 : ", err) + } else { + cblogger.Info("VPC 삭제 성공!!") + spew.Dump(result) + } + fmt.Println("\nGetVPC() Test Finished") + + + case 0: + fmt.Println("Exit") + return + } + } + } +} + +func main() { + cblogger.Info("NHN Cloud Resource Test") + + handleVPC() +} + +//handlerType : resources폴더의 xxxHandler.go에서 Handler이전까지의 문자열 +//(예) ImageHandler.go -> "Image" +func getResourceHandler(handlerType string) (interface{}, error) { + var cloudDriver idrv.CloudDriver + cloudDriver = new(nhndrv.NhnCloudDriver) + + config := readConfigFile() + // spew.Dump(config) + + cblogger.Infof("\n # NHN Cloud Region : [%s]", config.NhnCloud.Region) + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: config.NhnCloud.IdentityEndpoint, + Username: config.NhnCloud.Nhn_Username, + Password: config.NhnCloud.Api_Password, + DomainName: config.NhnCloud.DomainName, + TenantId: config.NhnCloud.TenantId, + }, + RegionInfo: idrv.RegionInfo{ + Region: config.NhnCloud.Region, + Zone: config.NhnCloud.Zone, + }, + } + + cloudConnection, errCon := cloudDriver.ConnectCloud(connectionInfo) + if errCon != nil { + return nil, errCon + } + + var resourceHandler interface{} + var err error + + switch handlerType { + case "Image": + resourceHandler, err = cloudConnection.CreateImageHandler() + case "Security": + resourceHandler, err = cloudConnection.CreateSecurityHandler() + case "VNetwork": + resourceHandler, err = cloudConnection.CreateVPCHandler() + case "VM": + resourceHandler, err = cloudConnection.CreateVMHandler() + case "VMSpec": + resourceHandler, err = cloudConnection.CreateVMSpecHandler() + case "VPC": + resourceHandler, err = cloudConnection.CreateVPCHandler() + } + + if err != nil { + return nil, err + } + return resourceHandler, nil +} + +// Region : 사용할 리전명 (ex) ap-northeast-2 +// ImageID : VM 생성에 사용할 AMI ID (ex) ami-047f7b46bd6dd5d84 +// BaseName : 다중 VM 생성 시 사용할 Prefix이름 ("BaseName" + "_" + "숫자" 형식으로 VM을 생성 함.) (ex) mcloud-barista +// VmID : 라이프 사이트클을 테스트할 EC2 인스턴스ID +// InstanceType : VM 생성시 사용할 인스턴스 타입 (ex) t2.micro +// KeyName : VM 생성시 사용할 키페어 이름 (ex) mcloud-barista-keypair +// MinCount : +// MaxCount : +// SubnetId : VM이 생성될 VPC의 SubnetId (ex) subnet-cf9ccf83 +// SecurityGroupID : 생성할 VM에 적용할 보안그룹 ID (ex) sg-0df1c209ea1915e4b +type Config struct { + NhnCloud struct { + IdentityEndpoint string `yaml:"identity_endpoint"` + Nhn_Username string `yaml:"nhn_username"` + Api_Password string `yaml:"api_password"` + DomainName string `yaml:"domain_name"` + TenantId string `yaml:"tenant_id"` + Region string `yaml:"region"` + Zone string `yaml:"zone"` + + VMName string `yaml:"vm_name"` + ImageId string `yaml:"image_id"` + VMSpecId string `yaml:"vmspec_id"` + NetworkId string `yaml:"network_id"` + SecurityGroups string `yaml:"security_groups"` + KeypairName string `yaml:"keypair_name"` + + VMId string `yaml:"vm_id"` + + Image struct { + Name string `yaml:"name"` + } `yaml:"image_info"` + + KeyPair struct { + Name string `yaml:"name"` + } `yaml:"keypair_info"` + + PublicIP struct { + Name string `yaml:"name"` + } `yaml:"public_info"` + + SecurityGroup struct { + Name string `yaml:"name"` + } `yaml:"security_group_info"` + + VirtualNetwork struct { + Name string `yaml:"name"` + } `yaml:"vnet_info"` + + Subnet struct { + Id string `yaml:"id"` + } `yaml:"subnet_info"` + + Router struct { + Name string `yaml:"name"` + GateWayId string `yaml:"gateway_id"` + AdminStateUp bool `yaml:"adminstatup"` + } `yaml:"router_info"` + } `yaml:"nhncloud"` +} + +func readConfigFile() Config { + // Set Environment Value of Project Root Path + // rootPath := "/home/sean/go/src/github.com/cloud-barista/nhncloud/nhncloud/main" + rootPath := os.Getenv("CBSPIDER_ROOT") + configPath := rootPath + "/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml" + cblogger.Info("Config file : " + configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cblogger.Error(err) + } + + var config Config + err = yaml.Unmarshal(data, &config) + if err != nil { + cblogger.Error(err) + } + return config +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml.sample b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml.sample new file mode 100644 index 000000000..400bd5dd8 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/conf/config.yaml.sample @@ -0,0 +1,24 @@ +## Config for NHN Cloud ## +nhncloud: + identity_endpoint: https://api-identity.infrastructure.cloud.toast.com + nhn_username: ex) ~~~@~~~.com + api_password: + # API 비밀번호 값 입력 + domain_name: ex) default + tenant_id: + # => NHN > Instance > 관리 > API 엔드포인트 설정 > TenantID를 입력해야함. (NHNCloudDriver.go의 각 client 참고) + + region: KR1 + zone: kr-pub-a + # zone: kr-pub-b + + # region: KR2 + # zone: kr2-pub-a + # zone: kr2-pub-b + + # region: JP1 + # zone: jp-pub-a + # zone: jp-pub-b + + ##### (Note) NHN Cloud only provides detailed Zone information with the API, but unlike other CSPs, the API Endpoint is different for each Region, so when calling with API, only the zone information of the Region comes out without the Region information. + \ No newline at end of file diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/goget-sdk.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/goget-sdk.sh new file mode 100755 index 000000000..88c7ccb95 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/goget-sdk.sh @@ -0,0 +1,6 @@ + +# export GIT_TERMINAL_PROMPT=1 +# export GOPRIVATE="github.com/cloud-barista" + +echo "# env GIT_TERMINAL_PROMPT=1 GOPRIVATE=github.com/cloud-barista go get -v github.com/cloud-barista/nhncloud-sdk-go@latest" +env GIT_TERMINAL_PROMPT=1 GOPRIVATE=github.com/cloud-barista go get -v github.com/cloud-barista/nhncloud-sdk-go@latest diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/ClusterHandler_test.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/ClusterHandler_test.go new file mode 100644 index 000000000..32a17e713 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/ClusterHandler_test.go @@ -0,0 +1,363 @@ +package pmks + +import ( + "encoding/json" + "os" + "testing" + + "github.com/jeremywohl/flatten" + + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + + // nhndrv "github.com/cloud-barista/nhncloud/nhncloud" + nhndrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/nhncloud" +) + +func printObj2Flat(object interface{}) { + temp, err := json.MarshalIndent(object, "", " ") + if err != nil { + println(err) + } else { + flat, err := flatten.FlattenString(string(temp), "", flatten.DotStyle) + if err != nil { + println(err) + } else { + println(flat) + } + } +} + +func getClusterHandler() (irs.ClusterHandler, error) { + + connectionInfo := idrv.ConnectionInfo{ + CredentialInfo: idrv.CredentialInfo{ + IdentityEndpoint: os.Getenv("ID_ENDPOINT"), + Username: os.Getenv("USERNAME"), + DomainName: os.Getenv("DOMAIN_NAME"), + Password: os.Getenv("PASSWORD"), + TenantId: os.Getenv("TENANT_ID"), + }, + RegionInfo: idrv.RegionInfo{ + Region: os.Getenv("REGION_NAME"), + Zone: os.Getenv("REGION_ZONE"), + }, + } + + cloudDriver := new(nhndrv.NhnCloudDriver) + cloudConnection, err := cloudDriver.ConnectCloud(connectionInfo) + if err != nil { + return nil, err + } + + clusterHandler, err := cloudConnection.CreateClusterHandler() + if err != nil { + return nil, err + } + + return clusterHandler, nil +} + +func TestCreateCluster(t *testing.T) { + + t.Log("TestCreateCluster()") + + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusterInfo := irs.ClusterInfo{ + IId: irs.IID{ + NameId: "cluster-test1", + SystemId: "", + }, + Version: "v1.23.3", + Network: irs.NetworkInfo{ + VpcIID: irs.IID{ + NameId: "", + SystemId: "8a07c257-72ba-4183-bb77-fa46f6d12c39", + }, + SubnetIIDs: []irs.IID{ + { + NameId: "", + SystemId: "e4ba747b-d9f5-45a2-b749-45567054bbe5", + }, + }, + }, + NodeGroupList: []irs.NodeGroupInfo{ + { + IId: irs.IID{ + NameId: "default-worker", + SystemId: "", + }, + ImageIID: irs.IID{ + NameId: "ubuntu 18.04", + SystemId: "2717ec03-3a4d-4728-b372-183065facdba", + }, + VMSpecName: "13646526-0bb9-400b-929f-797fdb7547eb", // flavor_id, + RootDiskType: "General HDD", + RootDiskSize: "20", // root_volume_size + KeyPairIID: irs.IID{ + NameId: "kp1", + SystemId: "", + }, + OnAutoScaling: true, + MinNodeSize: 1, + MaxNodeSize: 3, + DesiredNodeSize: 1, // node_count + }, + }, + } + + cluster, err := clusterHandler.CreateCluster(clusterInfo) + if err != nil { + t.Error(err) + } + printObj2Flat(cluster) + + cluster_, err := clusterHandler.GetCluster(cluster.IId) + if err != nil { + println(err.Error()) + } + t.Log(cluster_) + printObj2Flat(cluster_) + + t.Log(cluster) +} + +func TestListCluster(t *testing.T) { + + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, err := clusterHandler.ListCluster() + if err != nil { + t.Error(err) + } + + for _, cluster := range clusters { + t.Log(cluster) + printObj2Flat(cluster) + } + +} + +func TestGetCluster(t *testing.T) { + + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, err := clusterHandler.ListCluster() + if err != nil { + t.Error(err) + } + + t.Log(clusters) + + for _, cluster := range clusters { + cluster_, err := clusterHandler.GetCluster(cluster.IId) + if err != nil { + println(err.Error()) + } + t.Log(cluster_) + printObj2Flat(cluster_) + } +} + +// func TestDeleteCluster(t *testing.T) { +// } + +func TestAddNodeGroup(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + new_node_group := irs.NodeGroupInfo{ + IId: irs.IID{NameId: "added-nodegroup", SystemId: ""}, + ImageIID: irs.IID{NameId: "", SystemId: "2717ec03-3a4d-4728-b372-183065facdba"}, + VMSpecName: "13646526-0bb9-400b-929f-797fdb7547eb", + RootDiskType: "General HDD", + RootDiskSize: "20", + KeyPairIID: irs.IID{NameId: "kp1", SystemId: ""}, + OnAutoScaling: true, + MinNodeSize: 1, + MaxNodeSize: 3, + DesiredNodeSize: 1, + } + + t.Log(cluster) + node_group, err := clusterHandler.AddNodeGroup(cluster.IId, new_node_group) + if err != nil { + t.Error(err) + } + + t.Log(node_group) + + printObj2Flat(node_group) + } +} + +func TestListNodeGroup(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + node_groups, _ := clusterHandler.ListNodeGroup(cluster.IId) + for _, node_group := range node_groups { + t.Log(node_group.IId.NameId, node_group.IId.SystemId) + t.Log(node_group) + printObj2Flat(node_group) + } + + } +} + +func TestGetNodeGroup(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + node_groups, _ := clusterHandler.ListNodeGroup(cluster.IId) + for _, node_group := range node_groups { + node_group_, err := clusterHandler.GetNodeGroup(cluster.IId, node_group.IId) + if err != nil { + t.Error(err) + } + t.Log(node_group_.IId.NameId, node_group_.IId.SystemId) + t.Log(node_group_) + printObj2Flat(node_group_) + } + } +} + +func TestSetNodeGroupAutoScaling(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + node_groups, _ := clusterHandler.ListNodeGroup(cluster.IId) + for _, node_group := range node_groups { + + res, err := clusterHandler.SetNodeGroupAutoScaling(cluster.IId, node_group.IId, false) + if err != nil { + t.Error(err) + } + t.Log(res) + + res, err = clusterHandler.SetNodeGroupAutoScaling(cluster.IId, node_group.IId, true) + if err != nil { + t.Error(err) + } + t.Log(res) + } + } +} + +func TestChangeNodeGroupScaling(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + node_groups, _ := clusterHandler.ListNodeGroup(cluster.IId) + for _, node_group := range node_groups { + + res, err := clusterHandler.ChangeNodeGroupScaling(cluster.IId, node_group.IId, 0, 2, 5) + if err != nil { + t.Error(err) + } + t.Log(res) + printObj2Flat(res) + + res, err = clusterHandler.ChangeNodeGroupScaling(cluster.IId, node_group.IId, 0, 1, 3) + if err != nil { + t.Error(err) + } + t.Log(res) + printObj2Flat(res) + } + } +} + +func TestRemoveNodeGroup(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + node_groups, _ := clusterHandler.ListNodeGroup(cluster.IId) + for _, node_group := range node_groups { + res, err := clusterHandler.RemoveNodeGroup(cluster.IId, node_group.IId) + if err != nil { + t.Error(err) + } + if res == false { + t.Error("Failed to remove node group.") + //"deleting the last nodegroup is not supported + } + printObj2Flat(res) + } + } +} + +func TestUpgradeCluster(t *testing.T) { + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + //res, err := clusterHandler.UpgradeCluster(cluster.IId, "v1.23.3") + res, err := clusterHandler.UpgradeCluster(cluster.IId, "v1.24.3") + if err != nil { + t.Error(err) + } + t.Log(res) + printObj2Flat(res) + } +} + +func TestDeleteCluster(t *testing.T) { + + clusterHandler, err := getClusterHandler() + if err != nil { + t.Error(err) + } + + clusters, _ := clusterHandler.ListCluster() + for _, cluster := range clusters { + println(cluster.IId.NameId, cluster.IId.SystemId) + result, err := clusterHandler.DeleteCluster(cluster.IId) + if err != nil { + t.Error(err) + } + t.Log(result) + printObj2Flat(result) + } +} + +func TestAll(t *testing.T) { + // CSP 리전별 시나리오 기반 테스트 +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/calllog_conf.yaml b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/calllog_conf.yaml new file mode 100644 index 000000000..519db2629 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/calllog_conf.yaml @@ -0,0 +1,18 @@ +#### Config for Call-Log Lib. #### + +calllog: + ## true | false + loopcheck: false # This temp method for development is busy wait. cf) calllogger.go:levelSetupLoop(). + + ## info | error // The error is like switching off the call-log. + loglevel: info # If loopcheck is true, You can set this online. + + ## true | false // Now false is reserved for the future. + logfile: true + +## Config for File Output ## +logfileinfo: + filename: $CBSPIDER_ROOT/log/calllog/calllogs.log + maxsize: 20 # megabytes + maxbackups: 100 + maxage: 365 # days \ No newline at end of file diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/log_conf.yaml b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/log_conf.yaml new file mode 100644 index 000000000..437ad3fab --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/pmks_test/conf/log_conf.yaml @@ -0,0 +1,19 @@ +#### Config for cb-log Lib. #### + +cblog: + ## true | false + loopcheck: false # This temp method for development is busy wait. cf) cblogger.go:levelSetupLoop(). + + ## trace | debug | info | warn/warning | error | fatal | panic + ## Default logging level: info + loglevel: info # If loopcheck is true, You can set this online. + + ## true | false + logfile: true + +## Config for File Output ## +logfileinfo: + filename: $CBSPIDER_ROOT/log/cblogs.log + maxsize: 10 # megabytes + maxbackups: 50 + maxage: 31 # days diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_cluster.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_cluster.sh new file mode 100755 index 000000000..a59a82c68 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_cluster.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_ClusterHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_disk.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_disk.sh new file mode 100755 index 000000000..d3d396c3b --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_disk.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_DiskHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_image.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_image.sh new file mode 100755 index 000000000..aabad167c --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_image.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_ImageHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_keypair.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_keypair.sh new file mode 100755 index 000000000..02d1639a5 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_keypair.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_KeyPairHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_myimage.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_myimage.sh new file mode 100755 index 000000000..91211174a --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_myimage.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_MyImageHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_nlb.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_nlb.sh new file mode 100755 index 000000000..db56e1923 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_nlb.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_NLBHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_region-zone.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_region-zone.sh new file mode 100755 index 000000000..595e3b5f5 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_region-zone.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_RegionZoneHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_security.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_security.sh new file mode 100755 index 000000000..252ea90f4 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_security.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_SecurityHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vm.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vm.sh new file mode 100755 index 000000000..b65b1d5af --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vm.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_VMHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vmspec.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vmspec.sh new file mode 100755 index 000000000..17ec78feb --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vmspec.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_VMSpecHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vpc.sh b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vpc.sh new file mode 100755 index 000000000..a62a0061c --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/main/test_vpc.sh @@ -0,0 +1,3 @@ +source ../../../../../setup.env + +go run Test_VPCHandler.go diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ClusterHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ClusterHandler.go new file mode 100644 index 000000000..819bd7f37 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ClusterHandler.go @@ -0,0 +1,756 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, 2022.08. + +package resources + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + "runtime/debug" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/jeremywohl/flatten" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudClusterHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + ClusterClient *nhnsdk.ServiceClient +} + +func (clusterHandler *NhnCloudClusterHandler) CreateCluster(clusterReqInfo irs.ClusterInfo) (irs.ClusterInfo, error) { + cblogger.Info("NHN Cloud Driver: called CreateCluster()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterReqInfo.IId.NameId, "CreateCluster()") + + start := call.Start() + // 클러스터 생성 요청을 JSON 요청으로 변환 + payload, err := getClusterInfoRequestJSONString(clusterReqInfo, clusterHandler) + if err != nil { + err := fmt.Errorf("Failed to Get ClusterInfo JSON String : %v", err) + cblogger.Error(err) + return irs.ClusterInfo{}, err + } + response_json_str, err := CreateCluster(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, payload) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Create Cluster : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.ClusterInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + // 클러스터를 생성하고 나서 바로 GetClusterInfo를 하면 + // 클러스터 정보를 가져올 때도 있지만 못가져 오는 경우도 있다. + // 생성요청이 진행되는 중에 조회를 시도해서 그런것으로 추정된다. + // 실패를 방지하기 위해 10초간 대기한 후에 조회를 시도한다. + time.Sleep(time.Second * 10) + + // 클러스터 생성이 성공하면 getClusterInfo로 조회해서 반환한다. + // 만약 getClusterInfo가 실패하면, 요청정보에 cluster_id를 포함해서 반환하는 것으로 처리한다. + var response_json_obj map[string]interface{} + json.Unmarshal([]byte(response_json_str), &response_json_obj) + clusterReqInfo.IId.SystemId = response_json_obj["uuid"].(string) + + // // NodeGroup 생성 정보가 있는경우 생성을 시도한다. + // // 문제는 Cluster 생성이 완료되어야 NodeGroup 생성이 가능하다. + // // Cluster 생성이 완료되려면 최소 10분 이상 걸린다. + // // 성공할때까지 대기한 후에 생성을 시도해야 하는가? + // for { + // time.Sleep(10 * time.Second) + // clusterInfo, err := getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterReqInfo.IId.SystemId) + // if err != nil { + // err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // cblogger.Info(clusterInfo.Status) + // if clusterInfo.Status == irs.ClusterActive { + // break + // } + // } + + // for _, node_group := range clusterReqInfo.NodeGroupList { + // node_group_info, err := clusterHandler.AddNodeGroup(cluster_info.IId, node_group) + // if err != nil { + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // cluster_info.NodeGroupList = append(cluster_info.NodeGroupList, node_group_info) + // } + + cluster_info, err := getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterReqInfo.IId.SystemId) + if err != nil { + err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + cblogger.Error(err) + return irs.ClusterInfo{}, err + } + if cluster_info == nil { + err = fmt.Errorf("Succeeded in Creating Cluster but Failed to Get ClusterInfo") + cblogger.Error(err) + return clusterReqInfo, err + } + + return *cluster_info, nil +} + +func (clusterHandler *NhnCloudClusterHandler) ListCluster() ([]*irs.ClusterInfo, error) { + + cblogger.Info("NHN Cloud Driver: called ListCluster()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, "ListCluster()", "ListCluster()") // HisCall logging + + start := call.Start() + clusters_json_str, err := GetClusters(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to List Cluster : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return nil, err + } + cblogger.Info(call.String(callLogInfo)) + + var clusters_json_obj map[string]interface{} + json.Unmarshal([]byte(clusters_json_str), &clusters_json_obj) + clusters := clusters_json_obj["clusters"].([]interface{}) + cluster_info_list := make([]*irs.ClusterInfo, len(clusters)) + for i, cluster := range clusters { + uuid := cluster.(map[string]interface{})["uuid"].(string) + cluster_info_list[i], err = getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, uuid) + if err != nil { + err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + cblogger.Error(err) + return nil, err + } + } + + return cluster_info_list, nil +} + +func (clusterHandler *NhnCloudClusterHandler) GetCluster(clusterIID irs.IID) (irs.ClusterInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetCluster()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "GetCluster()") + + start := call.Start() + cluster_info, err := getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.ClusterInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + return *cluster_info, nil +} + +func (clusterHandler *NhnCloudClusterHandler) DeleteCluster(clusterIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteCluster()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "DeleteCluster()") + + start := call.Start() + res, err := DeleteCluster(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Delete Cluster : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return false, err + } + if res != "" { + // 삭제 처리를 성공하면 ""를 리턴한다. + // 삭제 처리를 실패하면 에러 메시지를 리턴한다. + err = fmt.Errorf("Failed to Delete Cluster : %s", res) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return false, err + } + cblogger.Info(call.String(callLogInfo)) + + return true, nil +} + +func (clusterHandler *NhnCloudClusterHandler) AddNodeGroup(clusterIID irs.IID, nodeGroupReqInfo irs.NodeGroupInfo) (irs.NodeGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called AddNodeGroup()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "AddNodeGroup()") + + start := call.Start() + // 노드 그룹 생성 요청을 JSON 요청으로 변환 + payload, err := getNodeGroupRequestJSONString(nodeGroupReqInfo, clusterHandler) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroup Request JSON String : %v", err) + cblogger.Error(err) + return irs.NodeGroupInfo{}, err + } + result_json_str, err := CreateNodeGroup(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, payload) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Create NodeGroup : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.NodeGroupInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + var result_json_obj map[string]interface{} + json.Unmarshal([]byte(result_json_str), &result_json_obj) + if result_json_obj["errors"] != nil { + err := fmt.Errorf("Failed to Create NodeGroup : %s", result_json_str) + cblogger.Error(err) + return irs.NodeGroupInfo{}, err + } + uuid := result_json_obj["uuid"].(string) + node_group_info, err := getNodeGroupInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, uuid) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroupInfo : %v", err) + cblogger.Error(err) + return irs.NodeGroupInfo{}, err + } + + return *node_group_info, nil +} + +func (clusterHandler *NhnCloudClusterHandler) ListNodeGroup(clusterIID irs.IID) ([]*irs.NodeGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListNodeGroup()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "ListNodeGroup()") + + start := call.Start() + node_group_info_list := []*irs.NodeGroupInfo{} + node_groups_json_str, err := GetNodeGroups(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to List NodeGroup : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return node_group_info_list, err + } + cblogger.Info(call.String(callLogInfo)) + + var node_groups_json_obj map[string]interface{} + json.Unmarshal([]byte(node_groups_json_str), &node_groups_json_obj) + node_groups := node_groups_json_obj["nodegroups"].([]interface{}) + for _, node_group := range node_groups { + node_group_info, err := getNodeGroupInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, node_group.(map[string]interface{})["uuid"].(string)) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroupInfo : %v", err) + cblogger.Error(err) + return node_group_info_list, err + } + node_group_info_list = append(node_group_info_list, node_group_info) + } + + return node_group_info_list, nil +} + +func (clusterHandler *NhnCloudClusterHandler) GetNodeGroup(clusterIID irs.IID, nodeGroupIID irs.IID) (irs.NodeGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetNodeGroup()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "GetNodeGroup()") + + start := call.Start() + temp, err := getNodeGroupInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, nodeGroupIID.SystemId) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroup : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.NodeGroupInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + return *temp, nil +} + +func (clusterHandler *NhnCloudClusterHandler) SetNodeGroupAutoScaling(clusterIID irs.IID, nodeGroupIID irs.IID, on bool) (bool, error) { + cblogger.Info("NHN Cloud Driver: called SetNodeGroupAutoScaling()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "GetNodeGroup()") + + start := call.Start() + temp := `{"ca_enable": %t}` + payload := fmt.Sprintf(temp, on) + _, err := ChangeNodeGroupAutoScaler(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, nodeGroupIID.SystemId, payload) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Set NodeGroup AutoScaling : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return false, err + } + cblogger.Info(call.String(callLogInfo)) + + return true, nil +} + +func (clusterHandler *NhnCloudClusterHandler) ChangeNodeGroupScaling(clusterIID irs.IID, nodeGroupIID irs.IID, desiredNodeSize int, minNodeSize int, maxNodeSize int) (irs.NodeGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called ChangeNodeGroupScaling()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "GetNodeGroup()") + + start := call.Start() + temp := `{ + "ca_enable": true, + "ca_max_node_count": %d, + "ca_min_node_count": %d + }` + payload := fmt.Sprintf(temp, maxNodeSize, minNodeSize) + //desiredNodeSize is not supported in NHN Cloud + _, err := ChangeNodeGroupAutoScaler(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, nodeGroupIID.SystemId, payload) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Change NodeGroup Scaling : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.NodeGroupInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + node_group_info, err := getNodeGroupInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, nodeGroupIID.SystemId) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroupInfo : %v", err) + cblogger.Error(err) + return irs.NodeGroupInfo{}, err + } + + return *node_group_info, nil +} + +func (clusterHandler *NhnCloudClusterHandler) RemoveNodeGroup(clusterIID irs.IID, nodeGroupIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called RemoveNodeGroup()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "RemoveNodeGroup()") + + start := call.Start() + res, err := DeleteNodeGroup(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, nodeGroupIID.SystemId) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Remove NodeGroup : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return false, err + } + if res != "" { + // 삭제 처리를 성공하면 ""를 리턴한다. + // 삭제 처리를 실패하면 에러 메시지를 리턴한다. + err := fmt.Errorf("Failed to Remove NodeGroup : %s", res) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return false, err + } + cblogger.Info(call.String(callLogInfo)) + + return true, nil +} + +// 업그레이드 순서 +// default-master 노드 그룹을 업그레이드한다. +// default-master 업그레이드가 완료되면, worker 노드들을 업그레이드 한다. +// default-master 업그레이드가 완료되기 전에는 worker 노드를 업그레이드 할 수 없다. +// default-master 업그레이드가 완료된 후에 (10분? 정도 소요됨) worker 노드를 업그레이드해야 한다. +func (clusterHandler *NhnCloudClusterHandler) UpgradeCluster(clusterIID irs.IID, newVersion string) (irs.ClusterInfo, error) { + cblogger.Info("NHN Cloud Driver: called UpgradeCluster()") + callLogInfo := GetCallLogScheme(clusterHandler.RegionInfo.Region, call.CLUSTER, clusterIID.NameId, "UpgradeCluster()") + + start := call.Start() + temp := `{"version": "%s"}` + payload := fmt.Sprintf(temp, newVersion) + res, err := UpgradeCluster(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, "default-master", payload) + callLogInfo.ElapsedTime = call.Elapsed(start) + if err != nil { + err := fmt.Errorf("Failed to Upgrade Cluster : %v", err) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.ClusterInfo{}, err + } + if strings.Contains(res, "errors") { + // {"errors": [{"request_id": "", "code": "", "status": 406, "title": "", "detail": "", "links": []}]} + err := fmt.Errorf("Failed to Upgrade Cluster : %s", res) + cblogger.Error(err) + cblogger.Error(call.String(callLogInfo)) + return irs.ClusterInfo{}, err + } + cblogger.Info(call.String(callLogInfo)) + + // // default-master 업그레이드 완료 확인 코드 + // // 업그레이드가 완료되면 worker 노드 업그레이드 진행 + // for { + // time.Sleep(10 * time.Second) + // clusterInfo, err := getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + // if err != nil { + // err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // cblogger.Info(clusterInfo.Status) + // if clusterInfo.Status == irs.ClusterActive { + // break + // } + // } + + // node_groups_json_str, err := GetNodeGroups(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + // if err != nil { + // err := fmt.Errorf("Failed to Get NodeGroups : %v", err) + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // var node_groups_json_obj map[string]interface{} + // json.Unmarshal([]byte(node_groups_json_str), &node_groups_json_obj) + // node_groups := node_groups_json_obj["nodegroups"].([]interface{}) + // for _, node_group := range node_groups { + // start := call.Start() + // res, err := UpgradeCluster(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId, node_group.(map[string]interface{})["uuid"].(string), payload) + // callLogInfo.ElapsedTime = call.Elapsed(start) + // cblogger.Info(call.String(callLogInfo)) + // if err != nil { + // err := fmt.Errorf("Failed to Upgrade Cluster : %v", err) + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // if strings.Contains(res, "errors") { + // // {"errors": [{"request_id": "", "code": "", "status": 406, "title": "", "detail": "", "links": []}]} + // err := fmt.Errorf("Failed to Upgrade Cluster : %s", res) + // cblogger.Error(err) + // return irs.ClusterInfo{}, err + // } + // } + + clusterInfo, err := getClusterInfo(clusterHandler.ClusterClient.Endpoint, clusterHandler.ClusterClient.TokenID, clusterIID.SystemId) + if err != nil { + err := fmt.Errorf("Failed to Get ClusterInfo : %v", err) + cblogger.Error(err) + return irs.ClusterInfo{}, err + } + + return *clusterInfo, nil +} + +func getClusterInfo(host string, token string, cluster_id string) (clusterInfo *irs.ClusterInfo, err error) { + + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Failed to Process GetClusterInfo() %v\n\n%v", r, string(debug.Stack())) + cblogger.Error(err) + } + }() + + cluster_json_str, err := GetCluster(host, token, cluster_id) + if err != nil { + err := fmt.Errorf("Failed to Get Cluster : %v", err) + cblogger.Error(err) + return nil, err + } + var cluster_json_obj map[string]interface{} + json.Unmarshal([]byte(cluster_json_str), &cluster_json_obj) + + health_status := cluster_json_obj["status"].(string) + cluster_status := irs.ClusterActive + if health_status == "CREATE_IN_PROGRESS" { + cluster_status = irs.ClusterCreating + } else if health_status == "UPDATE_IN_PROGRESS" { + cluster_status = irs.ClusterUpdating + } else if health_status == "UPDATE_FAILED" { + cluster_status = irs.ClusterInactive + } else if health_status == "DELETE_IN_PROGRESS" { + cluster_status = irs.ClusterDeleting + } else if health_status == "CREATE_COMPLETE" { + cluster_status = irs.ClusterActive + } + + created_at := cluster_json_obj["created_at"].(string) + datetime, err := time.Parse(time.RFC3339, created_at) + //RFC3339 = "2006-01-02T15:04:05Z07:00" + //datetime, err := time.Parse("2006-01-02T15:04:05+07:00", created_at) + if err != nil { + err := fmt.Errorf("Failed to Parse Created Time : %v", err) + cblogger.Error(err) + panic(err) + } + + version := "" + if cluster_json_obj["coe_version"] != nil { + version = cluster_json_obj["coe_version"].(string) + } + + clusterInfo = &irs.ClusterInfo{ + IId: irs.IID{ + NameId: cluster_json_obj["name"].(string), + SystemId: cluster_json_obj["uuid"].(string), + }, + Version: version, + Network: irs.NetworkInfo{ + VpcIID: irs.IID{ + NameId: "", + SystemId: cluster_json_obj["fixed_network"].(string), + }, + SubnetIIDs: []irs.IID{ + { + NameId: "", + SystemId: cluster_json_obj["fixed_subnet"].(string), + }, + }, + SecurityGroupIIDs: []irs.IID{ + { + NameId: "", + SystemId: "", + }, + }, + }, + Addons: irs.AddonsInfo{ + KeyValueList: []irs.KeyValue{}, + }, + Status: cluster_status, + CreatedTime: datetime, + KeyValueList: []irs.KeyValue{}, + } + + // k,v 추출 + // k,v 변환 규칙 작성 [k,v]:[ClusterInfo.k, ClusterInfo.v] + // 변환 규칙에 따라 k,v 변환 + // flat, err := flatten.FlattenString(cluster_json_str, "", flatten.DotStyle) + flat, err := flatten.Flatten(cluster_json_obj, "", flatten.DotStyle) + if err != nil { + err := fmt.Errorf("Failed to Flatten Cluster Info : %v", err) + cblogger.Error(err) + return nil, err + } + for k, v := range flat { + temp := fmt.Sprintf("%v", v) + clusterInfo.KeyValueList = append(clusterInfo.KeyValueList, irs.KeyValue{Key: k, Value: temp}) + } + + // NodeGroups + node_groups_json_str, err := GetNodeGroups(host, token, cluster_id) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroups : %v", err) + cblogger.Error(err) + return nil, err + } + + if node_groups_json_str == "" { + err := fmt.Errorf("Failed to Get Node Groups") + cblogger.Error(err) + return nil, err + } + + var node_groups_json_obj map[string]interface{} + json.Unmarshal([]byte(node_groups_json_str), &node_groups_json_obj) + node_groups := node_groups_json_obj["nodegroups"].([]interface{}) + for _, node_group := range node_groups { + node_group_id := node_group.(map[string]interface{})["uuid"].(string) + node_group_info, err := getNodeGroupInfo(host, token, cluster_id, node_group_id) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroupInfo : %v", err) + cblogger.Error(err) + return nil, err + } + clusterInfo.NodeGroupList = append(clusterInfo.NodeGroupList, *node_group_info) + } + + return clusterInfo, err +} + +func getNodeGroupInfo(host string, token string, cluster_id string, node_group_id string) (nodeGroupInfo *irs.NodeGroupInfo, err error) { + + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Failed to Process NodeGroupInfo : %v\n\n%v", r, string(debug.Stack())) + cblogger.Error(err) + } + }() + + node_group_json_str, err := GetNodeGroup(host, token, cluster_id, node_group_id) + if err != nil { + err := fmt.Errorf("Failed to Get NodeGroup : %v", err) + cblogger.Error(err) + return nil, err + } + + if node_group_json_str == "" { + err := fmt.Errorf("Failed to Get NodeGroup") + cblogger.Error(err) + return nil, err + } + + var node_group_json_obj map[string]interface{} + json.Unmarshal([]byte(node_group_json_str), &node_group_json_obj) + + health_status := node_group_json_obj["status"].(string) + status := irs.NodeGroupActive + if strings.EqualFold(health_status, "UPDATE_COMPLETE") { + status = irs.NodeGroupActive + } else if strings.EqualFold(health_status, "CREATE_IN_PROGRESS") { + status = irs.NodeGroupUpdating + } else if strings.EqualFold(health_status, "UPDATE_IN_PROGRESS") { + status = irs.NodeGroupUpdating // removing is a kind of updating? + } else if strings.EqualFold(health_status, "DELETE_IN_PROGRESS") { + status = irs.NodeGroupDeleting + } else if strings.EqualFold(health_status, "UPDATE_IN_PROGRESS") { + status = irs.NodeGroupUpdating + } else if strings.EqualFold(health_status, "CREATE_COMPLETE") { + status = irs.NodeGroupActive + } + + auto_scaling, _ := strconv.ParseBool(node_group_json_obj["labels"].(map[string]interface{})["ca_enable"].(string)) + ca_min_node_count, _ := strconv.ParseInt(node_group_json_obj["labels"].(map[string]interface{})["ca_min_node_count"].(string), 10, 32) + ca_max_node_count, _ := strconv.ParseInt(node_group_json_obj["labels"].(map[string]interface{})["ca_max_node_count"].(string), 10, 32) + node_count := int(node_group_json_obj["node_count"].(float64)) + + nodeGroupInfo = &irs.NodeGroupInfo{ + IId: irs.IID{ + NameId: node_group_json_obj["name"].(string), + SystemId: node_group_json_obj["uuid"].(string), + }, + ImageIID: irs.IID{ + NameId: "", + SystemId: node_group_json_obj["image_id"].(string), + }, + VMSpecName: node_group_json_obj["flavor_id"].(string), + RootDiskType: node_group_json_obj["labels"].(map[string]interface{})["boot_volume_size"].(string), + RootDiskSize: node_group_json_obj["labels"].(map[string]interface{})["boot_volume_type"].(string), + KeyPairIID: irs.IID{}, + OnAutoScaling: auto_scaling, + MinNodeSize: int(ca_min_node_count), + MaxNodeSize: int(ca_max_node_count), + DesiredNodeSize: int(node_count), + Nodes: []irs.IID{}, + KeyValueList: []irs.KeyValue{}, + Status: status, + } + + // k,v 추출 + // k,v 변환 규칙 작성 [k,v]:[ClusterInfo.k, ClusterInfo.v] + // 변환 규칙에 따라 k,v 변환 + // flat, err := flatten.FlattenString(cluster_json_str, "", flatten.DotStyle) + flat, err := flatten.Flatten(node_group_json_obj, "", flatten.DotStyle) + if err != nil { + err := fmt.Errorf("Failed to Flatten NodeGroup") + cblogger.Error(err) + return nil, err + } + for k, v := range flat { + temp := fmt.Sprintf("%v", v) + nodeGroupInfo.KeyValueList = append(nodeGroupInfo.KeyValueList, irs.KeyValue{Key: k, Value: temp}) + } + + return nodeGroupInfo, err +} + +func getClusterInfoRequestJSONString(clusterInfo irs.ClusterInfo, clusterHandler *NhnCloudClusterHandler) (info string, err error) { + + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Failed to Process getClusterInfoRequestJSONString() : %v\n\n%v", r, string(debug.Stack())) + cblogger.Error(err) + } + }() + + fixed_network := clusterInfo.Network.VpcIID.SystemId + fixed_subnet := clusterInfo.Network.SubnetIIDs[0].SystemId + flavor_id := clusterInfo.NodeGroupList[0].VMSpecName + keypair := clusterInfo.NodeGroupList[0].KeyPairIID.NameId + + availability_zone := clusterHandler.RegionInfo.Zone + boot_volume_size := clusterInfo.NodeGroupList[0].RootDiskSize + boot_volume_type := clusterInfo.NodeGroupList[0].RootDiskType + + ca_enable := "false" + if clusterInfo.NodeGroupList[0].OnAutoScaling { + ca_enable = "true" + } + ca_max_node_count := strconv.Itoa(clusterInfo.NodeGroupList[0].MaxNodeSize) + ca_min_node_count := strconv.Itoa(clusterInfo.NodeGroupList[0].MinNodeSize) + kube_tag := clusterInfo.Version + node_image := clusterInfo.NodeGroupList[0].ImageIID.SystemId + name := clusterInfo.IId.NameId + node_count := strconv.Itoa(clusterInfo.NodeGroupList[0].DesiredNodeSize) + + for _, v := range clusterInfo.NodeGroupList[0].KeyValueList { + switch v.Key { + case "availability_zone": + availability_zone = v.Value + } + } + + temp := `{ + "cluster_template_id": "iaas_console", + "create_timeout": 60, + "fixed_network": "%s", + "fixed_subnet": "%s", + "flavor_id": "%s", + "keypair": "%s", + "labels": { + "availability_zone": "%s", + "boot_volume_size": "%s", + "boot_volume_type": "%s", + "ca_enable": "%s", + "ca_max_node_count": "%s", + "ca_min_node_count": "%s", + "cert_manager_api": "True", + "clusterautoscale": "nodegroupfeature", + "kube_tag": "%s", + "master_lb_floating_ip_enabled": "False", + "node_image": "%s", + "user_script_v2": "" + }, + "name": "%s", + "node_count": %s + }` + + info = fmt.Sprintf(temp, fixed_network, fixed_subnet, flavor_id, keypair, availability_zone, boot_volume_size, boot_volume_type, ca_enable, ca_max_node_count, ca_min_node_count, kube_tag, node_image, name, node_count) + + return info, err +} + +func getNodeGroupRequestJSONString(nodeGroupReqInfo irs.NodeGroupInfo, clusterHandler *NhnCloudClusterHandler) (payload string, err error) { + + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Failed to Process getNodeGroupRequestJSONString() : %v\n\n%v", r, string(debug.Stack())) + cblogger.Error(err) + } + }() + + name := nodeGroupReqInfo.IId.NameId + node_count := nodeGroupReqInfo.DesiredNodeSize + flavor_id := nodeGroupReqInfo.VMSpecName + image_id := nodeGroupReqInfo.ImageIID.SystemId + + boot_volume_size := nodeGroupReqInfo.RootDiskSize + boot_volume_type := nodeGroupReqInfo.RootDiskType + + ca_enable := strconv.FormatBool(nodeGroupReqInfo.OnAutoScaling) + ca_max_node_count := strconv.Itoa(nodeGroupReqInfo.MaxNodeSize) + ca_min_node_count := strconv.Itoa(nodeGroupReqInfo.MinNodeSize) + + availability_zone := clusterHandler.RegionInfo.Zone + + temp := `{ + "name": "%s", + "node_count": %d, + "flavor_id": "%s", + "image_id": "%s", + "labels": { + "availability_zone": "%s", + "boot_volume_size": "%s", + "boot_volume_type": "%s", + "ca_enable": "%s", + "ca_max_node_count": "%s", + "ca_min_node_count": "%s" + } + }` + + payload = fmt.Sprintf(temp, name, node_count, flavor_id, image_id, availability_zone, boot_volume_size, boot_volume_type, ca_enable, ca_max_node_count, ca_min_node_count) + + return payload, err +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/CommonNhnCloudFunc.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/CommonNhnCloudFunc.go new file mode 100644 index 000000000..46e66023c --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/CommonNhnCloudFunc.go @@ -0,0 +1,294 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. + +package resources + +import ( + "os" + "os/exec" + // "errors" + "fmt" + "encoding/json" + + "strings" + "sync" + "time" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/secgroups" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/flavors" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/external" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/networks" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/ports" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/subnets" + + "github.com/sirupsen/logrus" + + cblog "github.com/cloud-barista/cb-log" + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +var once sync.Once +var cblogger *logrus.Logger +var calllogger *logrus.Logger + +func InitLog() { + once.Do(func() { + // cblog is a global variable. + cblogger = cblog.GetLogger("CB-SPIDER") + calllogger = call.GetLogger("HISCALL") + }) +} + +func LoggingError(hiscallInfo call.CLOUDLOGSCHEMA, err error) { + cblogger.Error(err.Error()) + hiscallInfo.ErrorMSG = err.Error() + calllogger.Error(call.String(hiscallInfo)) +} + +func LoggingInfo(hiscallInfo call.CLOUDLOGSCHEMA, start time.Time) { + hiscallInfo.ElapsedTime = call.Elapsed(start) + calllogger.Info(call.String(hiscallInfo)) +} + +func GetCallLogScheme(zone string, resourceType call.RES_TYPE, resourceName string, apiName string) call.CLOUDLOGSCHEMA { + cblogger.Info(fmt.Sprintf("Call %s %s", call.NHNCLOUD, apiName)) + + return call.CLOUDLOGSCHEMA{ + CloudOS: call.NHNCLOUD, + RegionZone: zone, + ResourceType: resourceType, + ResourceName: resourceName, + CloudOSAPI: apiName, + } +} + +func logAndReturnError(callLogInfo call.CLOUDLOGSCHEMA, givenErrString string, v interface{}) (error) { + newErr := fmt.Errorf(givenErrString + " %v", v) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return newErr +} + +func GetPublicVPCInfo(client *nhnsdk.ServiceClient, typeName string) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetPublicVPCInfo()") + + exTrue := true + listOpts := external.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + External: &exTrue, + } + allPages, err := networks.List(client, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get VPC Pages. %s", err.Error()) + cblogger.Error(newErr) + return "", newErr + } + + // external VPC 필터링 + var extVpcList []NetworkWithExt + err = networks.ExtractNetworksInto(allPages, &extVpcList) + if err != nil { + newErr := fmt.Errorf("Failed to Get VPC list. %s", err.Error()) + cblogger.Error(newErr) + return "", newErr + } + if len(extVpcList) == 0 { + newErr := fmt.Errorf("Failed to Get VPC list, external VPC not exist") + cblogger.Error(newErr) + return "", newErr + } + + extVpc := extVpcList[0] + + if strings.EqualFold(typeName, "ID") { + return extVpc.ID, nil + } else if strings.EqualFold(typeName, "NAME") { + return extVpc.Name, nil + } + + return "", nil +} + +func GetVMSpecIdWithName(client *nhnsdk.ServiceClient, flavorName string) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetVMSpecIdWithName()") + + allPages, err := flavors.ListDetail(client, nil).AllPages() + if err != nil { + return "", err + } + nhnFlavorList, err := flavors.ExtractFlavors(allPages) + if err != nil { + return "", err + } + + for _, nhnFlavor := range nhnFlavorList { + if strings.EqualFold(nhnFlavor.Name, flavorName) { + return nhnFlavor.ID, nil + } + } + + return "", fmt.Errorf("Failed to Find Flavor with the name [%s]", flavorName) +} + +func GetSGWithName(networkClient *nhnsdk.ServiceClient, securityGroupName string) (*secgroups.SecurityGroup, error) { + cblogger.Info("NHN Cloud Driver: called GetSGWithName()") + + allPages, err := secgroups.List(networkClient).AllPages() + if err != nil { + return nil, err + } + nhnSGList, err := secgroups.ExtractSecurityGroups(allPages) + if err != nil { + return nil, err + } + + for _, nhnSG := range nhnSGList { + if strings.EqualFold(nhnSG.Name, securityGroupName) { + return &nhnSG, nil + } + } + + return nil, fmt.Errorf("Failed to Find SecurityGroups with the name [%s]", securityGroupName) +} + +func GetNetworkWithName(networkClient *nhnsdk.ServiceClient, networkName string) (*networks.Network, error) { + cblogger.Info("NHN Cloud Driver: called GetNetworkWithName()") + + allPages, err := networks.List(networkClient, networks.ListOpts{Name: networkName}).AllPages() + if err != nil { + return nil, err + } + nhnNetList, err := networks.ExtractNetworks(allPages) + if err != nil { + return nil, err + } + + for _, nhnNetwork := range nhnNetList { + if strings.EqualFold(nhnNetwork.Name, networkName) { + return &nhnNetwork, nil + } + } + + return nil, fmt.Errorf("Failed to Find SecurityGroups Info with name [%s]", networkName) +} + +func GetSubnetWithId(networkClient *nhnsdk.ServiceClient, subnetId string) (*subnets.Subnet, error) { + cblogger.Info("NHN Cloud Driver: called GetSubnetWithId()") + + nhnSubnet, err := subnets.Get(networkClient, subnetId).Extract() + if err != nil { + return nil, err + } + + return nhnSubnet, nil +} + +func GetPortWithDeviceId(networkClient *nhnsdk.ServiceClient, deviceID string) (*ports.Port, error) { + cblogger.Info("NHN Cloud Driver: called GetPortWithDeviceId()") + + allPages, err := ports.List(networkClient, ports.ListOpts{}).AllPages() + if err != nil { + return nil, err + } + nhnPortList, err := ports.ExtractPorts(allPages) + if err != nil { + return nil, err + } + + for _, nhnPort := range nhnPortList { + if strings.EqualFold(nhnPort.DeviceID, deviceID) { + return &nhnPort, nil + } + } + + return nil, fmt.Errorf("Failed to Get Port Info. with the DeviceID [%s]", deviceID) +} + +func CheckIIDValidation(IId irs.IID) bool { + if strings.EqualFold(IId.NameId, "") && strings.EqualFold(IId.SystemId, "") { + newErr := fmt.Errorf("Invalid NameId and SystemId!!") + cblogger.Error(newErr.Error()) + return false + } + return true +} + +func CheckFolderAndCreate(folderPath string) error { + // Check if the Folder Exists and Create it + if _, err := os.Stat(folderPath); os.IsNotExist(err) { + if err := os.Mkdir(folderPath, 0700); err != nil { + return err + } + } + return nil +} + +func GetOriginalNameId(IID2NameId string) string { + var originalNameId string + + if len(IID2NameId) <= 9 { // For local test + originalNameId = IID2NameId + } else { // For CB-Spider IID2 NameId + reversedNameId := Reverse(IID2NameId) + originalNameId = reversedNameId[:21] + originalNameId = strings.TrimSuffix(IID2NameId, Reverse(originalNameId)) + } + cblogger.Infof("# originalNameId : %s", originalNameId) + return originalNameId +} + +func Reverse(s string) (result string) { + for _,v := range s { + result = string(v) + result + } + return +} + +func RunCommand(cmdName string, cmdArgs []string) (string, error) { + + /* + Ref) + var ( + cmdOut []byte + cmdErr error + ) + */ + + cblogger.Infof("cmdName : %s", cmdName) + cblogger.Infof("cmdArgs : %s", cmdArgs) + + //if cmdOut, cmdErr = exec.Command(cmdName, cmdArgs...).Output(); cmdErr != nil { + if cmdOut, cmdErr := exec.Command(cmdName, cmdArgs...).CombinedOutput(); cmdErr != nil { + fmt.Fprintln(os.Stderr, "There was an Error running command : ", cmdErr) + //panic("Can't exec the command: " + cmdErr1.Error()) + fmt.Println(fmt.Sprint(cmdErr) + ": " + string(cmdOut)) + os.Exit(1) + + return string(cmdOut), cmdErr + } else { + fmt.Println("cmdOut : ", string(cmdOut)) + + return string(cmdOut), nil + } +} + +// Convert Cloud Object to JSON String type +func ConvertJsonString(v interface{}) (string, error) { + jsonBytes, err := json.Marshal(v) + if err != nil { + newErr := fmt.Errorf("Failed to Convert Json to String. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return "", newErr + } + jsonString := string(jsonBytes) + return jsonString, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/DiskHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/DiskHandler.go new file mode 100644 index 000000000..19880058e --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/DiskHandler.go @@ -0,0 +1,629 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, 2022.08. + +package resources + +import ( + "fmt" + // "io/ioutil" + // "os" + "strconv" + "strings" + "time" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/blockstorage/v2/volumes" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/blockstorage/extensions/volumeactions" // For Attachment + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/servers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/volumeattach" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +const ( + HDD string = "General HDD" + SSD string = "General SSD" +) + +type NhnCloudDiskHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + VolumeClient *nhnsdk.ServiceClient +} + +func (diskHandler *NhnCloudDiskHandler) CreateDisk(diskReqInfo irs.DiskInfo) (irs.DiskInfo, error) { + cblogger.Info("NHN Cloud Driver: called CreateDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, diskReqInfo.IId.NameId, "CreateDisk()") + + if strings.EqualFold(diskReqInfo.IId.NameId, "") { + newErr := fmt.Errorf("Invalid Disk NameId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + if strings.EqualFold(diskHandler.RegionInfo.Zone, "") { + newErr := fmt.Errorf("Invalid Zone Info!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + reqDiskType := diskReqInfo.DiskType // 'default', 'General_HDD' or 'General_SSD' + reqDiskSize := diskReqInfo.DiskSize // 10~2000(GB) + + if strings.EqualFold(reqDiskType, "") || strings.EqualFold(reqDiskType, "default") { + reqDiskType = HDD // In case, Volume Type is not specified. + } else if strings.EqualFold(reqDiskType, "General_HDD") { + reqDiskType = HDD // "General HDD" + } else if strings.EqualFold(reqDiskType, "General_SSD") { + reqDiskType = SSD // "General SSD" + } else { + newErr := fmt.Errorf("Invalid Disk Type!!") + cblogger.Error(newErr.Error()) + } + + if strings.EqualFold(reqDiskSize, "") || strings.EqualFold(reqDiskSize, "default") { + reqDiskSize = DefaultDiskSize // In case, Volume Size is not specified. + } + + reqDiskSizeInt, err := strconv.Atoi(reqDiskSize) + if err != nil { + newErr := fmt.Errorf("Failed to Convert Disk Size to Int. type. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + if reqDiskSizeInt < 10 || reqDiskSizeInt > 2000 { // 10~2000(GB) + newErr := fmt.Errorf("Invalid Disk Size. Disk Size Must be between 10 and 2000.") + cblogger.Error(newErr.Error()) + return irs.DiskInfo{}, newErr + } + + start := call.Start() + create0pts := volumes.CreateOpts{ + Size: reqDiskSizeInt, + AvailabilityZone: diskHandler.RegionInfo.Zone, + Name: diskReqInfo.IId.NameId, + VolumeType: reqDiskType, + } + // cblogger.Info("\n### Disk create 0pts : ") + // spew.Dump(create0pts) + // cblogger.Info("\n") + + diskResult, err := volumes.Create(diskHandler.VolumeClient, create0pts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create New Disk Volume. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + // Because there are functions that use 'NameId', Input NameId too + newDiskIID := irs.IID{NameId: diskResult.Name, SystemId: diskResult.ID} + + // Wait for created VM info to be inquired + curStatus, err := diskHandler.WaitForDiskCreation(newDiskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Wait to Get Disk Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + cblogger.Infof("==> Disk Status of [%s] : [%s]", newDiskIID.NameId, curStatus) + + // Check VM Deploy Status + diskResult, err = volumes.Get(diskHandler.VolumeClient, diskResult.ID).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Disk Info!! : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + newDiskInfo, err := diskHandler.MappingDiskInfo(*diskResult) + if err != nil { + newErr := fmt.Errorf("Failed to Map Disk Info. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + return newDiskInfo, nil +} + +func (diskHandler *NhnCloudDiskHandler) ListDisk() ([]*irs.DiskInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, "ListDisk()", "ListDisk()") + + start := call.Start() + listOpts := volumes.ListOpts{} + allPages, err := volumes.List(diskHandler.VolumeClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Volume list!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + nhnVolumeList, err := volumes.ExtractVolumes(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Extract NHN Cloud Volume list!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, start) + // spew.Dump(nhnVolumeList) + + var volumeInfoList []*irs.DiskInfo + for _, volume := range nhnVolumeList { + volumeInfo, err := diskHandler.MappingDiskInfo(volume) + if err != nil { + newErr := fmt.Errorf("Failed to Get Disk Info list!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + volumeInfoList = append(volumeInfoList, &volumeInfo) + } + return volumeInfoList, nil +} + +func (diskHandler *NhnCloudDiskHandler) GetDisk(diskIID irs.IID) (irs.DiskInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, diskIID.SystemId, "GetDisk()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + nhnVolumeInfo, err := volumes.Get(diskHandler.VolumeClient, diskIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Disk Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + volumeInfo, err := diskHandler.MappingDiskInfo(*nhnVolumeInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Get Disk Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + return volumeInfo, nil +} + +func (diskHandler *NhnCloudDiskHandler) ChangeDiskSize(diskIID irs.IID, newDiskSize string) (bool, error) { + cblogger.Info("NHN Cloud Driver: called ChangeDiskSize()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, diskIID.SystemId, "ChangeDiskSize()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + return false, newErr + } + + newDiskSizeInt, err := strconv.Atoi(newDiskSize) + if err != nil { + newErr := fmt.Errorf("Failed to Convert New Disk Size to Int. type. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + diskInfo, err := diskHandler.GetDisk(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Disk Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + curDiskSizeInt, err := strconv.Atoi(diskInfo.DiskSize) + if err != nil { + newErr := fmt.Errorf("Failed to Convert Current Disk Size to Int. type. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + if newDiskSizeInt < 10 || newDiskSizeInt > 2000 { // 10~2000(GB) + newErr := fmt.Errorf("Invalid Disk Size. Disk Size Must be between 10 and 2000.") + cblogger.Error(newErr.Error()) + return false, newErr + } else if newDiskSizeInt <= curDiskSizeInt { + newErr := fmt.Errorf("Invalid Disk Size. New Disk Size must be Greater than Current Disk Size.") + cblogger.Error(newErr.Error()) + return false, newErr + } + + if diskInfo.Status != "Available" { + newErr := fmt.Errorf("Disk Resizing is possible only when the Disk status is in 'Available'.") + cblogger.Error(newErr.Error()) + return false, newErr + } + + start := call.Start() + extendOpts := volumeactions.ExtendSizeOpts{ + NewSize: newDiskSizeInt, + } + extendErr := volumeactions.ExtendSize(diskHandler.VolumeClient, diskIID.SystemId, extendOpts).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Change the Disk Size!! : [%v] ", extendErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + + return true, nil +} + +func (diskHandler *NhnCloudDiskHandler) DeleteDisk(diskIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, "DeleteDisk()", "DeleteDisk()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + curStatus, err := diskHandler.GetDiskStatus(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Disk Status : [%v] ", err) + cblogger.Error(newErr.Error()) + return false, newErr + } else if strings.EqualFold(string(curStatus), string(irs.DiskAttached)) { + newErr := fmt.Errorf("Failed to Delete the Disk Volume. The Disk Status is 'Attached'.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + start := call.Start() + delOpts := volumes.DeleteOpts { + Cascade : true, // Delete all snapshots of this volume as well. + } + delErr := volumes.Delete(diskHandler.VolumeClient, diskIID.SystemId, delOpts).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Delete the Disk Volume!! : [%v] ", delErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + + return true, nil +} + +func (diskHandler *NhnCloudDiskHandler) AttachDisk(diskIID irs.IID, vmIID irs.IID) (irs.DiskInfo, error) { + cblogger.Info("NHN Cloud Driver: called AttachDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, diskIID.SystemId, "AttachDisk()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } else if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + curStatus, err := diskHandler.GetDiskStatus(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Disk Status : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.DiskInfo{}, newErr + } else if strings.EqualFold(string(curStatus), string(irs.DiskAttached)) { + newErr := fmt.Errorf("Failed to Attach the Disk Volume. The Disk is already 'Attached'.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + + start := call.Start() + createOpts := volumeattach.CreateOpts{ + VolumeID: diskIID.SystemId, + } + _, createErr := volumeattach.Create(diskHandler.VMClient, vmIID.SystemId, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Attach the Disk Volume!! : [%v] ", createErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + // Wait for Disk Attachment finished + curStatus, waitErr := diskHandler.WaitForDiskAttachment(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Wait to Get Disk Info. [%v]", waitErr.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + cblogger.Infof("==> Disk Status : [%s]", string(curStatus)) + + diskInfo, err := diskHandler.GetDisk(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Disk Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.DiskInfo{}, newErr + } + return diskInfo, nil +} + +func (diskHandler *NhnCloudDiskHandler) DetachDisk(diskIID irs.IID, vmIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DetachDisk()") + callLogInfo := GetCallLogScheme(diskHandler.RegionInfo.Region, call.DISK, diskIID.SystemId, "DetachDisk()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } else if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + curStatus, err := diskHandler.GetDiskStatus(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Disk Status : [%v] ", err) + cblogger.Error(newErr.Error()) + return false, newErr + } else if !strings.EqualFold(string(curStatus), string(irs.DiskAttached)) { + newErr := fmt.Errorf("Failed to Detach the Disk Volume. The Disk Status is Not 'Attached'.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + isBootable, err := diskHandler.IsBootableDisk(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Bootable Disk Info. : [%v] ", err) + cblogger.Error(newErr.Error()) + return false, newErr + } else if isBootable { + newErr := fmt.Errorf("Failed to Detach the Disk Volume. The Disk is 'Bootable Disk and Attached'.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + start := call.Start() + delErr := volumeattach.Delete(diskHandler.VMClient, vmIID.SystemId, diskIID.SystemId).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Detach the Disk Volume!! : [%v] ", delErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + + return true, nil +} + +// Waiting for up to 500 seconds during Disk creation until Disk info. can be get +func (diskHandler *NhnCloudDiskHandler) WaitForDiskCreation(diskIID irs.IID) (irs.DiskStatus, error) { + cblogger.Info("===> Since Disk info. cannot be retrieved immediately after Disk creation, it waits until running.") + + curRetryCnt := 0 + maxRetryCnt := 500 + for { + curStatus, err := diskHandler.GetDiskStatus(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Disk Status of [%s] : [%v] ", diskIID.NameId, err) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } else { + cblogger.Infof("Succeeded in Getting the Disk Status of [%s] : [%s]", diskIID.NameId, string(curStatus)) + } + + cblogger.Infof("===> Disk Status : [%s]", string(curStatus)) + + switch string(curStatus) { + case "Creating": + curRetryCnt++ + cblogger.Infof("The Disk is still 'Creating', so wait for a second more before inquiring the Disk info.") + time.Sleep(time.Second * 2) + if curRetryCnt > maxRetryCnt { + newErr := fmt.Errorf("Despite waiting for a long time(%d sec), the Disk status is %s, so it is forcibly finished.", maxRetryCnt, string(curStatus)) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } + default: + cblogger.Infof("===> ### The Disk 'Creation' is finished, stopping the waiting.") + return curStatus, nil + //break + } + } +} + +// Waiting for up to 500 seconds during Disk Attachment +func (diskHandler *NhnCloudDiskHandler) WaitForDiskAttachment(diskIID irs.IID) (irs.DiskStatus, error) { + curRetryCnt := 0 + maxRetryCnt := 500 + for { + curStatus, err := diskHandler.GetDiskStatus(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Disk Status of [%s] : [%v] ", diskIID.NameId, err) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } else { + cblogger.Infof("Succeeded in Getting the Disk Status of [%s] : [%s]", diskIID.NameId, curStatus) + } + + cblogger.Infof("===> Disk Status : [%s]", string(curStatus)) + + switch string(curStatus) { + case string(irs.DiskCreating), string(irs.DiskAvailable), string(irs.DiskDeleting), string(irs.DiskError), "Unknown" : + curRetryCnt++ + cblogger.Infof("The Disk is still [%s], so wait for a second more during the Disk 'Attachment'.", string(curStatus)) + time.Sleep(time.Second * 2) + if curRetryCnt > maxRetryCnt { + newErr := fmt.Errorf("Despite waiting for a long time(%d sec), the Disk status is '%s', so it is forcibly finished.", maxRetryCnt, string(curStatus)) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } + default: + cblogger.Infof("===> ### The Disk 'Attachment' is finished, stopping the waiting.") + return curStatus, nil + //break + } + } +} + +func (diskHandler *NhnCloudDiskHandler) GetDiskStatus(diskIID irs.IID) (irs.DiskStatus, error) { + cblogger.Info("NHN Cloud Driver: called GetDiskStatus()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + return irs.DiskError, newErr + } + + diskResult, err := volumes.Get(diskHandler.VolumeClient, diskIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Disk Info!! : [%v]", err) + cblogger.Error(newErr.Error()) + return irs.DiskError, newErr + } + + cblogger.Infof("# diskResult.Status of NHN Cloud : [%s]", diskResult.Status) + diskStatus := ConvertDiskStatus(diskResult.Status) + + return diskStatus, nil +} + +func ConvertDiskStatus(diskStatus string) irs.DiskStatus { + cblogger.Info("NHN Cloud Driver: called ConvertDiskStatus()") + + var resultStatus irs.DiskStatus + switch strings.ToLower(diskStatus) { + case "creating": + resultStatus = irs.DiskCreating + case "available": + resultStatus = irs.DiskAvailable + case "in-use": + resultStatus = irs.DiskAttached + case "deleting": + resultStatus = irs.DiskDeleting + case "error": + resultStatus = irs.DiskError + case "error_deleting": + resultStatus = irs.DiskError + case "error_backing-up": + resultStatus = irs.DiskError + case "error_restoring": + resultStatus = irs.DiskError + case "error_extending": + resultStatus = irs.DiskError + default: + resultStatus = "Unknown" + } + + return resultStatus +} + +func (diskHandler *NhnCloudDiskHandler) IsBootableDisk(diskIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called IsBootableDisk()") + + if strings.EqualFold(diskIID.SystemId, "") { + newErr := fmt.Errorf("Invalid Disk SystemId!!") + cblogger.Error(newErr.Error()) + return false, newErr + } + + diskResult, err := volumes.Get(diskHandler.VolumeClient, diskIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Disk Info!! : [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } + + isBootable, err := strconv.ParseBool(diskResult.Bootable) + if err != nil { + newErr := fmt.Errorf("Failed to Parse the String value!! : [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } + + return isBootable, nil +} + +func (diskHandler *NhnCloudDiskHandler) MappingDiskInfo(volume volumes.Volume) (irs.DiskInfo, error) { + cblogger.Info("NHN Cloud Driver: called MappingDiskInfo()") + // cblogger.Info("\n\n### volume : ") + // spew.Dump(volume) + // cblogger.Info("\n") + + diskInfo := irs.DiskInfo{ + IId: irs.IID{ + SystemId: volume.ID, + }, + DiskSize: strconv.Itoa(volume.Size), + Status: ConvertDiskStatus(volume.Status), + CreatedTime: volume.CreatedAt, + } + + if strings.EqualFold(volume.Name, "") { // Bootable disk of Not 'u2' VMSpec + diskInfo.IId.NameId = "Auto_Created_Booting_Disk" + } else { + diskInfo.IId.NameId = volume.Name + } + + if strings.EqualFold(volume.VolumeType, HDD) { // "General HDD" + diskInfo.DiskType = "General_HDD" + } else if strings.EqualFold(volume.VolumeType, SSD) { // "General SSD" + diskInfo.DiskType = "General_SSD" + } + + if volume.Attachments != nil && len(volume.Attachments) > 0 { + vmId := volume.Attachments[0].ServerID + nhnVm, err := servers.Get(diskHandler.VMClient, vmId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get Volume Info list!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.DiskInfo{}, newErr + } else { + diskInfo.OwnerVM = irs.IID{ + NameId: nhnVm.Name, + SystemId: nhnVm.ID, + } + } + } + + keyValueList := []irs.KeyValue{ + {Key: "AvailabilityZone", Value: volume.AvailabilityZone}, + {Key: "IsBootable", Value: volume.Bootable}, + {Key: "IsMultiattached", Value: strconv.FormatBool(volume.Multiattach)}, + {Key: "IsEncrypted", Value: strconv.FormatBool(volume.Encrypted)}, + } + diskInfo.KeyValueList = keyValueList + + return diskInfo, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ImageHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ImageHandler.go new file mode 100644 index 000000000..db7f83154 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/ImageHandler.go @@ -0,0 +1,151 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI 2022.08. + +package resources + +import ( + // "errors" + "strings" + "fmt" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + images "github.com/cloud-barista/nhncloud-sdk-go/openstack/imageservice/v2/images" // imageservice/v2/images : For Visibility parameter + // comimages "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/images" // compute/v2/images + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudImageHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + ImageClient *nhnsdk.ServiceClient +} + +func (imageHandler *NhnCloudImageHandler) ListImage() ([]*irs.ImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListImage()") + callLogInfo := GetCallLogScheme(imageHandler.RegionInfo.Region, call.VMIMAGE, "ListImage()", "ListImage()") + + start := call.Start() + listOpts := images.ListOpts{ + Visibility: images.ImageVisibilityPublic, // Note : Public image only + } + allPages, err := images.List(imageHandler.ImageClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Image pages. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + nhnImageList, err := images.ExtractImages(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Image List. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, start) + + // cblogger.Info("\n\n### nhnImageList : ") + // spew.Dump(nhnImageList) + // cblogger.Info("# 출력 결과 수 : ", len(nhnImageList)) + + var imageInfoList []*irs.ImageInfo + for _, nhnImage := range nhnImageList { + imageInfo := imageHandler.MappingImageInfo(nhnImage) + imageInfoList = append(imageInfoList, imageInfo) + } + return imageInfoList, nil +} + +func (imageHandler *NhnCloudImageHandler) GetImage(imageIID irs.IID) (irs.ImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetImage()") + callLogInfo := GetCallLogScheme(imageHandler.RegionInfo.Region, call.VMIMAGE, imageIID.SystemId, "GetImage()") + + if strings.EqualFold(imageIID.SystemId, "") { + newErr := fmt.Errorf("Invalid SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.ImageInfo{}, newErr + } + + start := call.Start() + // nhnImage, err := comimages.Get(imageHandler.VMClient, imageIID.SystemId).Extract() // VM Client + nhnImage, err := images.Get(imageHandler.ImageClient, imageIID.SystemId).Extract() // Image Client + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Image Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.ImageInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + imageInfo := imageHandler.MappingImageInfo(*nhnImage) + return *imageInfo, nil +} + +func (imageHandler *NhnCloudImageHandler) CreateImage(imageReqInfo irs.ImageReqInfo) (irs.ImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called CreateImage()!") + + return irs.ImageInfo{}, fmt.Errorf("Does not support CreateImage() yet!!") +} + +func (imageHandler *NhnCloudImageHandler) CheckWindowsImage(imageIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called CheckWindowsImage()") + + return false, fmt.Errorf("Does not support CheckWindowsImage() yet!!") +} + +func (imageHandler *NhnCloudImageHandler) DeleteImage(imageIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteImage()!") + + return true, fmt.Errorf("Does not support DeleteImage() yet!!") +} + +func (imageHandler *NhnCloudImageHandler) MappingImageInfo(image images.Image) *irs.ImageInfo { + cblogger.Info("NHN Cloud Driver: called MappingImagInfo()!") + + var imgAvailability string + if strings.EqualFold(string(image.Status), "active") { + imgAvailability = "available" + } else { + imgAvailability = "unavailable" + } + + imageInfo := &irs.ImageInfo { + IId: irs.IID{ + NameId: image.ID, // Caution!! + SystemId: image.ID, + }, + GuestOS: image.Name, // Caution!! + Status: imgAvailability, + } + + keyValueList := []irs.KeyValue{ + {Key: "Region", Value: imageHandler.RegionInfo.Region}, + {Key: "Visibility:", Value: string(image.Visibility)}, + } + + for key, val := range image.Properties { + if (key == "os_architecture" || key == "hypervisor_type" || key == "release_date" || key == "description" || key == "os_distro" || key == "os_version" || key == "nhncloud_product"){ + metadata := irs.KeyValue{ + Key: strings.ToUpper(key), + Value: fmt.Sprintf("%v", val), + } + keyValueList = append(keyValueList, metadata) + } + } + + imageInfo.KeyValueList = keyValueList + return imageInfo +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/KeyPairHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/KeyPairHandler.go new file mode 100644 index 000000000..03d01d68b --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/KeyPairHandler.go @@ -0,0 +1,229 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI, 2022.04. + +package resources + +import ( + "fmt" + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/keypairs" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudKeyPairHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient +} + +func (keyPairHandler *NhnCloudKeyPairHandler) CreateKey(keyPairReqInfo irs.KeyPairReqInfo) (irs.KeyPairInfo, error) { + cblogger.Info("NHN Cloud Driver: called CreateKey()") + callLogInfo := GetCallLogScheme(keyPairHandler.RegionInfo.Region, call.VMKEYPAIR, keyPairReqInfo.IId.NameId, "CreateKey()") + + if keyPairReqInfo.IId.NameId == "" { + newErr := fmt.Errorf("Invalid KeyPair NameId.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + + exist, err := CheckExistKey(keyPairHandler.VMClient, keyPairReqInfo.IId) + if err != nil { + newErr := fmt.Errorf("Failed to Create Key. err = %s", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + if exist { + keyName := keyPairReqInfo.IId.SystemId + if keyPairReqInfo.IId.SystemId == "" { + keyName = keyPairReqInfo.IId.NameId + } + newErr := fmt.Errorf("Failed to Create Key. err = The Key name %s already exists", keyName) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + + start := call.Start() + create0pts := keypairs.CreateOpts{ + Name: keyPairReqInfo.IId.NameId, + PublicKey: "", + } + keyPair, err := keypairs.Create(keyPairHandler.VMClient, create0pts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create Key. err = %s", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + keyPairInfo := MappingKeypairInfo(*keyPair) + return *keyPairInfo, nil +} + +func (keyPairHandler *NhnCloudKeyPairHandler) ListKey() ([]*irs.KeyPairInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListKey()") + callLogInfo := GetCallLogScheme(keyPairHandler.RegionInfo.Region, call.VMKEYPAIR, "ListKey()", "ListKey()") + + start := call.Start() + var listOptsBuilder keypairs.ListOptsBuilder + allPages, err := keypairs.List(keyPairHandler.VMClient, listOptsBuilder).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get KeyList err = %s", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, start) + + keypair, err := keypairs.ExtractKeyPairs(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get KeyList err = %s", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + keyPairList := make([]*irs.KeyPairInfo, len(keypair)) + for i, k := range keypair { + keyPairList[i] = MappingKeypairInfo(k) + } + return keyPairList, nil +} + +func (keyPairHandler *NhnCloudKeyPairHandler) GetKey(keyIID irs.IID) (irs.KeyPairInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetKey()") + callLogInfo := GetCallLogScheme(keyPairHandler.RegionInfo.Region, call.VMKEYPAIR, keyIID.NameId, "GetKey()") + + if iidCheck := CheckIIDValidation(keyIID); !iidCheck { + newErr := fmt.Errorf("Failed to Get Key. InValid IID") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + + start := call.Start() + keyPair, err := GetRawKey(keyPairHandler.VMClient, keyIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Key. %s", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.KeyPairInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + keyPairInfo := MappingKeypairInfo(keyPair) + return *keyPairInfo, nil +} + +func (keyPairHandler *NhnCloudKeyPairHandler) DeleteKey(keyIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteKey()") + callLogInfo := GetCallLogScheme(keyPairHandler.RegionInfo.Region, call.VMKEYPAIR, keyIID.NameId, "DeleteKey()") + + exist, err := CheckExistKey(keyPairHandler.VMClient, keyIID) + if err != nil { + delErr := fmt.Errorf("Failed to Delete Key. %s", err) + cblogger.Error(delErr.Error()) + LoggingError(callLogInfo, delErr) + return false, delErr + } + + if !exist { + keyName := keyIID.SystemId + + if keyIID.SystemId == "" { + keyName = keyIID.NameId + } + delErr := fmt.Errorf("Failed to Delete Key. The Key name %s not found", keyName) + cblogger.Error(delErr.Error()) + LoggingError(callLogInfo, delErr) + return false, delErr + } + + start := call.Start() + var delOptsBuilder keypairs.DeleteOptsBuilder + err = keypairs.Delete(keyPairHandler.VMClient, keyIID.NameId, delOptsBuilder).ExtractErr() + if err != nil { + delErr := fmt.Errorf("Failed to Delete Key : %v", err.Error()) + cblogger.Error(delErr.Error()) + LoggingError(callLogInfo, delErr) + return false, delErr + } + LoggingInfo(callLogInfo, start) + + return true, nil +} + +func CheckExistKey(client *nhnsdk.ServiceClient, keyIID irs.IID) (bool, error) { + if ok := CheckIIDValidation(keyIID); !ok { + return false, fmt.Errorf("Invalid KeyPair IID!!") + } + + keyName := keyIID.SystemId + if keyIID.SystemId == "" { + keyName = keyIID.NameId + } + + var listOptsBuilder keypairs.ListOptsBuilder + + allPages, err := keypairs.List(client, listOptsBuilder).AllPages() + if err != nil { + return false, err + } + + keypairList, err := keypairs.ExtractKeyPairs(allPages) + if err != nil { + return false, err + } + + for _, keypair := range keypairList { + if keypair.Name == keyName { + return true, nil + } + } + + return false, nil +} + +func GetRawKey(client *nhnsdk.ServiceClient, keyIID irs.IID) (keypairs.KeyPair, error) { + keyName := keyIID.SystemId + + if keyIID.SystemId == "" { + keyName = keyIID.NameId + } + + var getOptsBuilder keypairs.GetOptsBuilder + keyPair, err := keypairs.Get(client, keyName, getOptsBuilder).Extract() + if err != nil { + return keypairs.KeyPair{}, err + } + + return *keyPair, nil +} + +func MappingKeypairInfo(keypair keypairs.KeyPair) *irs.KeyPairInfo { + keypairInfo := &irs.KeyPairInfo{ + IId: irs.IID{ + NameId: keypair.Name, + SystemId: keypair.Name, + }, + Fingerprint: keypair.Fingerprint, + PublicKey: keypair.PublicKey, + PrivateKey: keypair.PrivateKey, + VMUserID: DefaultVMUserName, + } + + return keypairInfo +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/MyImageHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/MyImageHandler.go new file mode 100644 index 000000000..4676b05dc --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/MyImageHandler.go @@ -0,0 +1,434 @@ +package resources + +import ( + "fmt" + "strings" + "strconv" + "time" + // "google.golang.org/grpc/metadata" + "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/servers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/flavors" + images "github.com/cloud-barista/nhncloud-sdk-go/openstack/imageservice/v2/images" // imageservice/v2/images : For Visibility parameter + // comimages "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/images" // compute/v2/images + "github.com/cloud-barista/nhncloud-sdk-go/openstack/blockstorage/extensions/volumeactions" + // "github.com/cloud-barista/nhncloud-sdk-go/openstack/blockstorage/v2/snapshots" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudMyImageHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + ImageClient *nhnsdk.ServiceClient + NetworkClient *nhnsdk.ServiceClient + VolumeClient *nhnsdk.ServiceClient +} + +// To Take a Snapshot with VM ID (To Create My Image) +func (myImageHandler *NhnCloudMyImageHandler) SnapshotVM(snapshotReqInfo irs.MyImageInfo) (irs.MyImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called SnapshotVM()") + callLogInfo := GetCallLogScheme(myImageHandler.RegionInfo.Region, call.MYIMAGE, snapshotReqInfo.SourceVM.SystemId, "SnapshotVM()") + + if strings.EqualFold(snapshotReqInfo.SourceVM.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + + var snapshotName string + if !strings.EqualFold(snapshotReqInfo.IId.NameId, "") { + snapshotName = snapshotReqInfo.IId.NameId + } + + nhnVMSpecType, err := myImageHandler.GetVMSpecType(irs.IID{SystemId: snapshotReqInfo.SourceVM.SystemId}) + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM Spec Type. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + + cblogger.Info("\n\n### nhnVMSpecType : ") + spew.Dump(nhnVMSpecType) + cblogger.Info("\n") + + // snapShotMap := make(map[string]string) + // snapShotMap["vmID"] = snapshotReqInfo.SourceVM.SystemId + + vmSpecType := nhnVMSpecType[:2] // Ex) vmSpecType : 'u2', 'm2' or 'c2' ... + cblogger.Infof("# vmSpecType : [%s]", vmSpecType) + + var newImageIID irs.IID + + if strings.EqualFold(vmSpecType, "u2") { + start := call.Start() + snapshotOpts := servers.CreateImageOpts{ + Name: snapshotName, + // Metadata: snapShotMap, + } + snapShotImageId, err := servers.CreateImage(myImageHandler.VMClient, snapshotReqInfo.SourceVM.SystemId, snapshotOpts).ExtractImageID() // Not images.CreateImage() + if err != nil { + newErr := fmt.Errorf("Failed to Create Snapshot of the VM. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + cblogger.Infof("\n\n# snapShotImageId : [%s]\n", snapShotImageId) + + newImageIID = irs.IID{SystemId: snapShotImageId} + } else { + bootableVolumeId, err := myImageHandler.GetBootableVolumeID(snapshotReqInfo.SourceVM) + if err != nil { + newErr := fmt.Errorf("Failed to Get Bootable VolumeID of the VM. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + + start := call.Start() + uploadImageOpts := volumeactions.UploadImageOpts { + ImageName: snapshotName, + Force: true, + } + volumeImage, err := volumeactions.UploadImage(myImageHandler.VolumeClient, bootableVolumeId, uploadImageOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create Image from the Volume!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + cblogger.Infof("\n\n# snapShotImageId : [%s]\n", volumeImage.ImageID) + + newImageIID = irs.IID{SystemId: volumeImage.ImageID} + + cblogger.Info("\n\n### volumeImage : ") + spew.Dump(volumeImage) + cblogger.Info("\n") + } + + // To Wait for Creating a Snapshot Image + curStatus, err := myImageHandler.WaitForImageSnapshot(newImageIID) + if err != nil { + newErr := fmt.Errorf("Failed to Wait to Get Image Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + cblogger.Infof("==> Image Status of [%s] : [%s]", newImageIID.SystemId, string(curStatus)) + + myImageInfo, err := myImageHandler.GetMyImage(newImageIID) + if err != nil { + newErr := fmt.Errorf("Failed to Wait to Get Image Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + return myImageInfo, nil +} + +// To Manage My Images +func (myImageHandler *NhnCloudMyImageHandler) ListMyImage() ([]*irs.MyImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListMyImage()") + callLogInfo := GetCallLogScheme(myImageHandler.RegionInfo.Region, call.MYIMAGE, "ListMyImage()", "ListMyImage()") + + start := call.Start() + listOpts := images.ListOpts{ + Visibility: images.ImageVisibilityPrivate, // Note : Private image only + } + allPages, err := images.List(myImageHandler.ImageClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Image pages. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + nhnImageList, err := images.ExtractImages(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Image List. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, start) + + // cblogger.Info("\n\n### nhnImageList : ") + // spew.Dump(nhnImageList) + // cblogger.Info("# 출력 결과 수 : ", len(nhnImageList)) + + var imageInfoList []*irs.MyImageInfo + for _, nhnImage := range nhnImageList { + imageInfo := myImageHandler.MappingMyImageInfo(nhnImage) + imageInfoList = append(imageInfoList, imageInfo) + } + return imageInfoList, nil +} + +func (myImageHandler *NhnCloudMyImageHandler) GetMyImage(myImageIID irs.IID) (irs.MyImageInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetMyImage()") + callLogInfo := GetCallLogScheme(myImageHandler.RegionInfo.Region, call.MYIMAGE, myImageIID.SystemId, "GetMyImage()") + + if strings.EqualFold(myImageIID.SystemId, "") { + newErr := fmt.Errorf("Invalid SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + + start := call.Start() + // nhnImage, err := comimages.Get(myImageHandler.VMClient, myImageIID.SystemId).Extract() // VM Client + nhnImage, err := images.Get(myImageHandler.ImageClient, myImageIID.SystemId).Extract() // Image Client + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud My Image Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.MyImageInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + imageInfo := myImageHandler.MappingMyImageInfo(*nhnImage) + return *imageInfo, nil +} + +func (myImageHandler *NhnCloudMyImageHandler) CheckWindowsImage(myImageIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called CheckWindowsImage()") + + return false, fmt.Errorf("NHN Cloud Driver Does not support CheckWindowsImage() yet!!") +} + +func (myImageHandler *NhnCloudMyImageHandler) DeleteMyImage(myImageIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteMyImage()") + callLogInfo := GetCallLogScheme(myImageHandler.RegionInfo.Region, call.MYIMAGE, myImageIID.SystemId, "DeleteMyImage()") + + if strings.EqualFold(myImageIID.SystemId, "") { + newErr := fmt.Errorf("Invalid SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + err := images.Delete(myImageHandler.ImageClient, myImageIID.SystemId).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Delete the Image. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + return true, nil +} + +func (myImageHandler *NhnCloudMyImageHandler) GetImageStatus(myImageIID irs.IID) (irs.MyImageStatus, error) { + cblogger.Info("NHN Cloud Driver: called GetImageStatus()") + callLogInfo := GetCallLogScheme(myImageHandler.RegionInfo.Region, call.MYIMAGE, myImageIID.SystemId, "GetImageStatus()") + + if strings.EqualFold(myImageIID.SystemId, "") { + newErr := fmt.Errorf("Invalid SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + start := call.Start() + // nhnImage, err := comimages.Get(myImageHandler.VMClient, myImageIID.SystemId).Extract() // VM Client + nhnImage, err := images.Get(myImageHandler.ImageClient, myImageIID.SystemId).Extract() // Image Client + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud My Image Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, start) + + myImageStatus := ConvertImageStatus(nhnImage.Status) + return myImageStatus, nil +} + +// Waiting for up to 500 seconds during Taking a Snapshot from a VM +func (myImageHandler *NhnCloudMyImageHandler) WaitForImageSnapshot(myImageIID irs.IID) (irs.MyImageStatus, error) { + cblogger.Info("===> Since Snapshot info. cannot be retrieved immediately after taking a snapshot, waits ....") + + if strings.EqualFold(myImageIID.SystemId, "") { + newErr := fmt.Errorf("Invalid SystemId!!") + cblogger.Error(newErr.Error()) + return "", newErr + } + + curRetryCnt := 0 + maxRetryCnt := 500 + for { + curStatus, err := myImageHandler.GetImageStatus(myImageIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Image Status of [%s] : [%v] ", myImageIID.NameId, err) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } else { + cblogger.Infof("Succeeded in Getting the Image Status of [%s] : [%s]", myImageIID.SystemId, string(curStatus)) + } + + cblogger.Infof("===> Image Status : [%s]", string(curStatus)) + + if strings.EqualFold(string(curStatus), "Unavailable") { + curRetryCnt++ + cblogger.Infof("The Image is still 'Unavailable', so wait for a second more before inquiring the Image info.") + time.Sleep(time.Second * 2) + if curRetryCnt > maxRetryCnt { + newErr := fmt.Errorf("Despite waiting for a long time(%d sec), the Image status is %s, so it is forcibly finished.", maxRetryCnt, string(curStatus)) + cblogger.Error(newErr.Error()) + return "Failed. ", newErr + } + } else { + cblogger.Infof("===> ### The Image Snapshot is finished, stopping the waiting.") + return curStatus, nil + //break + } + } +} + +func (myImageHandler *NhnCloudMyImageHandler) MappingMyImageInfo(myImage images.Image) *irs.MyImageInfo { + cblogger.Info("NHN Cloud Driver: called MappingMyImageInfo()!") + + // cblogger.Info("\n\n### myImage in MappingMyImageInfo() : ") + // spew.Dump(myImage) + // cblogger.Info("\n") + + myImageInfo := &irs.MyImageInfo { + IId: irs.IID{ + NameId: myImage.Name, + SystemId: myImage.ID, + }, + Status: ConvertImageStatus(myImage.Status), + CreatedTime: myImage.CreatedAt, + } + + keyValueList := []irs.KeyValue{ + {Key: "Region", Value: myImageHandler.RegionInfo.Region}, + {Key: "Visibility", Value: string(myImage.Visibility)}, + {Key: "DiskSize", Value: strconv.Itoa(myImage.MinDiskGigabytes)}, + } + + // In case the VMSpec type of the SourceVM is 'u2', the map of a snapshot image contains "instance_uuid". + if val, ok := myImage.Properties["instance_uuid"]; ok { + myImageInfo.SourceVM = irs.IID{SystemId: fmt.Sprintf("%v", val)} + } + + for key, val := range myImage.Properties { + if (key == "os_type" || key == "description" || key == "os_architecture" || key == "hypervisor_type" || key == "image_type" || key == "os_distro" || key == "os_version") { + metadata := irs.KeyValue{ + Key: strings.ToUpper(key), + Value: fmt.Sprintf("%v", val), + } + keyValueList = append(keyValueList, metadata) + } + } + + myImageInfo.KeyValueList = keyValueList + return myImageInfo +} + +func ConvertImageStatus(myImageStatus images.ImageStatus) irs.MyImageStatus { + cblogger.Info("NHN Cloud Driver: called ConvertImageStatus()") + + // Ref) https://github.com/cloud-barista/nhncloud-sdk-go/blob/main/openstack/imageservice/v2/images/types.go + var resultStatus irs.MyImageStatus + switch myImageStatus { + case images.ImageStatusQueued: + resultStatus = irs.MyImageUnavailable + case images.ImageStatusSaving: + resultStatus = irs.MyImageUnavailable + case images.ImageStatusActive: + resultStatus = irs.MyImageAvailable + case images.ImageStatusKilled: + resultStatus = irs.MyImageUnavailable + case images.ImageStatusDeleted: + resultStatus = irs.MyImageUnavailable + case images.ImageStatusPendingDelete: + resultStatus = irs.MyImageUnavailable + default: + resultStatus = "Unknown" + } + + return resultStatus +} + +func (myImageHandler *NhnCloudMyImageHandler) GetVMSpecType(vmIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetVMSpecType()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + return "", newErr + } + + nhnVM, err := servers.Get(myImageHandler.VMClient, vmIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM info form NHN Cloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + + var vmSpecType string + flavorId := nhnVM.Flavor["id"].(string) + nhnFlavor, err := flavors.Get(myImageHandler.VMClient, flavorId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the Flavor info form NHN Cloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return "", newErr + } else if nhnFlavor != nil { + vmSpecType = nhnFlavor.Name + } + + return vmSpecType, nil +} + +func (myImageHandler *NhnCloudMyImageHandler) GetBootableVolumeID(vmIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetBootableVolumeID()") + + vmHandler := NhnCloudVMHandler{ + RegionInfo: myImageHandler.RegionInfo, + VMClient: myImageHandler.VMClient, + ImageClient: myImageHandler.ImageClient, + NetworkClient: myImageHandler.NetworkClient, + VolumeClient: myImageHandler.VolumeClient, + } + + diskHandler := NhnCloudDiskHandler{ + RegionInfo: myImageHandler.RegionInfo, + VMClient: myImageHandler.VMClient, + VolumeClient: myImageHandler.VolumeClient, + } + + vmInfo, err := vmHandler.GetVM(vmIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM Info. : [%v] ", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + + var bootableVolumeId string + for _, diskIID := range vmInfo.DataDiskIIDs { + isBootable, err := diskHandler.IsBootableDisk(diskIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Bootable Disk Info. : [%v] ", err) + cblogger.Error(newErr.Error()) + return "", newErr + } else if isBootable { + bootableVolumeId = diskIID.SystemId + } + } + + if strings.EqualFold(bootableVolumeId, "") { + newErr := fmt.Errorf("Failed to Find any Bootable Volume : [%v] ", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + + return bootableVolumeId, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/NLBHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/NLBHandler.go new file mode 100644 index 000000000..8e184e939 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/NLBHandler.go @@ -0,0 +1,1959 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI Team, 2022.07. + +package resources + +import ( + "fmt" + "strings" + "strconv" + "time" + "github.com/davecgh/go-spew/spew" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/loadbalancer/v2/loadbalancers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/networks" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/subnets" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/loadbalancer/v2/listeners" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/external" + + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/servers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/loadbalancer/v2/monitors" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/loadbalancer/v2/pools" + // "github.com/cloud-barista/nhncloud-sdk-go/openstack/loadbalancer/v2/providers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/layer3/floatingips" +) + +type NhnCloudNLBHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + NetworkClient *nhnsdk.ServiceClient +} + +const ( + DefaultVpcName string = "Default Network" + PublicType string = "shared" + InternalType string = "dedicated" + DefaultWeight int = 1 + DefaultAdminStateUp bool = true + + // NHN Cloud default value for Listener and Health Monitor + DefaultConnectionLimit int = 2000 // NHN Cloud Listener ConnectionLimit range : 1 ~ 60000 (Dedicated LB : 1 ~ 480000) + DefaultKeepAliveTimeout int = 300 // NHN Cloud Listener KeepAliveTimeout range : 0 ~ 3600 + DefaultHealthCheckerInterval int = 30 + DefaultHealthCheckerTimeout int = 5 + DefaultHealthCheckerThreshold int = 2 +) + +// The Order to Create NHN NLB : NLB (w/ Subnet ID) -> Listener (w/ NLB ID) -> Pool (w/ Listener ID) -> HealthMonitor (w/ Pool ID) -> VM Members (w/ Pool ID), NLB Public IP (w/ NLB VIP_Port ID) +func (nlbHandler *NhnCloudNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (irs.NLBInfo, error) { + cblogger.Info("NHN Cloud Driver: called CreateNLB()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbReqInfo.IId.NameId, "CreateNLB()") + + if strings.EqualFold(nlbReqInfo.VpcIID.NameId, "") { + newErr := fmt.Errorf("Invalid VPC NameId required") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + + subnetId, err := nlbHandler.GetFirstSubnetIdWithVPCName(DefaultVpcName) // Caution!! + if err != nil { + newErr := fmt.Errorf("Failed to Get FirstSubnetId with VPC Name. : [%s]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + + var nlbType string + if strings.EqualFold(nlbReqInfo.Type, "") { + nlbType = PublicType + } else if strings.EqualFold(nlbReqInfo.Type, "PUBLIC") { + nlbType = PublicType + } else if strings.EqualFold(nlbReqInfo.Type, "INTERNAL") { + nlbType = InternalType + } else { + newErr := fmt.Errorf("Invalid NLB Type required!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + + if strings.EqualFold(nlbReqInfo.Scope, "GLOBAL") { + newErr := fmt.Errorf("NHN Cloud NLB does Not support 'GLOBAL' Scope!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + + callLogStart := call.Start() + createOpts := loadbalancers.CreateOpts { + Name: nlbReqInfo.IId.NameId, + Description: "CB-NLB : " + nlbReqInfo.IId.NameId, + VipSubnetID: subnetId, + AdminStateUp: *nhnsdk.Enabled, + LoadBalancerType: nlbType, + } + newNlb, err := loadbalancers.Create(nlbHandler.NetworkClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create NHN Cloud NLB : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + // Wait for provisioning to complete.('provisioningStatus' is "ACTIVE") + _, err = nlbHandler.WaitToGetNLBInfo(irs.IID{SystemId: newNlb.ID}) + if err != nil { + newErr := fmt.Errorf("Failed to Wait for Provisioning to Complete. : [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + // cblogger.Info("\n\n# New NLB on NHN Cloud : ") + // spew.Dump(newNlb) + + newNlbIID := irs.IID{SystemId: newNlb.ID} + + newListener, err := nlbHandler.CreateListener(newNlb.ID, nlbReqInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Create Listener. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + + // Clean up the Created New NLB in case of Creating Failure + _, err := nlbHandler.CleanUpNLB(newNlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Clean up the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } else { + cblogger.Info("\n\n# Succeeded in Deleting the NLB.") + } + + return irs.NLBInfo{}, newErr + } + // cblogger.Info("\n\n# New Listener : ") + // spew.Dump(newListener) + + cblogger.Info("\n\n#### Waiting for Provisioning the New Listener!!") + time.Sleep(25 * time.Second) + + newPool, err := nlbHandler.CreatePool(newListener.ID, nlbReqInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Create Pool. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + + // Clean up the Created New NLB in case of Creating Failure + _, err := nlbHandler.CleanUpNLB(newNlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Clean up the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } else { + cblogger.Info("\n\n# Succeeded in Deleting the NLB.") + } + + return irs.NLBInfo{}, newErr + } + // cblogger.Info("\n\n# New Pool : ") + // spew.Dump(newPool) + + cblogger.Info("\n\n#### Waiting for Provisioning the New Pool!!") + time.Sleep(25 * time.Second) + + newHealthMonitor, err := nlbHandler.CreateHealthMonitor(newPool.ID, nlbReqInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Create HealthMonitor. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + + // Clean up the Created New NLB in case of Creating Failure + _, err := nlbHandler.CleanUpNLB(newNlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Clean up the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } else { + cblogger.Info("\n\n# Succeeded in Deleting the NLB.") + } + + return irs.NLBInfo{}, newErr + } + cblogger.Info("\n\n# New Health Monitor : ") + spew.Dump(newHealthMonitor) + + cblogger.Info("\n\n#### Waiting for Provisioning the New Health Monitor!!") + time.Sleep(25 * time.Second) + + newMembers, err := nlbHandler.CreateVMMembers(newPool.ID, nlbReqInfo.VMGroup) + if err != nil { + newErr := fmt.Errorf("Failed to Create NLB Pool Members. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + + // Clean up the Created New NLB in case of Creating Failure + _, err := nlbHandler.CleanUpNLB(newNlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Clean up the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } else { + cblogger.Info("\n\n# Succeeded in Deleting the NLB.") + } + + return irs.NLBInfo{}, newErr + } + cblogger.Info("\n\n# New Members : ") + spew.Dump(newMembers) + + newFloatingIp, err := nlbHandler.CreatePublicIP(newNlb.VipPortID) + if err != nil { + newErr := fmt.Errorf("Failed to Create PublicIP for the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + if !strings.EqualFold(newFloatingIp, "") { + cblogger.Infof("\n\n# Succeeded in Creating New PublicIP for the NLB. : [%s]", newFloatingIp) + } + + nlbInfo, err := nlbHandler.GetNLB(newNlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Created NLB Info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + return nlbInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) ListNLB() ([]*irs.NLBInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListNLB()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", "ListNLB()", "ListNLB()") + + callLogStart := call.Start() + listOpts := loadbalancers.ListOpts{} + allPages, err := loadbalancers.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN NLB list. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + nlbList, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN NLB list. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + // cblogger.Info("\n\n# nlbList from NHNCLOUD : ") + // spew.Dump(nlbList) + + var nlbInfoList []*irs.NLBInfo + for _, nlb := range nlbList { + nlbInfo, err := nlbHandler.MappingNlbInfo(nlb) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB Info. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + nlbInfoList = append(nlbInfoList, &nlbInfo) + } + return nlbInfoList, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNLB(nlbIID irs.IID) (irs.NLBInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetNLB()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "GetNLB()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } + + callLogStart := call.Start() + listOpts := loadbalancers.ListOpts{ + ID: nlbIID.SystemId, + } + allPages, err := loadbalancers.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN NLB Pages. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + + nlbList, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN NLB list. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var nlbInfo irs.NLBInfo + if len(nlbList) > 0 { + nlbInfo, err = nlbHandler.MappingNlbInfo(nlbList[0]) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB Info from NHN NLB. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + } else { + newErr := fmt.Errorf("Failed to Get Any NLB Info. with the NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.NLBInfo{}, newErr + } + return nlbInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) DeleteNLB(nlbIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteNLB()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.NameId, "DeleteNLB()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + _, err := nlbHandler.DeletePublicIP(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Delete the PublicIP of the NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + callLogStart := call.Start() + delOpts := loadbalancers.DeleteOpts{Cascade: true} // Note : 'Cascade' will delete all children of the LB (Listeners, Monitors, etc). + delErr := loadbalancers.Delete(nlbHandler.NetworkClient, nlbIID.SystemId, delOpts).ExtractErr() + if delErr != nil { + newErr := fmt.Errorf("Failed to Delete the NLB. : [%v]", delErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + return true, nil +} + +func (nlbHandler *NhnCloudNLBHandler) ChangeListener(nlbIID irs.IID, listener irs.ListenerInfo) (irs.ListenerInfo, error) { + cblogger.Info("NHN Cloud Driver: called ChangeListener()") + + return irs.ListenerInfo{}, fmt.Errorf("NHN Cloud does not support ChangeListener() yet!!") +} + +func (nlbHandler *NhnCloudNLBHandler) ChangeVMGroupInfo(nlbIID irs.IID, vmGroup irs.VMGroupInfo) (irs.VMGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called ChangeVMGroupInfo()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "ChangeVMGroupInfo()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + nlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + var oldVMGroupInfo irs.VMGroupInfo + if !strings.EqualFold(nlbInfo.VMGroup.Protocol, "") { + oldVMGroupInfo = nlbInfo.VMGroup + } else { + newErr := fmt.Errorf("VMGroup is not Available in the NLB Info!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + // In case, New VMGroup Protocol and Port number are the Same as the Old one. + if strings.EqualFold(oldVMGroupInfo.Protocol, vmGroup.Protocol) && strings.EqualFold(oldVMGroupInfo.Port, vmGroup.Port) { + return oldVMGroupInfo, nil + } + + if len(*vmGroup.VMs) < 1 { // In case, the 'vmGroup' parameter does not contain VM IID value. + vmGroup.VMs = oldVMGroupInfo.VMs + } + + newMembers, err := nlbHandler.CreateVMMembers(nlbInfo.VMGroup.CspID, vmGroup) + if err != nil { + newErr := fmt.Errorf("Failed to Create NLB Pool Members. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + cblogger.Info("\n\n# New Members : ") + spew.Dump(newMembers) + + newVMGroupNlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + return newVMGroupNlbInfo.VMGroup, nil +} + +func (nlbHandler *NhnCloudNLBHandler) AddVMs(nlbIID irs.IID, vmIIDs *[]irs.IID) (irs.VMGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called AddVMs()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "AddVMs()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + if len(*vmIIDs) < 1 { + newErr := fmt.Errorf("Failded to Find any VM to Add to the VMGroup!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + nlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + var newVMsInfo irs.VMGroupInfo + if !strings.EqualFold(nlbInfo.VMGroup.Protocol, "") { + newVMsInfo.Protocol = nlbInfo.VMGroup.Protocol + } else { + newErr := fmt.Errorf("VMGroup Protocol is not Available in the NLB Info!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + if !strings.EqualFold(nlbInfo.VMGroup.Port, "") { + newVMsInfo.Port = nlbInfo.VMGroup.Port + } else { + newErr := fmt.Errorf("VMGroup Port is not Available in the NLB Info!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + newVMsInfo.VMs = vmIIDs + newMembers, err := nlbHandler.CreateVMMembers(nlbInfo.VMGroup.CspID, newVMsInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Create NLB Pool Members. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + cblogger.Info("\n\n# New VM Members : ") + spew.Dump(newMembers) + + newVMGroupNlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + return newVMGroupNlbInfo.VMGroup, nil +} + +func (nlbHandler *NhnCloudNLBHandler) RemoveVMs(nlbIID irs.IID, vmIIDs *[]irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called RemoveVMs()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", "RemoveVMs()", "RemoveVMs()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + if len(*vmIIDs) < 1 { + newErr := fmt.Errorf("Failed to Find any VM to Remove!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + nlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + // Note : Cloud-Barista supports only this case => [ LB : Listener : Pool : Health Checker = 1 : 1 : 1 : 1 ] + nhnPoolList, err := nlbHandler.GetNhnPoolListWithListenerId(nlbInfo.Listener.CspID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Pool list with the Listener ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + if len(nhnPoolList) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN Pool. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + vmMembers := nlbInfo.VMGroup.VMs + + for _, member := range *vmMembers { + for _, vmToDel := range *vmIIDs { + if strings.EqualFold(member.NameId, vmToDel.NameId) { + cblogger.Infof("\n\n#### Deleting VM [%s] from the VMGroup : ", vmToDel.NameId) + _, err = nlbHandler.DeleteVMMember(nhnPoolList[0].ID, vmToDel) + if err != nil { + newErr := fmt.Errorf("Failed to Delete the VM Member. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + } + } + } + + return true, nil +} + +func (nlbHandler *NhnCloudNLBHandler) CreateListener(nlbId string, nlbReqInfo irs.NLBInfo) (listeners.Listener, error) { + cblogger.Info("NHN Cloud Driver: called CreateListener()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbReqInfo.IId.NameId, "CreateListener()") + + if strings.EqualFold(nlbId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return listeners.Listener{}, newErr + } + + listenerProtocol, err := GetListenerProtocol(nlbReqInfo.Listener.Protocol) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Listener Protocol : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return listeners.Listener{}, newErr + } + + portNum, err := strconv.Atoi(nlbReqInfo.Listener.Port) + if err != nil { + newErr := fmt.Errorf("Invalid Listener Port. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return listeners.Listener{}, newErr + } + if portNum < 1 || portNum > 65535 { + newErr := fmt.Errorf("Invalid Listener Port.(Must be between 1 and 65535)") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return listeners.Listener{}, newErr + } + + callLogStart := call.Start() + createOpts := listeners.CreateOpts { + Protocol: listenerProtocol, + Description: nlbReqInfo.IId.NameId, + Name: nlbReqInfo.IId.NameId, + LoadbalancerID: nlbId, + AdminStateUp: *nhnsdk.Enabled, + ConnLimit: DefaultConnectionLimit, // NHN Cloud Listener ConnectionLimit range : 1 ~ 60000 (Dedicated LB : 1 ~ 480000) + KeepAliveTimeout: DefaultKeepAliveTimeout, // NHN Cloud Listener KeepAliveTimeout range : 0 ~ 3600 + ProtocolPort: portNum, + } + listener, err := listeners.Create(nlbHandler.NetworkClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create Listener. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return listeners.Listener{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + return *listener, nil +} + +func (nlbHandler *NhnCloudNLBHandler) CreatePool(listenerId string, nlbReqInfo irs.NLBInfo) (*pools.Pool, error) { + cblogger.Info("NHN Cloud Driver: called CreatePool()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", "CreatePool()", "CreatePool()") + + if strings.EqualFold(listenerId, "") { + newErr := fmt.Errorf("Invalid Listener ID!!") + cblogger.Error(newErr.Error()) + return nil, newErr + } + + poolProtocol, err := GetPoolProtocol(nlbReqInfo.VMGroup.Protocol) + if err != nil { + newErr := fmt.Errorf("Invalid Pool Protocol!! : [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return nil, newErr + } + + portNum, err := strconv.Atoi(nlbReqInfo.VMGroup.Port) + if err != nil { + newErr := fmt.Errorf("Invalid vmGroup Port. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + if portNum < 1 || portNum > 65535 { + newErr := fmt.Errorf("Invalid vmGroup Port.(Must be between 1 and 65535)") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + // # Note : NHN Cloud LBMethods on GoSDK(nhncloud-sdk-go) + // LBMethodRoundRobin LBMethod = "ROUND_ROBIN" + // LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" + // LBMethodSourceIp LBMethod = "SOURCE_IP" + createOpts := pools.CreateOpts{ + ListenerID: listenerId, // required:"true" + LBMethod: pools.LBMethodRoundRobin, // required:"true" + Protocol: poolProtocol, // required:"true". # Protocol of VM Member + Description: nlbReqInfo.IId.NameId, + MemberPort: portNum, + } + newPool, err := pools.Create(nlbHandler.NetworkClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create NHN Cloud Pool. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + return newPool, nil +} + +func (nlbHandler *NhnCloudNLBHandler) CreateHealthMonitor(poolId string, nlbReqInfo irs.NLBInfo) (monitors.Monitor, error) { + cblogger.Info("NHN Cloud Driver: called CreateHealthMonitor()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", "CreateHealthMonitor()", "CreateHealthMonitor()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + return monitors.Monitor{}, newErr + } + + switch strings.ToUpper(nlbReqInfo.HealthChecker.Protocol) { + case "TCP", "HTTP", "HTTPS": + cblogger.Infof("\n# HealthChecker.Protocol : [%s]", strings.ToUpper(nlbReqInfo.HealthChecker.Protocol)) + default: + return monitors.Monitor{}, fmt.Errorf("Invalid Health Monitor Type. (Must be 'TCP', 'HTTP' or 'HTTPS' for NHN Cloud.)") // According to the NHN Cloud API document + } + + healthCheckerInterval := nlbReqInfo.HealthChecker.Interval + healthCheckerTimeout := nlbReqInfo.HealthChecker.Timeout + healthCheckerThreshold := nlbReqInfo.HealthChecker.Threshold + + if healthCheckerInterval == -1 { + healthCheckerInterval = DefaultHealthCheckerInterval // 30 seconds + } + if healthCheckerTimeout == -1 { + healthCheckerTimeout = DefaultHealthCheckerTimeout // 5 seconds + } + if healthCheckerThreshold == -1 { + healthCheckerThreshold = DefaultHealthCheckerThreshold // 2 times + } + + if healthCheckerInterval > 5000 || healthCheckerInterval < 1 { + return monitors.Monitor{}, fmt.Errorf("Invalid HealthChecker Interval value. Must be a number between 1 and 5000") // According to the NHN Cloud LB console + } + // Ref) Interval : Status check interval + + if healthCheckerTimeout < 1 { + return monitors.Monitor{}, fmt.Errorf("Invalid HealthChecker Timeout value. Must be a number greater than zero.") + } + if healthCheckerInterval < healthCheckerTimeout { + return monitors.Monitor{}, fmt.Errorf("Invalid HealthChecker Timeout value. Must be less than the 'Interval' value.") + } + // Ref) Timeout : Maximum number of seconds for a Monitor to wait for a ping reply before it times out. The value must be less than the Interval(Delay) value. + + if healthCheckerThreshold > 10 || healthCheckerThreshold < 1 { + return monitors.Monitor{}, fmt.Errorf("Invalid HealthChecker Threshold value. Must be a number between 1 and 10") // According to the NHN Cloud LB console + } + // Ref) Threshold (MaxRetries) : Number of permissible ping failures before changing the member's status to INACTIVE. Must be a number between 1 and 10. + + portNum, err := strconv.Atoi(nlbReqInfo.HealthChecker.Port) + if err != nil { + newErr := fmt.Errorf("Invalid HealthChecker Port. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return monitors.Monitor{}, newErr + } + if portNum < 1 || portNum > 65535 { + newErr := fmt.Errorf("Invalid HealthChecker Port.(Must be between 1 and 65535)") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return monitors.Monitor{}, newErr + } + + callLogStart := call.Start() + // Note : NHN Cloud HealthChecker Protocols : TCP, HTTP or HTTPS + creatOpts := monitors.CreateOpts{ + PoolID: poolId, // required:"true" + HealthCheckPort: portNum, + Delay: healthCheckerInterval, // required:"true". Must be between 1 and 5000. + MaxRetries: healthCheckerThreshold, // required:"true" Must be between 1 and 10. + Timeout: healthCheckerTimeout, // required:"true". Must be between 1 and 5000. Must be smaller than Interval time. + Type: strings.ToUpper(nlbReqInfo.HealthChecker.Protocol), // required:"true" + } + newMonitor, err := monitors.Create(nlbHandler.NetworkClient, &creatOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create HealthChecker. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return monitors.Monitor{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + return *newMonitor, nil +} + +func (nlbHandler *NhnCloudNLBHandler) CreateVMMembers(poolId string, vmGroupInfo irs.VMGroupInfo) ([]pools.Member, error) { + cblogger.Info("NHN Cloud Driver: called CreateVMMembers()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", poolId, "CreateVMMembers()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + if len(*vmGroupInfo.VMs) < 1 { + newErr := fmt.Errorf("Failed to Find any VM to Create the VMGroup!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + portNum, err := strconv.Atoi(vmGroupInfo.Port) + if err != nil { + newErr := fmt.Errorf("Invalid VMGroup Port Number. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + if portNum < 1 || portNum > 65535 { + newErr := fmt.Errorf("Invalid VMGroup Port Number.(Must be between 1 and 65535)") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + var poolMembers []pools.Member + for _, vmIId := range *vmGroupInfo.VMs { + cblogger.Infof("\n\n#### Adding VM [%s] as a VMGroup Member : ", vmIId.NameId) + + privateIp, subnetId, err := nlbHandler.GetNetInfoWithVMName(vmIId.NameId) + if err != nil { + newErr := fmt.Errorf("Failed to Get Private IP and Subnet ID.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + creatOpts := pools.CreateMemberOpts{ + Weight: DefaultWeight, + AdminStateUp: DefaultAdminStateUp, + SubnetID: subnetId, + Address: privateIp, + ProtocolPort: portNum, + } + createResult, err := pools.CreateMember(nlbHandler.NetworkClient, poolId, creatOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create NLB Member with the Pool ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + poolMembers = append(poolMembers, *createResult) + + cblogger.Info("\n\n#### Waiting for Provisioning the Pool Member!!") + time.Sleep(25 * time.Second) + + // _, err = nlbHandler.WaitToGetVMMemberInfo(poolId, *&createResult.ID) // Wait until 'provisioningStatus' is "ACTIVE" + // if err != nil { + // return nil, err + // } + } + return poolMembers, nil +} + +func (nlbHandler *NhnCloudNLBHandler) CreatePublicIP(nlbVipPortId string) (string, error) { + cblogger.Info("NHN Cloud Driver: called CreatePublicIP()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbVipPortId, "CreatePublicIP()") + + if strings.EqualFold(nlbVipPortId, "") { + newErr := fmt.Errorf("Invalid Vip Port ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + externVPCID, err := GetPublicVPCInfo(nlbHandler.NetworkClient, "ID") + if err != nil { + newErr := fmt.Errorf("Failed to Get the VPC ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", err + } + + callLogStart := call.Start() + createOpts := floatingips.CreateOpts{ + FloatingNetworkID: externVPCID, + PortID: nlbVipPortId, + } + createResult, err := floatingips.Create(nlbHandler.NetworkClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create PublicIP for the New NLB. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, callLogStart) + // spew.Dump(createResult) + + newFloatingIp := createResult.FloatingIP + return newFloatingIp, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetVMGroupHealthInfo(nlbIID irs.IID) (irs.HealthInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetVMGroupHealthInfo()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "GetVMGroupHealthInfo()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthInfo{}, newErr + } + + nlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthInfo{}, newErr + } + + vmMemberList, err := nlbHandler.GetNhnVMMembersWithPoolId(nlbInfo.VMGroup.CspID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.HealthInfo{}, newErr + } + + var allVMs []irs.IID + var healthVMs []irs.IID + var unHealthVMs []irs.IID + + callLogStart := call.Start() + for _, member := range *vmMemberList { + vm, err := nlbHandler.GetNhnVMWithPrivateIp(member.Address) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM with the Private IP address. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.HealthInfo{}, newErr + } + + allVMs = append(allVMs, irs.IID{NameId: vm.Name, SystemId: vm.ID}) // Caution : Not 'VM Member ID' but 'VM System ID' + + if strings.EqualFold(member.OperatingStatus, "ACTIVE") { + cblogger.Infof("\n### [%s] is Healthy VM.", vm.Name) + healthVMs = append(healthVMs, irs.IID{NameId: vm.Name, SystemId: vm.ID}) + } else { + cblogger.Infof("\n### [%s] is Unhealthy VM.", vm.Name) + unHealthVMs = append(unHealthVMs, irs.IID{NameId: vm.Name, SystemId: vm.ID}) // In case of "INACTIVE", ... + } + } + LoggingInfo(callLogInfo, callLogStart) + + vmGroupHealthInfo := irs.HealthInfo{ + AllVMs: &allVMs, + HealthyVMs: &healthVMs, + UnHealthyVMs: &unHealthVMs, + } + return vmGroupHealthInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) ChangeHealthCheckerInfo(nlbIID irs.IID, healthChecker irs.HealthCheckerInfo) (irs.HealthCheckerInfo, error) { + cblogger.Info("NHN Cloud Driver: called ChangeHealthCheckerInfo()") + + return irs.HealthCheckerInfo{}, fmt.Errorf("NHN Cloud does not support ChangeHealthCheckerInfo() yet!!") +} + +func (nlbHandler *NhnCloudNLBHandler) GetListenerInfo(listenerId string) (irs.ListenerInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetListenerInfo()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", listenerId, "GetListenerInfo()") + + if strings.EqualFold(listenerId, "") { + newErr := fmt.Errorf("Invalid Listener ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.ListenerInfo{}, newErr + } + + callLogStart := call.Start() + listenerOptions := listeners.ListOpts{ + ID: listenerId, + } + allPages, err := listeners.List(nlbHandler.NetworkClient, &listenerOptions).AllPages() + if err != nil { + return irs.ListenerInfo{}, err + } + + nhnListenerList, err := listeners.ExtractListeners(allPages) + if len(nhnListenerList) < 1 { + newErr := fmt.Errorf("Failed to Get Listener with the ID [%s] : [%v]", listenerId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.ListenerInfo{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + listenerInfo, err := nlbHandler.MappingListenerInfo(nhnListenerList[0]) + if err != nil { + newErr := fmt.Errorf("Failed to Get Listener Info from NHN Listener. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.ListenerInfo{}, newErr + } + return listenerInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetFirstSubnetIdWithVPCName(vpcName string) (subnetId string, err error) { + cblogger.Info("NHN Cloud Driver: called GetFirstSubnetIdWithVPCName()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", vpcName, "GetFirstSubnetIdWithVPCName()") + + if strings.EqualFold(vpcName, "") { + newErr := fmt.Errorf("Invalid VPC Name required") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + callLogStart := call.Start() + listOpts := external.ListOptsExt { + ListOptsBuilder: networks.ListOpts{ + Name: vpcName, + }, + } + allPages, err := networks.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VPC List with the Name : [%s]", vpcName) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + var nhnVpcList []NetworkWithExt + err = networks.ExtractNetworksInto(allPages, &nhnVpcList) + if err != nil { + newErr := fmt.Errorf("Failed to Extract NHN VPC List.") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var nhnVpc NetworkWithExt + + for _, vpc := range nhnVpcList { + if strings.EqualFold(vpc.Name, vpcName) { + nhnVpc = vpc + } + } + + if len(nhnVpc.Subnets) > 0 { + return nhnVpc.Subnets[0], nil + } else { + return "", fmt.Errorf("Failed to Get the First Subnet ID.") + } +} + +// Waiting for Provisioning to Complete. +func (nlbHandler *NhnCloudNLBHandler) WaitToGetNLBInfo(nlbIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called WaitToGetNLBInfo()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + return false, newErr + } + + curRetryCnt := 0 + maxRetryCnt := 240 + for { + curRetryCnt++ + provisioningStatus, err := nlbHandler.GetNlbProvisioningStatus(nlbIID) + if err == nil { + if strings.EqualFold(provisioningStatus, "ACTIVE") { + return true, nil + } + if strings.EqualFold(provisioningStatus, "ERROR") { + return false, fmt.Errorf("Failed to Create NLB. ProvisioningStatus : ERROR") + } + } + time.Sleep(3 * time.Second) + if curRetryCnt > maxRetryCnt { + return false, fmt.Errorf("Failed to Create NLB. Exceeded maximum retry count %d", maxRetryCnt) + } + } +} + +func (nlbHandler *NhnCloudNLBHandler) GetNlbProvisioningStatus(nlbIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetNlbProvisioningStatus()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "GetNlbProvisioningStatus()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + nlbInfo, err := nlbHandler.GetNLB(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB info!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + var status string + // Use Key/Value info of the nlbInfo. + for _, keyInfo := range nlbInfo.KeyValueList { + if strings.EqualFold(keyInfo.Key, "NLB_ProvisioningStatus") { + status = keyInfo.Value + break + } + } + return status, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNlbVipPortId(nlbIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetNlbVipPortId()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "GetNlbVipPortId()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + callLogStart := call.Start() + listOpts := loadbalancers.ListOpts{ + ID: nlbIID.SystemId, + } + allPages, err := loadbalancers.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud NLB Pages. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + nlbList, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud NLB list. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var vipPortId string + if len(nlbList) > 0 { + vipPortId = nlbList[0].VipPortID + } else { + newErr := fmt.Errorf("Failed to Get Any NHN Cloud NLB Info. with the NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + cblogger.Info("\n# VipPortId : " + vipPortId) + return vipPortId, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNlbPrivateIp(nlbIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetNlbPrivateIp()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "GetNlbPrivateIp()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + callLogStart := call.Start() + listOpts := loadbalancers.ListOpts{ + ID: nlbIID.SystemId, + } + allPages, err := loadbalancers.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud NLB Pages. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + nlbList, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud NLB list. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var privateIp string + if len(nlbList) > 0 { + privateIp = nlbList[0].VipAddress + } else { + newErr := fmt.Errorf("Failed to Get Any NHN Cloud NLB Info. with the NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + cblogger.Info("\n# NLB Private IP : " + privateIp) + return privateIp, nil +} + +// Caution : 'vmMemberId' is not VM ID. +func (nlbHandler *NhnCloudNLBHandler) GetVMMemberOperatingStatus(poolId string, vmMemberId string) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetVMMemberProvisioningStatus()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", vmMemberId, "GetVMMemberProvisioningStatus()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + if strings.EqualFold(vmMemberId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + + callLogStart := call.Start() + nhnMember, err := nlbHandler.GetNhnVMMember(poolId, vmMemberId) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + LoggingInfo(callLogInfo, callLogStart) + + return *&nhnMember.OperatingStatus, nil +} + +// Note : NHN Cloud Listener Protocols : TCP, HTTP, HTTPS or TERMINATED_HTTPS +func GetListenerProtocol(protocol string) (listeners.Protocol, error) { + cblogger.Info("NHN Cloud Driver: called GetListenerProtocol()") + + if strings.EqualFold(protocol, string(listeners.ProtocolTCP)) { + return listeners.ProtocolTCP, nil + } else if strings.EqualFold(protocol, string(listeners.ProtocolHTTP)) { + return listeners.ProtocolHTTP, nil + } else if strings.EqualFold(protocol, string(listeners.ProtocolHTTPS)) { + return listeners.ProtocolHTTPS, nil + } else if strings.EqualFold(protocol, string(listeners.ProtocolTerminatedHTTPS)) { + return listeners.ProtocolTerminatedHTTPS, nil + } + + newErr := fmt.Errorf("NHN Listener supports only TCP, HTTP, HTTPS or TERMINATED_HTTPS protocol!!") // According to the NHN Cloud API document + cblogger.Error(newErr.Error()) + return "", newErr +} + +func GetPoolProtocol(protocol string) (pools.Protocol, error) { + cblogger.Info("NHN Cloud Driver: called GetPoolProtocol()") + + if strings.EqualFold(protocol, string(pools.ProtocolTCP)) { + return pools.ProtocolTCP, nil + } else if strings.EqualFold(protocol, string(pools.ProtocolHTTP)) { + return pools.ProtocolHTTP, nil + } else if strings.EqualFold(protocol, string(pools.ProtocolHTTPS)) { + return pools.ProtocolHTTPS, nil + } + + newErr := fmt.Errorf("NHN Pool supports only TCP, HTTP or HTTPS protocol!!") + cblogger.Error(newErr.Error()) + return "", newErr +} + +func (nlbHandler *NhnCloudNLBHandler) GetHealthMonitorInfo(healthMonitorId string) (irs.HealthCheckerInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetHealthMonitorInfo()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", healthMonitorId, "GetHealthMonitorInfo()") + + if strings.EqualFold(healthMonitorId, "") { + newErr := fmt.Errorf("Invalid Health Monitor ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + + callLogStart := call.Start() + listOpts := monitors.ListOpts{ + ID: healthMonitorId, + } + allPages, err := monitors.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN HealthChecker with the ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + + nhnMonitorlist, err := monitors.ExtractMonitors(allPages) + if len(nhnMonitorlist) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN HealthChecker with the ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + healthCheckerInfo := nlbHandler.MappingMonitorInfo(nhnMonitorlist[0]) + + return healthCheckerInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetHealthMonitorListWithPoolId(poolId string) ([]irs.HealthCheckerInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetHealthMonitorListWithPoolId()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", poolId, "GetHealthMonitorListWithPoolId()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + listOpts := monitors.ListOpts{} + allPages, err := monitors.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN HealthChecker list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + nhnMonitorlist, err := monitors.ExtractMonitors(allPages) + if len(nhnMonitorlist) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN HealthChecker. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var healthCheckerInfoList []irs.HealthCheckerInfo + + for _, nhnMonitor := range nhnMonitorlist { + for _, pool := range nhnMonitor.Pools { + if pool.ID == poolId { + healthCheckerInfo := nlbHandler.MappingMonitorInfo(nhnMonitor) + healthCheckerInfoList = append(healthCheckerInfoList, healthCheckerInfo) + } + } + } + + return healthCheckerInfoList, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNhnPoolListWithListenerId(listenerId string) ([]pools.Pool, error) { + cblogger.Info("NHN Cloud Driver: called GetNhnPoolListWithListenerId()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", listenerId, "GetNhnPoolListWithListenerId()") + + if strings.EqualFold(listenerId, "") { + newErr := fmt.Errorf("Invalid Listener ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + listOpts := pools.ListOpts{} + callLogStart := call.Start() + allPages, err := pools.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Pool list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + poolList, err := pools.ExtractPools(allPages) + if len(poolList) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN Pool. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var nhnPoolList []pools.Pool + + for _, pool := range poolList { + for _, listener := range pool.Listeners { + if listener.ID == listenerId { + nhnPoolList = append(nhnPoolList, pool) + } + } + } + + return nhnPoolList, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetHealthMonitorInfoWithListenerId(listenerId string) (irs.HealthCheckerInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetHealthMonitorInfoWithListenerId()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", listenerId, "GetHealthMonitorInfoWithListenerId()") + + if strings.EqualFold(listenerId, "") { + newErr := fmt.Errorf("Invalid Listener ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + + nhnPoolList, err := nlbHandler.GetNhnPoolListWithListenerId(listenerId) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Pool list with the Listener ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + if len(nhnPoolList) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN Pool. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + + // Note : Cloud-Barista supports only this case => [ LB : Listener : Pool : Health Checker = 1 : 1 : 1 : 1 ] + monitorList, err := nlbHandler.GetHealthMonitorListWithPoolId(nhnPoolList[0].ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Health Monitor list with the ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + if len(monitorList) < 1 { + newErr := fmt.Errorf("Failed to Get Health Monitor list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.HealthCheckerInfo{}, newErr + } + + // Note : Cloud-Barista supports only this case => [ LB : Listener : Pool : Health Checker = 1 : 1 : 1 : 1 ] + return monitorList[0], nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNetInfoWithVMName(vmName string) (string, string, error) { + cblogger.Info("NHN Cloud Driver: called GetNetInfoWithVMName()") + + if strings.EqualFold(vmName, "") { + newErr := fmt.Errorf("Invalid VM Name!!") + cblogger.Error(newErr.Error()) + return "", "", newErr + } + + listOpts := servers.ListOpts{ + Limit: 200, + } + allPages, err := servers.List(nlbHandler.VMClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get HNH Server list. [%v]", err.Error()) + cblogger.Error(err.Error()) + return "", "", newErr + } + + serverList, err := servers.ExtractServers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get HNH Server list. [%v]", err.Error()) + cblogger.Error(err.Error()) + return "", "", newErr + } + + var ipAddress string + var subnetId string + for _, server := range serverList { + if strings.EqualFold(server.Name, vmName) { + for _, subnet := range server.Addresses { + for _, addr := range subnet.([]interface{}) { + addrMap := addr.(map[string]interface{}) + if addrMap["OS-EXT-IPS:type"] == "fixed" { // In case of fixed IP (Private IP Address) + ipAddress = addrMap["addr"].(string) + } + } + } + + // Get Subnet, Network Interface Info + port, err := GetPortWithDeviceId(nlbHandler.NetworkClient, server.ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get HNH Port Info. [%v]", err.Error()) + cblogger.Error(err.Error()) + return "", "", newErr + } + if port != nil { + if len(port.FixedIPs) > 0 { + subnetId = port.FixedIPs[0].SubnetID + } + } + } + } + + return ipAddress, subnetId, nil +} + +func (nlbHandler *NhnCloudNLBHandler) DeleteVMMember(poolId string, vmIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeleteVMMember()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", poolId, "DeleteVMMember()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + if strings.EqualFold(vmIID.NameId, "") { + newErr := fmt.Errorf("Invalid VM NameId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + vmMemberList, err := nlbHandler.GetNhnVMMembersWithPoolId(poolId) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + for _, member := range *vmMemberList { + vm, err := nlbHandler.GetNhnVMWithPrivateIp(member.Address) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM with the Private IP address. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + if strings.EqualFold(vmIID.NameId, vm.Name) { + callLogStart := call.Start() + err = pools.DeleteMember(nlbHandler.NetworkClient, poolId, member.ID).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Delete the NHN VM Member. : [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, callLogStart) + } + + } + + cblogger.Info("\n\n#### Waiting for Deleting the Pool Member!!") + time.Sleep(10 * time.Second) + + return true, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetVMGroupInfo(nlb loadbalancers.LoadBalancer) (irs.VMGroupInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetVMGroupInfo()") + + if strings.EqualFold(nlb.Listeners[0].ID, "") { + newErr := fmt.Errorf("Invalid Listener ID") + cblogger.Error(newErr.Error()) + return irs.VMGroupInfo{}, newErr + } + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlb.Listeners[0].ID, "GetVMGroupInfo()") + + // Note : Cloud-Barista supports only this case => [ LB : Listener : Pool : Health Checker = 1 : 1 : 1 : 1 ] + nhnPoolList, err := nlbHandler.GetNhnPoolListWithListenerId(nlb.Listeners[0].ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Pool list with the Listener ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + if len(nhnPoolList) < 1 { + newErr := fmt.Errorf("Failed to Get Any NHN Pool. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + vmGroupInfo := irs.VMGroupInfo{ + Protocol: nhnPoolList[0].Protocol, // Note : Protocol of the 'NHN Pool' + Port: strconv.Itoa(nhnPoolList[0].MemberPort), // Member's port for receiving. Deliver traffic to this port. Not Exits on API Manual. + CspID: nhnPoolList[0].ID, // Note : ID of the 'NHN Pool' + } + + vmMemberList, err := nlbHandler.GetNhnVMMembersWithPoolId(nhnPoolList[0].ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Members. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + var vmIIds []irs.IID + for _, member := range *vmMemberList { + vm, err := nlbHandler.GetNhnVMWithPrivateIp(member.Address) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM with the Private IP address. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMGroupInfo{}, newErr + } + + vmIIds = append(vmIIds, irs.IID{ + NameId: vm.Name, + SystemId: vm.ID, // Caution : Not 'VM Member' ID + }) + } + vmGroupInfo.VMs = &vmIIds + return vmGroupInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNhnVMMembersWithPoolId(poolId string) (*[]pools.Member, error) { + cblogger.Info("NHN Cloud Driver: called GetNhnVMMembersWithPoolId()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", poolId, "GetNhnVMMembersWithPoolId()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + lostOpts := pools.ListMembersOpts{} + allPages, err := pools.ListMembers(nlbHandler.NetworkClient, poolId, &lostOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member pages. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + vmMemberList, err := pools.ExtractMembers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + return &vmMemberList, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNhnVMMember(poolId string, memberId string) (*pools.Member, error) { + cblogger.Info("NHN Cloud Driver: called GetNhnVMMember()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", memberId, "GetNhnVMMember()") + + if strings.EqualFold(poolId, "") { + newErr := fmt.Errorf("Invalid Pool ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + if strings.EqualFold(memberId, "") { + newErr := fmt.Errorf("Invalid VM Member ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + lostOpts := pools.ListMembersOpts{ + ID: memberId, + } + allPages, err := pools.ListMembers(nlbHandler.NetworkClient, poolId, &lostOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member pages. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + vmMemberList, err := pools.ExtractMembers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Member list. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + return &vmMemberList[0], nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetNhnVMWithPrivateIp(privateIp string) (*servers.Server, error) { + cblogger.Info("NHN Cloud Driver: called GetNhnVMWithPrivateIp()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", privateIp, "GetNhnVMWithPrivateIp()") + + if strings.EqualFold(privateIp, "") { + newErr := fmt.Errorf("Invalid Private IP!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + callLogStart := call.Start() + listOpts := servers.ListOpts{ + Limit: 200, + } + allPages, err := servers.List(nlbHandler.VMClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN VM Pages. [%v]", err.Error()) + cblogger.Error(err.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + serverList, err := servers.ExtractServers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get HNH VM list. [%v]", err.Error()) + cblogger.Error(err.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + var nhnVM servers.Server + for _, server := range serverList { + for _, subnet := range server.Addresses { + for _, addr := range subnet.([]interface{}) { + addrMap := addr.(map[string]interface{}) + if addrMap["OS-EXT-IPS:type"] == "fixed" { // In case of fixed IP (Private IP Address) + if strings.EqualFold(addrMap["addr"].(string), privateIp){ + nhnVM = server + } + } + } + } + } + + return &nhnVM, nil +} + +func (nlbHandler *NhnCloudNLBHandler) MappingNlbInfo(nhnNLB loadbalancers.LoadBalancer) (irs.NLBInfo, error) { + cblogger.Info("NHN Cloud Driver: called MappingNlbInfo()") + + vpcId, err := nlbHandler.GetVPCIdWithSubnetId(nhnNLB.VipSubnetID) + if err != nil { + cblogger.Error(err.Error()) + return irs.NLBInfo{}, err + } + + var nlbType string + if strings.EqualFold(nhnNLB.LoadBalancerType, PublicType) { + nlbType = "PUBLIC" + } else if strings.EqualFold(nhnNLB.LoadBalancerType, InternalType){ + nlbType = "INTERNAL" + } + + nlbInfo := irs.NLBInfo{ + IId: irs.IID{ + NameId: nhnNLB.Name, + SystemId: nhnNLB.ID, + }, + VpcIID: irs.IID{ + SystemId: vpcId, + }, + Type: nlbType, + Scope: "REGION", + } + + keyValueList := []irs.KeyValue{ + {Key: "NLB_ProvisioningStatus", Value: nhnNLB.ProvisioningStatus}, + {Key: "NLB_OperatingStatus", Value: nhnNLB.OperatingStatus}, + {Key: "Provider", Value: nhnNLB.Provider}, + {Key: "NLB_PrivateIp", Value: nhnNLB.VipAddress}, + {Key: "SubnetId", Value: nhnNLB.VipSubnetID}, + {Key: "VipPortId", Value: nhnNLB.VipPortID}, + } + + if len(nhnNLB.Listeners) > 0 { + nlbIID := irs.IID{SystemId:nhnNLB.ID} + publicIp, err := nlbHandler.GetNlbPublicIP(nlbIID) + if err != nil { + cblogger.Error(err.Error()) + return irs.NLBInfo{}, err + } + + listenerInfo, err := nlbHandler.GetListenerInfo(nhnNLB.Listeners[0].ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Listener with the ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } + listenerInfo.IP = publicIp + nlbInfo.Listener = listenerInfo + + listenerKeyValue := irs.KeyValue{Key: "ListenerId", Value: nhnNLB.Listeners[0].ID} + keyValueList = append(keyValueList, listenerKeyValue) + + monitorInfo, err := nlbHandler.GetHealthMonitorInfoWithListenerId(nhnNLB.Listeners[0].ID) + if err != nil { + newErr := fmt.Errorf("Failed to Get Listener with the ID. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } + nlbInfo.HealthChecker = monitorInfo + + monitorKeyValue := irs.KeyValue{Key: "HealthCheckerId", Value: nlbInfo.HealthChecker.CspID} + keyValueList = append(keyValueList, monitorKeyValue) + + vmGroupInfo, err := nlbHandler.GetVMGroupInfo(nhnNLB) + if err != nil { + newErr := fmt.Errorf("Failed to Get VM Info with the NHH NLB info. [%v]", err.Error()) + cblogger.Error(newErr.Error()) + return irs.NLBInfo{}, newErr + } + + nlbInfo.VMGroup = vmGroupInfo + + poolKeyValue := irs.KeyValue{Key: "PoolId", Value: nlbInfo.VMGroup.CspID} // Note : VMGroup.CspID => PoolId + keyValueList = append(keyValueList, poolKeyValue) + } + + nlbInfo.KeyValueList = keyValueList + + return nlbInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) MappingListenerInfo(nhnListener listeners.Listener) (irs.ListenerInfo, error) { + cblogger.Info("NHN Cloud Driver: called MappingListenerInfo()") + + listenerProtocol, err := GetListenerProtocol(string(nhnListener.Protocol)) + if err != nil { + newErr := fmt.Errorf("Invalid Listener Protocol!!") + cblogger.Error(newErr.Error()) + return irs.ListenerInfo{}, newErr + } + + listenerInfo := irs.ListenerInfo{ + Protocol: string(listenerProtocol), + Port: strconv.Itoa(nhnListener.ProtocolPort), + CspID: nhnListener.ID, + } + + keyValueList := []irs.KeyValue{ + {Key: "AdminStateUp", Value: strconv.FormatBool(nhnListener.AdminStateUp)}, + {Key: "ConnectionLimit", Value: strconv.Itoa(nhnListener.ConnLimit)}, + {Key: "KeepaliveTimeout(Sec)", Value: strconv.Itoa(nhnListener.KeepaliveTimeout)}, + } + listenerInfo.KeyValueList = keyValueList + + return listenerInfo, nil +} + +func (nlbHandler *NhnCloudNLBHandler) MappingMonitorInfo(nhnMonitor monitors.Monitor) (irs.HealthCheckerInfo) { + cblogger.Info("NHN Cloud Driver: called MappingMonitorInfo()") + + healthCheckerInfo := irs.HealthCheckerInfo{ + Protocol: nhnMonitor.Type, + Port: strconv.Itoa(nhnMonitor.HealthCheckPort), + Interval: nhnMonitor.Delay, + Threshold: nhnMonitor.MaxRetries, + Timeout: nhnMonitor.Timeout, + CspID: nhnMonitor.ID, + } + + keyValueList := []irs.KeyValue{ + {Key: "AdminStateUp", Value: strconv.FormatBool(nhnMonitor.AdminStateUp)}, + {Key: "PoolId", Value: nhnMonitor.Pools[0].ID}, + } + healthCheckerInfo.KeyValueList = keyValueList + + return healthCheckerInfo +} + +func (nlbHandler *NhnCloudNLBHandler) GetNlbPublicIP(nlbIID irs.IID) (string, error) { + cblogger.Info("NHN Cloud Driver: called DeletePublicIP()") + + privateIp, err := nlbHandler.GetNlbPrivateIp(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB VPC ID and VIP Port ID!! [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + + // To Get Floating IP(Public IP) Address Info. + listOpts := floatingips.ListOpts{ + FixedIP: privateIp, + } + allPages, err := floatingips.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get FloatingIP Pages!! [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + ipList, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get FloatingIP List of the NLB!! [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + + var floatingIp string + if len(ipList) < 1 { + newErr := fmt.Errorf("Failed to Get Any FloatingIP Info of the NLB!! [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } else { + floatingIp = ipList[0].FloatingIP + } + + cblogger.Info("\n# NLB Floating IP : " + floatingIp) + + return floatingIp, nil +} + +func (nlbHandler *NhnCloudNLBHandler) DeletePublicIP(nlbIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called DeletePublicIP()") + + vipPortId, err := nlbHandler.GetNlbVipPortId(nlbIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get NLB VPC ID and VIP Port ID!! [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } + + // To Get Floating IP(Public IP) Address Info. + listOpts := floatingips.ListOpts{ + PortID: vipPortId, + } + allPages, err := floatingips.List(nlbHandler.NetworkClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get FloatingIP Pages!! [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } + ipList, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get FloatingIP List!! [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } + + var floatingIpId string + if len(ipList) < 1 { + newErr := fmt.Errorf("Failed to Get Any FloatingIP Info!! [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } else { + floatingIpId = ipList[0].ID + } + + delErr := floatingips.Delete(nlbHandler.NetworkClient, floatingIpId).ExtractErr() + if delErr != nil { + newErr := fmt.Errorf("Failed to Delete the FloatingIP of the NLB!! [%v]", err) + cblogger.Error(newErr.Error()) + return false, newErr + } else { + cblogger.Info("\n# Succeeded in Deleting the FloatingIP of the NLB.") + } + + return true, nil +} + +// Clean up the Created New NLB in case of failure +func (nlbHandler *NhnCloudNLBHandler) CleanUpNLB(nlbIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Driver: called CleanUpNLB()") + callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", nlbIID.SystemId, "CleanUpNLB()") + + if strings.EqualFold(nlbIID.SystemId, "") { + newErr := fmt.Errorf("Invalid NLB ID!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + cblogger.Info("\n\n#### Waiting for Provisioning to Delete the NLB!!") + time.Sleep(20 * time.Second) + + callLogStart := call.Start() + delOpts := loadbalancers.DeleteOpts{Cascade: true} // Note : 'Cascade' will delete all children of the LB (Listeners, Monitors, etc). + delErr := loadbalancers.Delete(nlbHandler.NetworkClient, nlbIID.SystemId, delOpts).ExtractErr() + if delErr != nil { + newErr := fmt.Errorf("Failed to Delete the NLB. : [%v]", delErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, callLogStart) + + return true, nil +} + +func (nlbHandler *NhnCloudNLBHandler) GetVPCIdWithSubnetId(subnetId string) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetVPCIdWithSubnetId()") + + if strings.EqualFold(subnetId, "") { + newErr := fmt.Errorf("Invalid Subnet ID!!") + cblogger.Error(newErr.Error()) + return "", newErr + } + + subnet, err := subnets.Get(nlbHandler.NetworkClient, subnetId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Cloud Subnet Info with the Subnet ID [%s] : %v", subnetId, err.Error()) + cblogger.Error(newErr.Error()) + return "", nil + } + + VPCId := subnet.NetworkID + + return VPCId, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/RegionZoneHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/RegionZoneHandler.go new file mode 100644 index 000000000..d48a4dff8 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/RegionZoneHandler.go @@ -0,0 +1,294 @@ +// Proof of Concepts for the Cloud-Barista Multi-Cloud Project. +// * Cloud-Barista: https://github.com/cloud-barista +// +// NHN Cloud RegionZone Handler +// +// Created by ETRI, 2023.09. +//================================================================================================== + +package resources + +import ( + "sync" + "strings" + // "errors" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + ostack "github.com/cloud-barista/nhncloud-sdk-go/openstack" + az "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/availabilityzones" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +// ##### (Note) NHN Cloud only provides detailed Zone information with the API, but unlike other CSPs, the API Endpoint is different for each Region, so when calling with API, only the zone information of the Region comes out without the Region information. +// NHN Region Info. Ref) https://docs.nhncloud.com/ko/Compute/Compute/ko/identity-api/ +// KeyValueList Omission Issue : https://github.com/cloud-barista/cb-spider/issues/930#issuecomment-1734817828 + +type NhnRegionInfo struct { + RegionCode string + RegionName string +} + +// As Constant Variables +func getSupportedRegions() []NhnRegionInfo { + regionInfoList := []NhnRegionInfo { + { RegionCode: "KR1", + RegionName: "한국(판교)", + }, + { RegionCode: "KR2", + RegionName: "한국(평촌)", + }, + { RegionCode: "JP1", + RegionName: "일본", + }, + } + return regionInfoList +} + +type NhnCloudRegionZoneHandler struct { + CredentialInfo idrv.CredentialInfo + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient +} + +func (regionZoneHandler *NhnCloudRegionZoneHandler) ListRegionZone() ([]*irs.RegionZoneInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListRegionZone()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, "ListRegionZone()", "ListRegionZone()") + + nhnRegionList := getSupportedRegions() + // cblogger.Infof("# nhnRegionlist : [%d]", len(nhnRegionList)) + + // ### Even though NHN Cloud does not provide Region info., to get the Zonelist of 'All' Region. + var regionZoneInfoList []*irs.RegionZoneInfo + var wait sync.WaitGroup + var zoneInfoListError error + for _, regionInfo := range nhnRegionList { + wait.Add(1) + go func(regionInfo NhnRegionInfo) { + defer wait.Done() + cblogger.Info("# NHN RegionCode : ", regionInfo.RegionCode) + + regionZoneInfo := irs.RegionZoneInfo{ + Name: regionInfo.RegionCode, + DisplayName: regionInfo.RegionName, + // KeyValueList: []irs.KeyValue{ + // {Key: "RegionCode", Value: regionInfo.RegionCode}, + // }, + } + + zoneInfoList, err := regionZoneHandler.getZoneInfoList(regionInfo.RegionCode) + if err != nil { + zoneInfoListError = err + return + } + regionZoneInfo.ZoneList = zoneInfoList + regionZoneInfoList = append(regionZoneInfoList, ®ionZoneInfo) + }(regionInfo) + + } + wait.Wait() + + if zoneInfoListError != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get ZoneInfoList!!", zoneInfoListError) + return nil, rtnErr + } + + return regionZoneInfoList, nil +} + +func (regionZoneHandler NhnCloudRegionZoneHandler) GetRegionZone(regionCode string) (irs.RegionZoneInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetRegionZone()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, regionCode, "GetRegionZone()") + + nhnRegionList := getSupportedRegions() + var regionZoneInfo irs.RegionZoneInfo + for _, regionInfo := range nhnRegionList { + // cblogger.Info("# NCP RegionCode : ", regionInfo.RegionCode) + + if strings.EqualFold(regionCode, regionInfo.RegionCode) { + regionZoneInfo = irs.RegionZoneInfo { + Name: regionInfo.RegionCode, + DisplayName: regionInfo.RegionName, + // KeyValueList: []irs.KeyValue{ + // {Key: "RegionCode", Value: regionInfo.RegionCode}, + // }, + } + } + } + + // If there is no Region information in the driver, ... + if strings.EqualFold(regionZoneInfo.DisplayName, "") { + regionZoneInfo = irs.RegionZoneInfo { + Name: regionCode, + DisplayName: "", + // KeyValueList: []irs.KeyValue{ + // {Key: "RegionCode", Value: regionCode}, + // }, + } + } + + zoneInfoList, err := regionZoneHandler.getZoneInfoList(regionCode) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get ZoneInfoList : ", err) + return irs.RegionZoneInfo{}, rtnErr + } + regionZoneInfo.ZoneList = zoneInfoList + return regionZoneInfo, nil +} + +func (regionZoneHandler *NhnCloudRegionZoneHandler) ListOrgRegion() (string, error) { + cblogger.Info("NHN Cloud Driver: called ListOrgRegion()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, "ListOrgRegion()", "ListOrgRegion()") + + // To return the results with a style similar to other CSPs. + type Regions struct { + RegionList []NhnRegionInfo + } + + nhnRegionList := getSupportedRegions() + regionList := Regions{ + RegionList: nhnRegionList, + } + jsonString, err := ConvertJsonString(regionList) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Convert to Json String : ", err) + return "", rtnErr + } + return jsonString, nil +} + +func (regionZoneHandler *NhnCloudRegionZoneHandler) ListOrgZone() (string, error) { + cblogger.Info("NHN Cloud Driver: called ListOrgZone()!!") + + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, regionZoneHandler.RegionInfo.Region, "ListOrgZone()") + + // To return the results with a style similar to other CSPs. + type Zones struct { + ZoneList []az.AvailabilityZone + } + + nhnZoneList, err := regionZoneHandler.getNhnZoneList(regionZoneHandler.RegionInfo.Region) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get ZoneInfoList : ", err) + return "", rtnErr + } + zoneList := Zones{ + ZoneList: nhnZoneList, + } + jsonString, err := ConvertJsonString(zoneList) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Convert to Json String : ", err) + return "", rtnErr + } + return jsonString, nil +} + +func (regionZoneHandler NhnCloudRegionZoneHandler) getZoneInfoList(regionCode string) ([]irs.ZoneInfo, error) { + cblogger.Info("NHN Cloud Driver: called getZoneInfoList()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, regionCode, "getZoneInfoList()") + + if strings.EqualFold(regionCode, "") { + rtnErr := logAndReturnError(callLogInfo, "Invalid RegionCode!!", "") + return nil, rtnErr + } + + nhnZoneList, err := regionZoneHandler.getNhnZoneList(regionCode) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get ZoneInfoList : ", err) + return nil, rtnErr + } + + var zoneInfoList []irs.ZoneInfo + for _, zone := range nhnZoneList { + var zoneStatus irs.ZoneStatus + if zone.ZoneState.Available { + zoneStatus = irs.ZoneAvailable + } else { + zoneStatus = irs.ZoneUnavailable + } + + zoneInfo := irs.ZoneInfo{ + Name: zone.ZoneName, + DisplayName: "N/A", + Status: zoneStatus, + // KeyValueList: []irs.KeyValue{ + // {Key: "ZoneCode", Value: zone.ZoneName}, + // }, + } + zoneInfoList = append(zoneInfoList, zoneInfo) + } + return zoneInfoList, nil +} + +func (regionZoneHandler NhnCloudRegionZoneHandler) getNhnZoneList(regionCode string) ([]az.AvailabilityZone, error) { + cblogger.Info("NHN Cloud Driver: called getNhnZoneList()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, regionCode, "getNhnZoneList()") + + if strings.EqualFold(regionCode, "") { + rtnErr := logAndReturnError(callLogInfo, "Invalid RegionCode!!", "") + return nil, rtnErr + } + + regionInfo := idrv.RegionInfo{ + Region: regionCode, + } + connInfo := idrv.ConnectionInfo { + CredentialInfo: regionZoneHandler.CredentialInfo, + RegionInfo: regionInfo, + } + vmClient, err := regionZoneHandler.getNhnVMClient(connInfo) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get VMClient : ", err) + return nil, rtnErr + } + callLogStart := call.Start() + allPages, err := az.List(vmClient).AllPages() + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Zone Pages from NHN Cloud : ", err) + return nil, rtnErr + } + nhnZoneList, err := az.ExtractAvailabilityZones(allPages) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Zone List from NHN Cloud : ", err) + return nil, rtnErr + } + LoggingInfo(callLogInfo, callLogStart) + // cblogger.Infof("# Count : [%d]", len(nhnZoneList)) + // spew.Dump(nhnZoneList) + return nhnZoneList, nil +} + +func (regionZoneHandler NhnCloudRegionZoneHandler) getNhnVMClient(connInfo idrv.ConnectionInfo) (*nhnsdk.ServiceClient, error) { + cblogger.Info("NHN Cloud Driver: called getNhnVMClient()!!") + callLogInfo := GetCallLogScheme(regionZoneHandler.RegionInfo.Zone, call.REGIONZONE, "getNhnVMClient()", "getNhnVMClient()") + + authOpts := nhnsdk.AuthOptions{ + IdentityEndpoint: connInfo.CredentialInfo.IdentityEndpoint, + Username: connInfo.CredentialInfo.Username, + Password: connInfo.CredentialInfo.Password, + DomainName: connInfo.CredentialInfo.DomainName, + TenantID: connInfo.CredentialInfo.TenantId, // Caution : TenantID spelling for SDK + } + + if strings.EqualFold(authOpts.IdentityEndpoint, "") { + rtnErr := logAndReturnError(callLogInfo, "Invalid IdentityEndpoint!!", "") + return nil, rtnErr + } + + providerClient, err := ostack.AuthenticatedClient(authOpts) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get the ProviderClient Info : ", err) + return nil, rtnErr + } + vmClient, err := ostack.NewComputeV2(providerClient, nhnsdk.EndpointOpts{ + Region: connInfo.RegionInfo.Region, + }) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get VMClient Info : ", err) + return nil, rtnErr + } + return vmClient, err +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/SecurityHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/SecurityHandler.go new file mode 100644 index 000000000..c507739b1 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/SecurityHandler.go @@ -0,0 +1,732 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI, 2022.04. + +package resources + +import ( + // "errors" + "fmt" + "errors" + "strconv" + "strings" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/secgroups" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/security/rules" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/external" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/networks" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudSecurityHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + NetworkClient *nhnsdk.ServiceClient +} + +func (securityHandler *NhnCloudSecurityHandler) CreateSecurity(securityReqInfo irs.SecurityReqInfo) (irs.SecurityInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called CreateSecurity()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, securityReqInfo.IId.NameId, "CreateSecurity()") + + // Check if the SecurityGroup Exists + sgInfoList, err := securityHandler.ListSecurity() + if err != nil { + newErr := fmt.Errorf("Failed to Get SG List!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + for _, sgInfo := range sgInfoList { + if sgInfo.IId.NameId == securityReqInfo.IId.NameId { + newErr := fmt.Errorf("Security Group with name [%s] exists already!!", securityReqInfo.IId.NameId) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + } + + // Create SecurityGroup + createOpts := secgroups.CreateOpts{ + Name: securityReqInfo.IId.NameId, + Description: securityReqInfo.IId.NameId, + } + start := call.Start() + newSG, err := secgroups.Create(securityHandler.VMClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create New S/G on NHNCLOUD!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } else { + cblogger.Infof("Succeeded in Creating New S/G : [%s]", securityReqInfo.IId.NameId) + } + LoggingInfo(callLogInfo, start) + cblogger.Infof("New S/G SystemId : [%s]", newSG.ID) + + newSGIID := irs.IID { + SystemId: newSG.ID, + } + + // Add Requested S/G Rules to the New S/G + _, addErr := securityHandler.AddRules(newSGIID, securityReqInfo.SecurityRules) + if err != nil { + newErr := fmt.Errorf("Failed to Add Rule on the S/G!! : [%v] ", addErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + // Basically, Open 'Outbound' All Protocol for Any S/G (<= CB-Spider Rule) + openErr := securityHandler.OpenOutboundAllProtocol(newSGIID) + if openErr != nil { + cblogger.Error(openErr) + LoggingError(callLogInfo, openErr) + // return irs.SecurityInfo{}, openErr + } + + // Return Created S/G Info. + newSGInfo, getErr := securityHandler.GetSecurity(newSGIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get New S/G info!! : [%v] ", getErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + return newSGInfo, nil +} + +func (securityHandler *NhnCloudSecurityHandler) ListSecurity() ([]*irs.SecurityInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called ListSecurity()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, "ListSecurity()", "ListSecurity()") + + // Get Security Group list + start := call.Start() + allPages, err := secgroups.List(securityHandler.VMClient).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get SG List from NhnCloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + + nhnSGList, err := secgroups.ExtractSecurityGroups(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Extract SG List from NhnCloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + LoggingInfo(callLogInfo, start) + + + // Get the Default VPC SystemID + vpcSystemId, err := securityHandler.GetDefaultVPCSystemID() + if err != nil { + newErr := fmt.Errorf("Failed to Get Get the Default VPC SystemID : [%v]", err) + cblogger.Error(newErr.Error()) + return nil, newErr + } + + // Mapping S/G list info. + var sgInfoList []*irs.SecurityInfo + for _, nhnSG := range nhnSGList { + sgInfo, err := securityHandler.MappingSecurityInfo(nhnSG, vpcSystemId) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return nil, err + } + sgInfoList = append(sgInfoList, sgInfo) + } + return sgInfoList, nil +} + +func (securityHandler *NhnCloudSecurityHandler) GetSecurity(securityIID irs.IID) (irs.SecurityInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called GetSecurity()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, securityIID.SystemId, "GetSecurity()") + + start := call.Start() + nhnSG, err := secgroups.Get(securityHandler.VMClient, securityIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the S/G info from NHNCLOUD!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + // spew.Dump(nhnSG) + + // Get the Default VPC SystemID + vpcSystemId, err := securityHandler.GetDefaultVPCSystemID() + if err != nil { + newErr := fmt.Errorf("Failed to Get Get the Default VPC SystemID : [%v]", err) + cblogger.Error(newErr.Error()) + return irs.SecurityInfo{}, newErr + } + + securityInfo, err := securityHandler.MappingSecurityInfo(*nhnSG, vpcSystemId) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return irs.SecurityInfo{}, err + } + return *securityInfo, nil +} + +func (securityHandler *NhnCloudSecurityHandler) DeleteSecurity(securityIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Cloud Driver: called DeleteSecurity()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, securityIID.SystemId, "DeleteSecurity()") + + start := call.Start() + result := secgroups.Delete(securityHandler.VMClient, securityIID.SystemId) + if result.Err != nil { + newErr := fmt.Errorf("Failed to Delete the S/G on NHNCLOUD!! : [%v] ", result.Err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + + return true, nil +} + +func (securityHandler *NhnCloudSecurityHandler) AddRules(sgIID irs.IID, securityRules *[]irs.SecurityRuleInfo) (irs.SecurityInfo, error) { + cblogger.Info("NHN Cloud Driver: called AddRules()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, sgIID.SystemId, "AddRules()") + + if sgIID.SystemId == "" { + newErr := fmt.Errorf("Invalid S/G SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + // Check if the S/G exists + sgInfo, err := securityHandler.GetSecurity(sgIID) + if err != nil { + newErr := fmt.Errorf("Failed to Find any S/G info. with the SystemId : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + cblogger.Infof("S/G SystemId to Add the Rules [%s]", sgInfo.IId.NameId) + + // Add SecurityGroup Rules to the S/G + for _, curRule := range *securityRules { + if curRule.Direction == "" { + return irs.SecurityInfo{}, errors.New("Failed to Find 'Direction' Value in the requested rule!!") + } else if curRule.IPProtocol == "" { + return irs.SecurityInfo{}, errors.New("Failed to Find 'IPProtocol' Value in the requested rule!!") + } else if curRule.FromPort == "" { + return irs.SecurityInfo{}, errors.New("Failed to Find 'FromPort' Value in the requested rule!!") + } else if curRule.ToPort == "" { + return irs.SecurityInfo{}, errors.New("Failed to Find 'ToPort' Value in the requested rule!!") + } else if curRule.CIDR == "" { + return irs.SecurityInfo{}, errors.New("Failed to Find 'CIDR' Value in the requested rule!!") + } + + cblogger.Infof("curRule.IPProtocol : [%s]", curRule.IPProtocol) + + if strings.EqualFold(curRule.IPProtocol, "ALL") { // Add SecurityGroup Rules in case of 'All Traffic Open Rule' + if strings.EqualFold(curRule.FromPort, "-1") && strings.EqualFold(curRule.ToPort, "-1") { + var direction string + if strings.EqualFold(curRule.Direction, "inbound") { + direction = string(rules.DirIngress) + } else if strings.EqualFold(curRule.Direction, "outbound") { + direction = string(rules.DirEgress) + } else { + return irs.SecurityInfo{}, errors.New("Invalid Rule Direction!!") + } + + allProtocolTypeCode := []string {"tcp", "udp", "icmp"} + allCIDR := "0.0.0.0/0" + + for _, curProtocolType := range allProtocolTypeCode { + var createRuleOpts rules.CreateOpts + if strings.EqualFold(curProtocolType, "icmp") { // Without fromPort / toPort + createRuleOpts = rules.CreateOpts{ + Direction: rules.RuleDirection(direction), + EtherType: rules.EtherType4, + SecGroupID: sgIID.SystemId, + Protocol: rules.RuleProtocol(curProtocolType), //Caution!! + RemoteIPPrefix: allCIDR, //Caution!! + } + } else { + var fromPort int + var toPort int + if strings.EqualFold(curRule.FromPort, "-1") && strings.EqualFold(curRule.ToPort, "-1") { // Check again + fromPort = 1 + toPort = 65535 + } + + createRuleOpts = rules.CreateOpts{ + Direction: rules.RuleDirection(direction), + EtherType: rules.EtherType4, + SecGroupID: sgIID.SystemId, + PortRangeMin: fromPort, + PortRangeMax: toPort, + Protocol: rules.RuleProtocol(curProtocolType), //Caution!! + RemoteIPPrefix: allCIDR, //Caution!! + } + } + + start := call.Start() + _, err := rules.Create(securityHandler.NetworkClient, createRuleOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create New Rule to the S/G : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + cblogger.Infof("Succeeded in Adding New [%s], [%s] Rule!!", curRule.Direction, curProtocolType) + } + } else { + return irs.SecurityInfo{}, errors.New("To Specify 'All Traffic Allow Rule', Specify '-1' as FromPort/ToPort!!") + } + } else { + // Add SecurityGroup Rules if not 'All Traffic Open Rule' + var direction string + if strings.EqualFold(curRule.Direction, "inbound") { + direction = string(rules.DirIngress) + } else if strings.EqualFold(curRule.Direction, "outbound") { + direction = string(rules.DirEgress) + } else { + return irs.SecurityInfo{}, errors.New("Invalid Rule Direction!!") + } + + var createRuleOpts rules.CreateOpts + + if strings.EqualFold(curRule.IPProtocol, "icmp") { + createRuleOpts = rules.CreateOpts{ + Direction: rules.RuleDirection(direction), + EtherType: rules.EtherType4, + SecGroupID: sgIID.SystemId, + Protocol: rules.RuleProtocol(strings.ToLower(curRule.IPProtocol)), + RemoteIPPrefix: curRule.CIDR, + } + } else { + var fromPort int + var toPort int + if (curRule.FromPort == "-1") || (curRule.ToPort == "-1") { + fromPort = 1 + toPort = 65535 + } else { + fromPort, _ = strconv.Atoi(curRule.FromPort) + toPort, _ = strconv.Atoi(curRule.ToPort) + } + + createRuleOpts = rules.CreateOpts{ + Direction: rules.RuleDirection(direction), + EtherType: rules.EtherType4, + SecGroupID: sgIID.SystemId, + PortRangeMin: fromPort, + PortRangeMax: toPort, + Protocol: rules.RuleProtocol(strings.ToLower(curRule.IPProtocol)), + RemoteIPPrefix: curRule.CIDR, + } + } + + start := call.Start() + _, err := rules.Create(securityHandler.NetworkClient, createRuleOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create New Rule to the S/G : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + // Note : OpenStack Bug : Sometimes this function makes an error (After adding a rule successfully ) like : "Security group rule already exists. Rule id is ~~~~~~~." + // Ref) https://bugzilla.redhat.com/show_bug.cgi?id=1786675 + cblogger.Infof("Succeeded in Adding New [%s], [%s] Rule!!", curRule.Direction, rules.RuleProtocol(strings.ToLower(curRule.IPProtocol))) + } + } + + // Return Current SecurityGroup Info. + securityInfo, err := securityHandler.GetSecurity(sgIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the S/G Info : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SecurityInfo{}, newErr + } + + // // AddServer will associate a server and a security group, enforcing the rules of the group on the server. + // addServerResult := secgroups.AddServer(securityHandler.VMClient, serverID, securityIID.NameId) + + // // RemoveServer will disassociate a server from a security grou + // removeServerResult := secgroups.RemoveServer(securityHandler.VMClient, serverID, securityIID.NameId) + + return securityInfo, nil +} + +func (securityHandler *NhnCloudSecurityHandler) RemoveRules(sgIID irs.IID, securityRules *[]irs.SecurityRuleInfo) (bool, error) { + cblogger.Info("NHN Cloud Driver: called RemoveRules()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, sgIID.SystemId, "RemoveRules()") + + if sgIID.SystemId == "" { + newErr := fmt.Errorf("Invalid S/G SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + // Check if the S/G exists + sgInfo, err := securityHandler.GetSecurity(sgIID) + if err != nil { + newErr := fmt.Errorf("Failed to Find any S/G info. with the SystemId : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + cblogger.Infof("S/G SystemId to Remove the Rules [%s]", sgInfo.IId.SystemId) + + // Deletge the given S/G Rules + for _, curRule := range *securityRules { + if curRule.Direction == "" { + return false, errors.New("Failed to Find 'Direction' Value in the requested rule!!") + } else if curRule.IPProtocol == "" { + return false, errors.New("Failed to Find 'IPProtocol' Value in the requested rule!!") + } else if curRule.FromPort == "" { + return false, errors.New("Failed to Find 'FromPort' Value in the requested rule!!") + } else if curRule.ToPort == "" { + return false, errors.New("Failed to Find 'ToPort' Value in the requested rule!!") + } else if curRule.CIDR == "" { + return false, errors.New("Failed to Find 'CIDR' Value in the requested rule!!") + } + + cblogger.Infof("curRule.IPProtocol : [%s]", curRule.IPProtocol) + + if strings.EqualFold(curRule.IPProtocol, "ALL") { // Add SecurityGroup Rules in case of 'All Traffic Open Rule' + if strings.EqualFold(curRule.FromPort, "-1") && strings.EqualFold(curRule.ToPort, "-1") { + var direction string + if strings.EqualFold(curRule.Direction, "inbound") { + direction = "inbound" + } else if strings.EqualFold(curRule.Direction, "outbound") { + direction = "outbound" + } else { + return false, errors.New("Invalid Rule Direction!!") + } + + allProtocolTypeCode := []string {"tcp", "udp", "icmp"} + allCIDR := "0.0.0.0/0" + + for _, curProtocolType := range allProtocolTypeCode { + var ruleInfo irs.SecurityRuleInfo + if strings.EqualFold(curProtocolType, "icmp") { + ruleInfo = irs.SecurityRuleInfo { + Direction: direction, + IPProtocol: curProtocolType, + FromPort: "-1", + ToPort: "-1", + CIDR: allCIDR, + } + } else { + ruleInfo = irs.SecurityRuleInfo { + Direction: direction, + IPProtocol: curProtocolType, + FromPort: "1", + ToPort: "65535", + CIDR: allCIDR, + } + } + + // Get the Rule ID from the S/G + ruleId, err := securityHandler.GetRuleIdFromRuleInfo(sgIID, ruleInfo) + if err != nil { + newErr := fmt.Errorf("Failed to Find any S/G info. with the SystemId : [%s] : [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + cblogger.Infof("The RuleID of Current Rule : ", ruleId) + + // Delete the Rule + start := call.Start() + delResult := rules.Delete(securityHandler.NetworkClient, ruleId) + LoggingInfo(callLogInfo, start) + if delResult.Err != nil { + newErr := fmt.Errorf("Failed to Remove Rules of the S/G : [%s] : [%v]", sgIID.SystemId, delResult.Err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + // spew.Dump(delResult) + + cblogger.Infof("Succeeded in Removing the [%s], [%s] Rule!!", direction, curProtocolType) + } + } else { + return false, errors.New("To Specify 'All Traffic Allow Rule', Specify '-1' as FromPort/ToPort!!") + } + } else { + // Get the Rule ID from the S/G + ruleId, err := securityHandler.GetRuleIdFromRuleInfo(sgIID, curRule) + if err != nil { + newErr := fmt.Errorf("Failed to Find any S/G info. with the SystemId : [%s], [%v]", sgIID.SystemId, err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + cblogger.Infof("The RuleID of Current Rule : ", ruleId) + + // Delete the Rule + start := call.Start() + delResult := rules.Delete(securityHandler.NetworkClient, ruleId) + if delResult.Err != nil { + newErr := fmt.Errorf("Failed to Remove Rules of the S/G : [%s] : [%v]", sgIID.SystemId, delResult.Err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + LoggingInfo(callLogInfo, start) + // spew.Dump(delResult) + + cblogger.Infof("Succeeded in Removing the [%s], [%s] Rule!!", curRule.Direction, curRule.IPProtocol) + } + } + + // // AddServer will associate a server and a security group, enforcing the rules of the group on the server. + // addServerResult := secgroups.AddServer(securityHandler.VMClient, serverID, securityIID.NameId) + + // // RemoveServer will disassociate a server from a security group + // removeServerResult := secgroups.RemoveServer(securityHandler.VMClient, serverID, securityIID.NameId) + + return true, nil +} + +func (securityHandler *NhnCloudSecurityHandler) OpenOutboundAllProtocol(sgIID irs.IID) (error) { + cblogger.Info("NHN Cloud driver: called OpenOutboundAllProtocol()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, sgIID.SystemId, "OpenOutboundAllProtocol()") + + reqRules := []irs.SecurityRuleInfo { + { + Direction: "outbound", + IPProtocol: "ALL", + FromPort: "-1", + ToPort: "-1", + CIDR: "0.0.0.0/0", + }, + } + + _, err := securityHandler.AddRules(sgIID, &reqRules) + if err != nil { + newErr := fmt.Errorf("Failed to Add Outbound All Protocol Opening Rule. : [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return newErr + } + + return nil +} + +func (securityHandler *NhnCloudSecurityHandler) MappingSecurityInfo(nhnSG secgroups.SecurityGroup, defaultVPCSystemId string) (*irs.SecurityInfo, error) { + cblogger.Info("NHN Cloud Driver: called MappingSecurityInfo()!") + // spew.Dump(nhnSG) + + secInfo := &irs.SecurityInfo{ + IId: irs.IID{ + NameId: nhnSG.Name, + SystemId: nhnSG.ID, + }, + + VpcIID: irs.IID { + NameId: "", + SystemId: defaultVPCSystemId, + }, + + KeyValueList: []irs.KeyValue{ + {Key: "TenantID", Value: nhnSG.TenantID}, + }, + } + + listOpts := rules.ListOpts{ + SecGroupID: nhnSG.ID, + } + + allPages, err := rules.List(securityHandler.NetworkClient, listOpts).AllPages() + if err != nil { + cblogger.Error(err.Error()) + return nil, err + } + + nhnRuleList, err := rules.ExtractRules(allPages) + if err != nil { + cblogger.Error(err.Error()) + return nil, err + } + + if len(nhnRuleList) < 1 { + cblogger.Infof("$$$ The S/G [%s] contains No Rule!!", nhnSG.ID) + // return nil, nil // Caution!! + } else { + // Set Security Rule info. list + var sgRuleList []irs.SecurityRuleInfo + for _, nhnRule := range nhnRuleList { + if !strings.EqualFold(nhnRule.Protocol, "") { // Since on NHN Cloud Console ... + var direction string + if strings.EqualFold(nhnRule.Direction, string(rules.DirIngress)) { + direction = "inbound" + } else if strings.EqualFold(nhnRule.Direction, string(rules.DirEgress)) { + direction = "outbound" + } else { + return nil, errors.New("Invalid Rule Direction!!") + } + + ruleInfo := irs.SecurityRuleInfo{ + Direction: direction, + IPProtocol: strings.ToLower(nhnRule.Protocol), + CIDR: nhnRule.RemoteIPPrefix, + } + + if strings.EqualFold(nhnRule.Protocol, "icmp") { + ruleInfo.FromPort = "-1" + ruleInfo.ToPort = "-1" + } else { + ruleInfo.FromPort = strconv.Itoa(nhnRule.PortRangeMin) + ruleInfo.ToPort = strconv.Itoa(nhnRule.PortRangeMax) + } + + sgRuleList = append(sgRuleList, ruleInfo) + } + } + + secInfo.SecurityRules = &sgRuleList + } + return secInfo, nil +} + +func (securityHandler *NhnCloudSecurityHandler) GetRuleIdFromRuleInfo(sgIID irs.IID, givenRule irs.SecurityRuleInfo) (string, error) { + cblogger.Info("NHN Cloud Driver: called GetRuleIdFromRuleInfo()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.SECURITYGROUP, sgIID.SystemId, "GetRuleIdFromRuleInfo()") + + // Get NHN Cloud S/G Raw Info + nhnSG, err := secgroups.Get(securityHandler.VMClient, sgIID.SystemId).Extract() + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return "", err + } + + listOpts := rules.ListOpts{ + SecGroupID: nhnSG.ID, + } + + allPages, err := rules.List(securityHandler.NetworkClient, listOpts).AllPages() + if err != nil { + cblogger.Error(err.Error()) + return "", err + } + + nhnRuleList, err := rules.ExtractRules(allPages) + if err != nil { + cblogger.Error(err.Error()) + return "", err + } + // spew.Dump(nhnRuleList) + + var ruleId string + + if len(nhnRuleList) < 1 { + cblogger.Infof("$$$ The S/G [%s] contains No Rule!!", nhnSG.ID) + return "", nil // Caution!! + } else { + // Set Security Rule info. list + for _, nhnRule := range nhnRuleList { + if !strings.EqualFold(nhnRule.Protocol, "") { // Since on NHN Cloud Console ... + var direction string + if strings.EqualFold(nhnRule.Direction, string(rules.DirIngress)) { + direction = "inbound" + } else if strings.EqualFold(nhnRule.Direction, string(rules.DirEgress)) { + direction = "outbound" + } else { + return "", errors.New("Invalid Rule Direction!!") + } + + var fromPort string + var toPort string + if strings.EqualFold(nhnRule.Protocol, "icmp") { + fromPort = "-1" // Caution : Not strconv.Itoa(0) + toPort = "-1" // Caution : Not strconv.Itoa(0) + } else { + fromPort = strconv.Itoa(nhnRule.PortRangeMin) + toPort = strconv.Itoa(nhnRule.PortRangeMax) + } + + if strings.EqualFold(givenRule.Direction, direction) && strings.EqualFold(givenRule.IPProtocol, nhnRule.Protocol) && strings.EqualFold(givenRule.FromPort, fromPort) && strings.EqualFold(givenRule.ToPort, toPort) && strings.EqualFold(givenRule.CIDR, nhnRule.RemoteIPPrefix) { + ruleId = nhnRule.ID + break + } + } + } + } + + if strings.EqualFold(ruleId, "") { + return "", errors.New("Failed to Find RuleID with the Given S/G Rule!!") + } + return ruleId, nil +} + +func (securityHandler *NhnCloudSecurityHandler) GetDefaultVPCSystemID() (string, error) { + cblogger.Info("NHN Cloud Cloud Driver: called GetDefaultVPCSystemID()!") + callLogInfo := GetCallLogScheme(securityHandler.RegionInfo.Region, call.VPCSUBNET, "GetDefaultVPCSystemID()", "GetDefaultVPCSystemID()") + + var vpcSystemId string + + listOpts := external.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + } + + start := call.Start() + allPages, err := networks.List(securityHandler.NetworkClient, listOpts).AllPages() + if err != nil { + cblogger.Errorf("Failed to Get Network list from NHN Cloud. : [%v]", err) + LoggingError(callLogInfo, err) + return "", err + } + LoggingInfo(callLogInfo, start) + + // To Get VPC info list + var vpcList []NetworkWithExt + err = networks.ExtractNetworksInto(allPages, &vpcList) + if err != nil { + cblogger.Errorf("Failed to Get VPC list from NHN Cloud. : [%v]", err) + LoggingError(callLogInfo, err) + return "", err + } + + for _, vpc := range vpcList { + if strings.EqualFold(vpc.Name, "Default Network") { + vpcSystemId = vpc.ID + cblogger.Infof("# SystemId of the Default VPC : [%s]", vpcSystemId) + break + } + } + + // When the "Default Network" VPC is not found + if strings.EqualFold(vpcSystemId, "") { + newErr := fmt.Errorf("Failed to Find the 'Default Network' VPC on your NHN Cloud project!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return "", newErr + } + return vpcSystemId, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMHandler.go new file mode 100644 index 000000000..f5f011531 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMHandler.go @@ -0,0 +1,1000 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI, 2022.03. + +package resources + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/blockstorage/v2/volumes" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/bootfromvolume" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/floatingips" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/keypairs" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/extensions/startstop" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/flavors" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/servers" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/images" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +const ( + DefaultVMUserName string = "cb-user" + CloudInitFilePath string = "/cloud-driver-libs/.cloud-init-nhncloud/cloud-init" + DefaultDiskSize string = "20" +) + +type NhnCloudVMHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient + ImageClient *nhnsdk.ServiceClient + NetworkClient *nhnsdk.ServiceClient + VolumeClient *nhnsdk.ServiceClient +} + +func (vmHandler *NhnCloudVMHandler) StartVM(vmReqInfo irs.VMReqInfo) (irs.VMInfo, error) { + cblogger.Info("NHN Cloud Driver: called StartVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmReqInfo.IId.NameId, "StartVM()") + + if strings.EqualFold(vmReqInfo.IId.NameId, "") { + newErr := fmt.Errorf("Invalid VM NameId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // Check if VM Name is Duplicated + listOpts := servers.ListOpts{Name: vmReqInfo.IId.NameId} + allPages, err := servers.List(vmHandler.VMClient, listOpts).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get VM with the name : %s", vmReqInfo.IId.NameId) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + vmList, err := servers.ExtractServers(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Get VM Info with the name : %s", vmReqInfo.IId.NameId) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + if len(vmList) != 0 { + newErr := fmt.Errorf("The VM Name [%s] already exists!!", vmReqInfo.IId.NameId) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // Get VM SpecId with the name + vmSpecId, err := GetVMSpecIdWithName(vmHandler.VMClient, vmReqInfo.VMSpecName) + if err != nil { + newErr := fmt.Errorf("Failed to Get VMSpec ID with the name : %v", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + cblogger.Infof("# vmSpecId : [%s]", vmSpecId) + + // Get SecurityGroupId list + var sgIdList []string + for _, sgIID := range vmReqInfo.SecurityGroupIIDs { + sgIdList = append(sgIdList, sgIID.SystemId) + } + + // Get KeyPair Info (to Get PublicKey info for cloud-init) + var getOptsBuilder keypairs.GetOptsBuilder + keyPair, err := keypairs.Get(vmHandler.VMClient, vmReqInfo.KeyPairIID.NameId, getOptsBuilder).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get KeyPair Info. with the name : %v", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // Set cloud-init script + rootPath := os.Getenv("CBSPIDER_ROOT") + fileData, err := os.ReadFile(rootPath + CloudInitFilePath) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return irs.VMInfo{}, err + } + fileStr := string(fileData) + fileStr = strings.ReplaceAll(fileStr, "{{username}}", DefaultVMUserName) + fileStr = strings.ReplaceAll(fileStr, "{{public_key}}", keyPair.PublicKey) + + // Preparing VM Creation Options + serverCreateOpts := servers.CreateOpts{ + Name: vmReqInfo.IId.NameId, + SecurityGroups: sgIdList, + ImageRef: vmReqInfo.ImageIID.SystemId, + FlavorRef: vmSpecId, + Networks: []servers.Network{ + {UUID: vmReqInfo.VpcIID.SystemId}, + }, + AvailabilityZone: vmHandler.RegionInfo.Zone, + UserData: []byte(fileStr), // Apply cloud-init script + } + + // Add KeyPair Name + createOpts := keypairs.CreateOptsExt{ + KeyName: vmReqInfo.KeyPairIID.NameId, + } + + nhnVMSpecType := vmReqInfo.VMSpecName[:2] // Ex) u2 or m2 or c2 ... + cblogger.Infof("# nhnVMSpecType : [%s]", nhnVMSpecType) + + reqDiskType := vmReqInfo.RootDiskType // 'default', 'General_HDD' or 'General_SSD' + reqDiskSize := vmReqInfo.RootDiskSize + + // Set VM RootDiskType + if strings.EqualFold(reqDiskType, "General_HDD") { + reqDiskType = HDD // "General HDD" + } else if strings.EqualFold(reqDiskType, "General_SSD") { + reqDiskType = SSD // "General SSD" + } + + // In case, Volume Type is not specified. + if strings.EqualFold(reqDiskType, "") || strings.EqualFold(reqDiskType, "default") { + reqDiskType = HDD + } + + // When Volume Type is Incorrect + if strings.EqualFold(nhnVMSpecType, "u2") && !strings.EqualFold(reqDiskType, HDD) { + newErr := fmt.Errorf("Invalid RootDiskType!! Specified VMSpec [%s] supports only 'default' or 'General_HDD' RootDiskType!!", vmReqInfo.VMSpecName) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + if strings.EqualFold(nhnVMSpecType, "u2") && (!strings.EqualFold(reqDiskSize, "") && !strings.EqualFold(reqDiskSize, "default")) { + + vmSpecHandler := NhnCloudVMSpecHandler{ + RegionInfo: vmHandler.RegionInfo, + VMClient: vmHandler.VMClient, + } + vmSpec, err := vmSpecHandler.GetVMSpec(vmReqInfo.VMSpecName) // Check vmSpec info. + if err != nil { + newErr := fmt.Errorf("Failed to Get VMSpec Info. with the name : %v", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // Use Key/Value info of the vmSpec Info. + var localDisk string + for _, keyInfo := range vmSpec.KeyValueList { + if strings.EqualFold(keyInfo.Key, "LocalDiskSize(GB)") { + localDisk = keyInfo.Value + break + } + } + + if reqDiskSize != localDisk { + newErr := fmt.Errorf("Invalid RootDiskSize!! Specified VMSpec [%s] supports only [%s](GB)!!", vmReqInfo.VMSpecName, localDisk) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } + } + + if nhnVMSpecType != "u2" && (reqDiskType != HDD && reqDiskType != SSD) { + newErr := fmt.Errorf("Invalid RootDiskType!! Must be 'default', 'General_HDD' or 'General_SSD'") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // Set VM RootDiskSize + // When Volume Size is not specified. + if strings.EqualFold(reqDiskSize, "") || strings.EqualFold(reqDiskSize, "default") { + reqDiskSize = DefaultDiskSize + } + + reqDiskSizeInt, err := strconv.Atoi(reqDiskSize) + if err != nil { + newErr := fmt.Errorf("Failed to Convert diskSize to int type. [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + // When req Volume Size must be more than 20GB and less than 1000GB + if nhnVMSpecType != "u2" && (reqDiskSizeInt < 20 || reqDiskSizeInt > 1000) { + newErr := fmt.Errorf("Specified Invalid RootDiskSize!! RootDiskSize range should be 20 to 1000(GB)!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + start := call.Start() + createOpts.CreateOptsBuilder = serverCreateOpts + + var newNhnVM *servers.Server + if strings.EqualFold(nhnVMSpecType, "u2") { // Only HDD and Default RootDiskSize according to the VMSpec + newNhnVM, err = servers.Create(vmHandler.VMClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create a VM with the Local Disk!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + } else { + blockDeviceSet := []bootfromvolume.BlockDevice{ + { + UUID: vmReqInfo.ImageIID.SystemId, + SourceType: bootfromvolume.SourceImage, + VolumeType: reqDiskType, + VolumeSize: reqDiskSizeInt, + DestinationType: bootfromvolume.DestinationVolume, // Destination_type must be 'Volume'. Not 'bootfromvolume.DestinationLocal' + DeleteOnTermination: true, + }, + } + + bootOpts := bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: createOpts, + BlockDevice: blockDeviceSet, + } + newNhnVM, err = bootfromvolume.Create(vmHandler.VMClient, bootOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create a VM with the Block Storage Volume!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + } + LoggingInfo(callLogInfo, start) + + // Because there are functions that use NameID, input NameId too + newVMIID := irs.IID{NameId: vmReqInfo.IId.NameId, SystemId: newNhnVM.ID} + + // Wait for created VM info to be inquired + curStatus, errStatus := vmHandler.WaitToGetVMInfo(newVMIID) + if errStatus != nil { + newErr := fmt.Errorf("Failed to Wait to Get VM Info!! [%v]", errStatus) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + cblogger.Infof("==> VM status of [%s] : [%s]", newVMIID.NameId, curStatus) + + // Set Disk Attachment Info + diskHandler := NhnCloudDiskHandler{ + RegionInfo: vmHandler.RegionInfo, + VMClient: vmHandler.VMClient, + VolumeClient: vmHandler.VolumeClient, + } + if len(vmReqInfo.DataDiskIIDs) != 0 { + for _, DataDiskIID := range vmReqInfo.DataDiskIIDs { + _, err := diskHandler.AttachDisk(DataDiskIID, newVMIID) + if err != nil { + newErr := fmt.Errorf("Failed to Attach the Disk Volume to the VM!! [%v]", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + cblogger.Infof("# Disk [%s] Attached Successfully!!", DataDiskIID.SystemId) + } + } + + // To Check VM Deployment Status + nhnVM, getErr := servers.Get(vmHandler.VMClient, newNhnVM.ID).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get VMInfo : [%v]", getErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + vmInfo := irs.VMInfo{} + if strings.EqualFold(nhnVM.Status, "active") { + // Associate Public IP to the VM + if ok, err := vmHandler.AssociatePublicIP(nhnVM.ID); !ok { + newErr := fmt.Errorf("Failed to Start VM. Failed to Associate PublicIP : %v", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + // Get Final VM info + nhnVM, err := servers.Get(vmHandler.VMClient, nhnVM.ID).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get New VM Info. %s", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + var mappingErr error + vmInfo, mappingErr = vmHandler.MappingVMInfo(*nhnVM) + if mappingErr != nil { + newErr := fmt.Errorf("Failed to Map New VM Info. %s", mappingErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + } + return vmInfo, nil +} + +func (vmHandler *NhnCloudVMHandler) SuspendVM(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("NHN Cloud Driver: called SuspendVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.SystemId, "SuspendVM()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + + var resultStatus string + + cblogger.Info("Start Get VM Status...") + vmStatus, err := vmHandler.GetVMStatus(vmIID) + if err != nil { + cblogger.Errorf("[%s] Failed to Get the VM Status of VM : ", vmIID.SystemId) + cblogger.Error(err) + LoggingError(callLogInfo, err) + return irs.VMStatus("Failed to Get the VM Status of VM. "), err + } else { + cblogger.Infof("Succeeded in Getting the VM Status of [%s] : [%s]", vmIID.SystemId, vmStatus) + } + + if strings.EqualFold(string(vmStatus), "Suspended") { + resultStatus = "The VM has already been Suspended." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Rebooting") { + resultStatus = "The VM is in the process of Rebooting." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Deleted") { + resultStatus = "The VM has been Deleted." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Creating") { + resultStatus = "The VM is in the process of Creating." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Terminating") { + resultStatus = "The VM is in the process of Terminating." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else { + start := call.Start() + err := startstop.Stop(vmHandler.VMClient, vmIID.SystemId).Err + if err != nil { + newErr := fmt.Errorf("Failed to Suspend the VM!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + LoggingInfo(callLogInfo, start) + } + + return irs.Suspending, nil +} + +func (vmHandler *NhnCloudVMHandler) ResumeVM(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("NHN Cloud Driver: called ResumeVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.NameId, "ResumeVM()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + + var resultStatus string + + cblogger.Info("Start Get VM Status...") + vmStatus, err := vmHandler.GetVMStatus(vmIID) + if err != nil { + cblogger.Errorf("Failed to Get the VM Status of : [%s]", vmIID.SystemId) + cblogger.Error(err) + LoggingError(callLogInfo, err) + return irs.VMStatus("Failed. "), err + } else { + cblogger.Infof("Succeeded in Getting the VM Status of [%s] : [%s]", vmIID.SystemId, vmStatus) + } + + if strings.EqualFold(string(vmStatus), "Running") { + resultStatus = "The VM is Running. Cannot be Resumed!!" + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Rebooting") { + resultStatus = "The VM is in the process of Rebooting. Cannot be Resumed" + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Terminating") { + resultStatus = "The VM is already in the process of Terminating. Cannot be Resumed" + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Deleted") { + resultStatus = "The VM has been Deleted. Cannot be Resumed" + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Creating") { + resultStatus = "The VM is in the process of Creating. Cannot be Resumed" + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else { + start := call.Start() + err := startstop.Start(vmHandler.VMClient, vmIID.SystemId).Err + if err != nil { + newErr := fmt.Errorf("Failed to Start the VM!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + LoggingInfo(callLogInfo, start) + + return irs.Resuming, nil + } +} + +func (vmHandler *NhnCloudVMHandler) RebootVM(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("NHN Cloud Driver: called RebootVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.SystemId, "RebootVM()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + + cblogger.Info("Start Get VM Status...") + vmStatus, err := vmHandler.GetVMStatus(vmIID) + if err != nil { + cblogger.Errorf("[%s] Failed to Get the VM Status.", vmIID) + cblogger.Error(err) + LoggingError(callLogInfo, err) + return irs.VMStatus("Failed to Get the VM Status."), err + } else { + cblogger.Infof("Succeeded in Getting the VM Status of [%s] : [%s]", vmIID, vmStatus) + } + + var resultStatus string + + if strings.EqualFold(string(vmStatus), "Suspended") { + resultStatus = "The VM had been Suspended." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Rebooting") { + resultStatus = "The VM is already in the process of Rebooting." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Deleted") { + resultStatus = "The VM has been Deleted." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Booting") { + resultStatus = "The VM is in the process of Booting." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Creating") { + resultStatus = "The VM is in the process of Creating." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else if strings.EqualFold(string(vmStatus), "Terminating") { + resultStatus = "The VM is in the process of Terminating." + cblogger.Error(resultStatus) + return irs.VMStatus("Failed. " + resultStatus), err + } else { + start := call.Start() + rebootOpts := servers.RebootOpts{ + Type: servers.SoftReboot, + } + + err := servers.Reboot(vmHandler.VMClient, vmIID.SystemId, rebootOpts).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Reboot the VM!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + LoggingInfo(callLogInfo, start) + + return irs.Rebooting, nil + } +} + +func (vmHandler *NhnCloudVMHandler) TerminateVM(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("NHN Cloud Driver: called TerminateVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.SystemId, "TerminateVM()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + + server, err := vmHandler.GetVM(vmIID) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return irs.Failed, err + } + + allPages, err := floatingips.List(vmHandler.VMClient).AllPages() + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return irs.Failed, err + } + publicIPList, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return irs.Failed, err + } + + var publicIPId string + for _, p := range publicIPList { + if strings.EqualFold(server.PublicIP, p.IP) { + publicIPId = p.ID + break + } + } + + if publicIPId != "" { + err := floatingips.Delete(vmHandler.VMClient, publicIPId).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Delete the Floating IP!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + } + + start := call.Start() + err = servers.Delete(vmHandler.VMClient, server.IId.SystemId).ExtractErr() + if err != nil { + newErr := fmt.Errorf("Failed to Terminate the VM!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + LoggingInfo(callLogInfo, start) + + return irs.Terminating, nil +} + +func (vmHandler *NhnCloudVMHandler) ListVMStatus() ([]*irs.VMStatusInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListVMStatus()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, "ListVMStatus()", "ListVMStatus()") + + start := call.Start() + allPages, err := servers.List(vmHandler.VMClient, nil).AllPages() + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return nil, err + } + LoggingInfo(callLogInfo, start) + + servers, err := servers.ExtractServers(allPages) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return nil, err + } + + // Add to List + vmStatusList := make([]*irs.VMStatusInfo, len(servers)) + for idx, s := range servers { + vmStatus := getVmStatus(s.Status) + vmStatusInfo := irs.VMStatusInfo{ + IId: irs.IID{ + NameId: s.Name, + SystemId: s.ID, + }, + VmStatus: vmStatus, + } + vmStatusList[idx] = &vmStatusInfo + } + + return vmStatusList, nil +} + +func (vmHandler *NhnCloudVMHandler) GetVMStatus(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("NHN Cloud Driver: called GetVMStatus()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.SystemId, "GetVMStatus()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + + start := call.Start() + serverResult, err := servers.Get(vmHandler.VMClient, vmIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM info.!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.Failed, newErr + } + LoggingInfo(callLogInfo, start) + + cblogger.Infof("# serverResult.Status of NHN Cloud : [%s]", serverResult.Status) + vmStatus := getVmStatus(serverResult.Status) + return vmStatus, nil +} + +func (vmHandler *NhnCloudVMHandler) ListVM() ([]*irs.VMInfo, error) { + cblogger.Info("NHN Cloud Driver: called ListVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, "ListVM()", "ListVM()") + + start := call.Start() + listOpts := servers.ListOpts{ + Limit: 100, + } + allPages, err := servers.List(vmHandler.VMClient, listOpts).AllPages() + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return nil, err + } + serverList, err := servers.ExtractServers(allPages) + if err != nil { + cblogger.Error(err.Error()) + LoggingError(callLogInfo, err) + return nil, err + } + LoggingInfo(callLogInfo, start) + + // Mapping VM info list + var vmInfoList []*irs.VMInfo + for _, server := range serverList { + vmInfo, err := vmHandler.MappingVMInfo(server) + if err != nil { + newErr := fmt.Errorf("Failed to Map New VM Info. %s", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return nil, newErr + } + vmInfoList = append(vmInfoList, &vmInfo) + } + return vmInfoList, nil +} + +func (vmHandler *NhnCloudVMHandler) GetVM(vmIID irs.IID) (irs.VMInfo, error) { + cblogger.Info("NHN Cloud Driver: called GetVM()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, vmIID.SystemId, "GetVM()") + + if strings.EqualFold(vmIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VM SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + + start := call.Start() + nhnVM, err := servers.Get(vmHandler.VMClient, vmIID.SystemId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM info.!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + LoggingInfo(callLogInfo, start) + + vmInfo, mappingErr := vmHandler.MappingVMInfo(*nhnVM) + if mappingErr != nil { + newErr := fmt.Errorf("Failed to Map New VM Info. %s", mappingErr) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VMInfo{}, newErr + } + return vmInfo, nil +} + +func (vmHandler *NhnCloudVMHandler) AssociatePublicIP(serverID string) (bool, error) { + cblogger.Info("NHN Cloud Driver: called AssociatePublicIP()") + callLogInfo := GetCallLogScheme(vmHandler.RegionInfo.Region, call.VM, "AssociatePublicIP()", "AssociatePublicIP()") + + if strings.EqualFold(serverID, "") { + newErr := fmt.Errorf("Invalid serverID!!") + cblogger.Error(newErr.Error()) + return false, newErr + } + + // Create PublicIP + extVPCName, _ := GetPublicVPCInfo(vmHandler.NetworkClient, "NAME") + createOpts := floatingips.CreateOpts{ + Pool: extVPCName, + } + publicIP, err := floatingips.Create(vmHandler.VMClient, createOpts).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Create Public IP!! : [%v] ", err) + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return false, newErr + } + + // Associate Floating IP to the VM + curRetryCnt := 0 + maxRetryCnt := 120 + for { + associateOpts := floatingips.AssociateOpts{ + FloatingIP: publicIP.IP, + } + err = floatingips.AssociateInstance(vmHandler.VMClient, serverID, associateOpts).ExtractErr() + if err == nil { + break + } else { + newErr := fmt.Errorf("Failed to AssociateInstance the Public IP!! : [%v] ", err) + cblogger.Error(newErr.Error()) + } + + time.Sleep(1 * time.Second) + curRetryCnt++ + if curRetryCnt > maxRetryCnt { + newErr := fmt.Errorf("Failed to Associate Floating IP to VM, Exceeded Maximum Retry Count %d", maxRetryCnt) + cblogger.Error(newErr.Error()) + return false, newErr + } + } + + return true, nil +} + +func getVmStatus(vmStatus string) irs.VMStatus { + cblogger.Info("NHN Cloud Driver: called getVmStatus()") + + var resultStatus string + switch strings.ToLower(vmStatus) { + case "build": + resultStatus = "Creating" + case "active": + resultStatus = "Running" + case "shutoff": + resultStatus = "Suspended" + case "paused": + resultStatus = "Suspended" + case "reboot": + resultStatus = "Rebooting" + case "hard_reboot": + resultStatus = "Rebooting" + case "deleted": + resultStatus = "Deleted" + case "error": + resultStatus = "Error" + default: + resultStatus = "Unknown" + } + + return irs.VMStatus(resultStatus) +} + +func (vmHandler *NhnCloudVMHandler) MappingVMInfo(server servers.Server) (irs.VMInfo, error) { + cblogger.Info("NHN Cloud Driver: called MappingVMInfo()") + + // cblogger.Infof("\n\n### Server from NHN :") + // spew.Dump(server) + // cblogger.Infof("\n\n") + + vmInfo := irs.VMInfo{ + IId: irs.IID{ + NameId: server.Name, + SystemId: server.ID, + }, + Region: irs.RegionInfo{ + Region: vmHandler.RegionInfo.Region, + Zone: vmHandler.RegionInfo.Zone, + }, + KeyPairIId: irs.IID{ + NameId: server.KeyName, + SystemId: server.KeyName, + }, + VMUserId: DefaultVMUserName, + VMUserPasswd: "N/A", + NetworkInterface: server.HostID, + } + vmInfo.StartTime = server.Created + + // Image Info + imageId := server.Image["id"].(string) + nhnImage, err := images.Get(vmHandler.VMClient, imageId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the Image info form NHN Cloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if nhnImage != nil { + vmInfo.ImageIId.NameId = nhnImage.ID + vmInfo.ImageIId.SystemId = nhnImage.ID + } + + // Flavor Info + var vRam string + var vCPU string + flavorId := server.Flavor["id"].(string) + nhnFlavor, err := flavors.Get(vmHandler.VMClient, flavorId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the Flavor info form NHN Cloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if nhnFlavor != nil { + // spew.Dump(flavor) + vmInfo.VMSpecName = nhnFlavor.Name + if vmInfo.RootDiskSize == "" { // In case of u2 VMSpec type + vmInfo.RootDiskType = "General_HDD" // u2 type VMSpec only supports 'General_HHD'. + vmInfo.RootDiskSize = strconv.Itoa(nhnFlavor.Disk) + vmInfo.RootDeviceName = "/dev/vda" + } + if strconv.Itoa(nhnFlavor.VCPUs) != "" { + vCPU = strconv.Itoa(nhnFlavor.VCPUs) + } + if strconv.Itoa(nhnFlavor.RAM) != "" { + vRam = strconv.Itoa(nhnFlavor.RAM) + } + } + + // Get Disk Type, Size Info and DataDiskIIDs + var diskIIDs []irs.IID + if len(server.AttachedVolumes) != 0 { + for _, volume := range server.AttachedVolumes { + nhnVolume, err := volumes.Get(vmHandler.VolumeClient, volume.ID).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get the Volume Info form NHN Cloud!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } + if nhnVolume.Bootable == "true" { // Only For 'Root' disk + // cblogger.Info("# nhnVolume : ") + // spew.Dump(nhnVolume) + switch nhnVolume.VolumeType { + case HDD: + vmInfo.RootDiskType = "General_HDD" + case SSD: + vmInfo.RootDiskType = "General_SSD" + case "": + vmInfo.RootDiskType = "N/A" + } + + vmInfo.RootDiskSize = strconv.Itoa(nhnVolume.Size) + vmInfo.RootDeviceName = nhnVolume.Attachments[0].Device + } + + diskIIDs = append(diskIIDs, irs.IID{NameId: nhnVolume.Name, SystemId: nhnVolume.ID}) + } + } + vmInfo.DataDiskIIDs = diskIIDs + + for key, subnet := range server.Addresses { + // VPC Info + vmInfo.VpcIID.NameId = key + nhnNetwork, err := GetNetworkWithName(vmHandler.NetworkClient, vmInfo.VpcIID.NameId) + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Cloud Network Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if nhnNetwork != nil { + vmInfo.VpcIID.SystemId = nhnNetwork.ID + } + // PrivateIP, PublicIp Info + for _, addr := range subnet.([]interface{}) { + addrMap := addr.(map[string]interface{}) + if addrMap["OS-EXT-IPS:type"] == "floating" { + vmInfo.PublicIP = addrMap["addr"].(string) + } else if addrMap["OS-EXT-IPS:type"] == "fixed" { + vmInfo.PrivateIP = addrMap["addr"].(string) + } + } + } + + // Subnet, Network Interface Info + nhnPort, err := GetPortWithDeviceId(vmHandler.NetworkClient, vmInfo.IId.SystemId) + if err != nil { + newErr := fmt.Errorf("Failed to Get the NHN Cloud Port Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if nhnPort != nil { + // Subnet Info + if len(nhnPort.FixedIPs) > 0 { + vmInfo.SubnetIID.SystemId = nhnPort.FixedIPs[0].SubnetID + } + + nhnSubnet, err := GetSubnetWithId(vmHandler.NetworkClient, vmInfo.SubnetIID.SystemId) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Subnet Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if nhnSubnet != nil { + vmInfo.SubnetIID.NameId = nhnSubnet.Name + } + // Network Interface Info + vmInfo.NetworkInterface = nhnPort.ID + } + + // SecurityGroup Info + if len(server.SecurityGroups) != 0 { + sgIIds := make([]irs.IID, len(server.SecurityGroups)) + for i, secGroupMap := range server.SecurityGroups { + secGroupName := secGroupMap["name"].(string) + sgIIds[i] = irs.IID{ + NameId: secGroupName, + } + secGroup, err := GetSGWithName(vmHandler.VMClient, secGroupName) + if err != nil { + newErr := fmt.Errorf("Failed to Get the Security Group Info!! : [%v] ", err) + cblogger.Error(newErr.Error()) + return irs.VMInfo{}, newErr + } else if secGroup != nil { + sgIIds[i].SystemId = secGroup.ID + } + } + vmInfo.SecurityGroupIIds = sgIIds + } + + vmInfo.SSHAccessPoint = vmInfo.PublicIP + ":22" + + var keyValueList []irs.KeyValue + if vCPU != "" { + keyValue := irs.KeyValue{Key: "vCPU", Value: vCPU} + keyValueList = append(keyValueList, keyValue) + } + if vRam != "" { + keyValue := irs.KeyValue{Key: "vRAM(GB)", Value: vRam} + keyValueList = append(keyValueList, keyValue) + } + vmInfo.KeyValueList = keyValueList + return vmInfo, nil +} + +// Waiting for up to 500 seconds during VM creation until VM info. can be get +func (vmHandler *NhnCloudVMHandler) WaitToGetVMInfo(vmIID irs.IID) (irs.VMStatus, error) { + cblogger.Info("===> Since VM info. cannot be retrieved immediately after VM creation, it waits until running.") + + curRetryCnt := 0 + maxRetryCnt := 500 + for { + curStatus, err := vmHandler.GetVMStatus(vmIID) + if err != nil { + newErr := fmt.Errorf("Failed to Get the VM Status of [%s] : [%v] ", vmIID.NameId, err) + cblogger.Error(newErr.Error()) + return irs.VMStatus("Failed. "), newErr + } else { + cblogger.Infof("Succeeded in Getting the VM Status of [%s] : [%s]", vmIID.NameId, curStatus) + } + + cblogger.Info("===> VM Status : ", curStatus) + + switch string(curStatus) { + case "Creating", "Booting": + curRetryCnt++ + cblogger.Infof("The VM is still 'Creating', so wait for a second more before inquiring the VM info.") + time.Sleep(time.Second * 2) + if curRetryCnt > maxRetryCnt { + newErr := fmt.Errorf("Despite waiting for a long time(%d sec), the VM status is %s, so it is forcibly finished.", maxRetryCnt, curStatus) + cblogger.Error(newErr.Error()) + return irs.VMStatus("Failed. "), newErr + } + default: + cblogger.Infof("===> ### The VM Creation is finished, stopping the waiting.") + return irs.VMStatus(curStatus), nil + //break + } + } +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMSpecHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMSpecHandler.go new file mode 100644 index 000000000..00190c994 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VMSpecHandler.go @@ -0,0 +1,210 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI 2022.03 updated. + +package resources + +import ( + "fmt" + "strconv" + "strings" + // "errors" + // "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/compute/v2/flavors" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudVMSpecHandler struct { + RegionInfo idrv.RegionInfo + VMClient *nhnsdk.ServiceClient +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) ListVMSpec() ([]*irs.VMSpecInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called ListVMSpec()!") + callLogInfo := GetCallLogScheme(vmSpecHandler.RegionInfo.Region, call.VMSPEC, "ListVMSpec()", "ListVMSpec()") + + listOpts := flavors.ListOpts{ + Limit: 100, // Note) default : 20 + } + start := call.Start() + allPages, err := flavors.ListDetail(vmSpecHandler.VMClient, listOpts).AllPages() + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Flavor List : ", err) + return nil, rtnErr + } + specList, err := flavors.ExtractFlavors(allPages) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Extract Flavors : ", err) + return nil, rtnErr + } + LoggingInfo(callLogInfo, start) + + var vmSpecInfoList []*irs.VMSpecInfo + for _, vmSpec := range specList { + vmSpecInfo := vmSpecHandler.MappingVMSpecInfo(vmSpec) + vmSpecInfoList = append(vmSpecInfoList, vmSpecInfo) + } + return vmSpecInfoList, nil +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) GetVMSpec(specName string) (irs.VMSpecInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called GetVMSpec()!") + callLogInfo := GetCallLogScheme(vmSpecHandler.RegionInfo.Region, call.VMSPEC, specName, "GetVMSpec()") + + if strings.EqualFold(specName, "") { + rtnErr := logAndReturnError(callLogInfo, "Invalid vmSpec Name!!", nil) + return irs.VMSpecInfo{}, rtnErr + } + + flavorId, err := vmSpecHandler.getIDFromName(specName) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Flavor ID From the Name : ", err) + return irs.VMSpecInfo{}, rtnErr + } + start := call.Start() + flavor, err := flavors.Get(vmSpecHandler.VMClient, flavorId).Extract() + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get NHN Flavor Info : ", err) + return irs.VMSpecInfo{}, rtnErr + } + LoggingInfo(callLogInfo, start) + // spew.Dump(flavor) + + vmSpecInfo := vmSpecHandler.MappingVMSpecInfo(*flavor) + return *vmSpecInfo, nil +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) ListOrgVMSpec() (string, error) { + cblogger.Info("NHN Cloud Cloud Driver: called ListOrgVMSpec()!") + callLogInfo := GetCallLogScheme(vmSpecHandler.RegionInfo.Region, call.VMSPEC, "ListOrgVMSpec()", "ListOrgVMSpec()") + + start := call.Start() + allPages, err := flavors.ListDetail(vmSpecHandler.VMClient, flavors.ListOpts{}).AllPages() + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get flavor List : ", err) + return "", rtnErr + } + flavorList, err := flavors.ExtractFlavors(allPages) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Extract Flavors : ", err) + return "", rtnErr + } + LoggingInfo(callLogInfo, start) + + var flvList struct { + Result []flavors.Flavor `json:"flavorList"` + } + flvList.Result = flavorList + + jsonString, err := ConvertJsonString(flvList) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Convert to Json String : ", err) + return "", rtnErr + } + return jsonString, nil +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) GetOrgVMSpec(specName string) (string, error) { + cblogger.Info("NHN Cloud Cloud Driver: called GetOrgVMSpec()!") + callLogInfo := GetCallLogScheme(vmSpecHandler.RegionInfo.Region, call.VMSPEC, specName, "GetOrgVMSpec()") + + if strings.EqualFold(specName, "") { + rtnErr := logAndReturnError(callLogInfo, "Invalid vmSpec Name!!", nil) + return "", rtnErr + } + + flavorId, err := vmSpecHandler.getIDFromName(specName) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Get Flavor ID From the Name : ", err) + return "", rtnErr + } + start := call.Start() + flavor, err := flavors.Get(vmSpecHandler.VMClient, flavorId).Extract() + if err != nil { + newErr := fmt.Errorf("Failed to Get NHN Flavor Info : [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + LoggingInfo(callLogInfo, start) + + var nhnFlavor struct { + Result flavors.Flavor `json:"flavor"` + } + nhnFlavor.Result = *flavor + + jsonString, err := ConvertJsonString(nhnFlavor) + if err != nil { + rtnErr := logAndReturnError(callLogInfo, "Failed to Convert to Json String : ", err) + return "", rtnErr + } + return jsonString, nil +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) MappingVMSpecInfo(vmSpec flavors.Flavor) *irs.VMSpecInfo { + cblogger.Info("NHN Cloud Cloud Driver: called MappingVMSpecInfo()!") + + vmSpecInfo := &irs.VMSpecInfo { + Region: vmSpecHandler.RegionInfo.Region, + Name: vmSpec.Name, + VCpu: irs.VCpuInfo{Count: strconv.Itoa(vmSpec.VCPUs), Clock: "N/A"}, + Mem: strconv.Itoa(vmSpec.RAM), + Gpu: []irs.GpuInfo{{Count: "N/A", Mfr: "N/A", Model: "N/A", Mem: "N/A"}}, + + KeyValueList: []irs.KeyValue{ + {Key: "Region", Value: vmSpecHandler.RegionInfo.Region}, + {Key: "LocalDiskSize(GB)", Value: strconv.Itoa(vmSpec.Disk)}, + }, + } + + if strconv.Itoa(vmSpec.Disk) == "0" { + keyValue := irs.KeyValue { + Key : "Notice!!", + Value : "Specify 'RootDiskType' and 'RootDiskSize' when VM Creation to Boot from the Attached Volume!!", + } + vmSpecInfo.KeyValueList = append(vmSpecInfo.KeyValueList, keyValue) + } + return vmSpecInfo +} + +func (vmSpecHandler *NhnCloudVMSpecHandler) getIDFromName(specName string) (string, error) { + cblogger.Info("NHN Cloud Cloud Driver: called getIDFromName()!") + + var flavorNameList []flavors.Flavor + + allPages, err := flavors.ListDetail(vmSpecHandler.VMClient, flavors.ListOpts{}).AllPages() + if err != nil { + newErr := fmt.Errorf("Failed to Get flavors.List : [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + flavorList, err := flavors.ExtractFlavors(allPages) + if err != nil { + newErr := fmt.Errorf("Failed to Extract Flavor List : [%v]", err) + cblogger.Error(newErr.Error()) + return "", newErr + } + for _, flavor := range flavorList { + if strings.EqualFold(flavor.Name, specName) { + flavorNameList = append(flavorNameList, flavor) + } + } + + if len(flavorNameList) > 1 { + return "", fmt.Errorf("Found multiple vmSpec with the name %s", specName) + } else if len(flavorNameList) == 0 { + return "", fmt.Errorf("Failed to Find vmSpec with the name %s", specName) + } + return flavorNameList[0].ID, nil +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VPCHandler.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VPCHandler.go new file mode 100644 index 000000000..92260e9fa --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/VPCHandler.go @@ -0,0 +1,368 @@ +// Proof of Concepts of CB-Spider. +// The CB-Spider is a sub-Framework of the Cloud-Barista Multi-Cloud Project. +// The CB-Spider Mission is to connect all the clouds with a single interface. +// +// * Cloud-Barista: https://github.com/cloud-barista +// +// This is a Cloud Driver Example for PoC Test. +// +// by ETRI, Innogrid, 2021.12. +// by ETRI 2022.03. updated +// by ETRI 2023.11. updated + +package resources + +import ( + "errors" + "strings" + "fmt" + "sync" + "github.com/davecgh/go-spew/spew" + + nhnsdk "github.com/cloud-barista/nhncloud-sdk-go" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/extensions/external" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/networks" + "github.com/cloud-barista/nhncloud-sdk-go/openstack/networking/v2/subnets" + + call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" + idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces" + irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" +) + +type NhnCloudVPCHandler struct { + RegionInfo idrv.RegionInfo + NetworkClient *nhnsdk.ServiceClient +} + +type NetworkWithExt struct { + networks.Network + external.NetworkExternalExt +} + +func (vpcHandler *NhnCloudVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.VPCInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called CreateVPC()!") + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, vpcReqInfo.IId.NameId, "CreateVPC()") + + if strings.EqualFold(vpcReqInfo.IId.NameId, "") { + newErr := fmt.Errorf("Invalid VPC NameId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VPCInfo{}, newErr + } + + // Get VPC list + vpcList, err := vpcHandler.ListVPC() + if err != nil { + cblogger.Errorf("Failed to Get VPC list : %v", err) + LoggingError(callLogInfo, err) + return irs.VPCInfo{}, err + } + + // Search VPC SystemId by NameID in the VPC list + var vpcSystemId string + for _, curVPC := range vpcList { + if strings.EqualFold(curVPC.IId.NameId, "Default Network") { + vpcSystemId = curVPC.IId.SystemId + cblogger.Infof("# SystemId of the VPC : [%s]", vpcSystemId) + break + } + } + + // When the "Default Network" VPC is not found + if strings.EqualFold(vpcSystemId, "") { + cblogger.Error("Failed to Find the 'Default Network' VPC on your NHN Cloud project!!") + return irs.VPCInfo{}, nil + } + + vpcInfo, err := vpcHandler.GetVPC(irs.IID{SystemId: vpcSystemId}) + if err != nil { + cblogger.Errorf("Failed to Find any VPC Info with the SystemId. : [%s], %v", vpcSystemId, err) + LoggingError(callLogInfo, err) + return irs.VPCInfo{}, err + } else { + vpcInfo.IId.NameId = vpcReqInfo.IId.NameId // Caution!! For IID2 NameID validation check for VPC + } + + // Create Subnet + var subnetList []irs.SubnetInfo + + for _, subnet := range vpcReqInfo.SubnetInfoList { + cblogger.Infof("# Subnet NameId to Create : [%s]", subnet.IId.NameId) + newSubnet, err := vpcHandler.CreateSubnet(subnet) // Caution!! For IID2 NameID validation check for Subnet + if err != nil { + cblogger.Errorf("Failed to Create NHN Cloud Sunbnet : [%v]", err) + LoggingError(callLogInfo, err) + return irs.VPCInfo{}, err + } + subnetList = append(subnetList, newSubnet) + } + + vpcInfo.SubnetInfoList = subnetList + // vpcInfo.IPv4_CIDR = vpcReqInfo.IPv4_CIDR // Caution!! NHN Cloud doesn't return CIDR info so, ~~~. + + return vpcInfo, err +} + +func (vpcHandler *NhnCloudVPCHandler) GetVPC(vpcIID irs.IID) (irs.VPCInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called GetVPC()!") + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, vpcIID.SystemId, "GetVPC()") + + cblogger.Infof("\n\n### vpcIID.SystemId : %s", vpcIID.SystemId) + if strings.EqualFold(vpcIID.SystemId, "") { + newErr := fmt.Errorf("Invalid VPC SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.VPCInfo{}, newErr + } + + start := call.Start() + var vpc NetworkWithExt + err := networks.Get(vpcHandler.NetworkClient, vpcIID.SystemId).ExtractInto(&vpc) + if err != nil { + cblogger.Errorf("Failed to Get VPC with the SystemId : [%s]. %v", vpcIID.SystemId, err) + LoggingError(callLogInfo, err) + return irs.VPCInfo{}, err + } + LoggingInfo(callLogInfo, start) + + vpcInfo := vpcHandler.MappingVpcInfo(vpc) + + cblogger.Info("\n\n### vpcInfo :") + spew.Dump(vpcInfo) + + return *vpcInfo, nil +} + +func (vpcHandler *NhnCloudVPCHandler) ListVPC() ([]*irs.VPCInfo, error) { + cblogger.Info("NHN Cloud Cloud Driver: called ListVPC()!") + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, "ListVPC()", "ListVPC()") + + start := call.Start() + listOpts := external.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + } + allPages, err := networks.List(vpcHandler.NetworkClient, listOpts).AllPages() + if err != nil { + cblogger.Errorf("Failed to Get Network list. %v", err) + LoggingError(callLogInfo, err) + return nil, err + } + LoggingInfo(callLogInfo, start) + + // cblogger.Info("\n\n### allPages :") + // spew.Dump(allPages) + + // To Get VPC info list + var vpcList []NetworkWithExt + err = networks.ExtractNetworksInto(allPages, &vpcList) + if err != nil { + cblogger.Errorf("Failed to Get VPC list. %v", err) + LoggingError(callLogInfo, err) + return nil, err + } + + var vpcInfoList []*irs.VPCInfo + for _, vpc := range vpcList { + vpcInfo := vpcHandler.MappingVpcInfo(vpc) + vpcInfoList = append(vpcInfoList, vpcInfo) + } + return vpcInfoList, nil +} + +func (vpcHandler *NhnCloudVPCHandler) DeleteVPC(vpcIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud Cloud Driver: called DeleteVPC()!") + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, vpcIID.SystemId, "DeleteVPC()") + + //To check whether the VPC exists. + cblogger.Infof("vpcIID.SystemId to Delete : [%s]", vpcIID.SystemId) + + vpcInfo, err := vpcHandler.GetVPC(vpcIID) + if err != nil { + cblogger.Errorf("Failed to Find the VPC with the SystemID. : [%s] : [%v]", vpcIID.SystemId, err) + LoggingError(callLogInfo, err) + return false, err + } else { + cblogger.Infof("Succeeded in Deleting the VPC : " + vpcInfo.IId.SystemId) + } + + return true, nil +} + +func (vpcHandler *NhnCloudVPCHandler) CreateSubnet(subnetReqInfo irs.SubnetInfo) (irs.SubnetInfo, error) { + cblogger.Info("NHN Cloud cloud driver: called CreateSubnet()!!") + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, subnetReqInfo.IId.NameId, "CreateSubnet()") + + if subnetReqInfo.IId.NameId == "" { + newErr := fmt.Errorf("Invalid Sunbet NameId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SubnetInfo{}, newErr + } + + listOpts := external.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + } + allPages, err := networks.List(vpcHandler.NetworkClient, listOpts).AllPages() + if err != nil { + cblogger.Errorf("Failed to Get Network list. %v", err) + LoggingError(callLogInfo, err) + return irs.SubnetInfo{}, err + } + + // cblogger.Info("\n\n### allPages :") + // spew.Dump(allPages) + + // To Get VPC info list + var vpcList []NetworkWithExt + err = networks.ExtractNetworksInto(allPages, &vpcList) + if err != nil { + cblogger.Errorf("Failed to Get VPC list. %v", err) + LoggingError(callLogInfo, err) + return irs.SubnetInfo{}, err + } + + var newSubnetInfo irs.SubnetInfo + + // To Get Subnet info with the originalNameId + for _, vpc := range vpcList { + for _, subnetId := range vpc.Subnets { + subnetInfo, err := vpcHandler.GetSubnet(irs.IID{SystemId: subnetId}) + if err != nil { + cblogger.Errorf("Failed to Get Subnet with Id [%s] : %s", subnetId, err) + continue + } + if strings.EqualFold(subnetInfo.IId.NameId, "Default Network") { + cblogger.Infof("# Found Default Subnet on NHN Cloud : [%s]", subnetInfo.IId.NameId) + newSubnetInfo = subnetInfo + newSubnetInfo.IId.NameId = subnetReqInfo.IId.NameId //Caution!! For IID2 NameID validation check + break + } + } + } + + // When the Subnet is not found + if newSubnetInfo.IId.SystemId == "" { + newErr := fmt.Errorf("Failed to Find the 'Default Network' Subnet on your NHN Cloud project!!") + LoggingError(callLogInfo, newErr) + return irs.SubnetInfo{}, newErr + } + return newSubnetInfo, err +} + +func (vpcHandler *NhnCloudVPCHandler) GetSubnet(subnetIId irs.IID) (irs.SubnetInfo, error) { + cblogger.Info("NHN Cloud cloud driver: called GetSubnet()!!") + // HisCall logging + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, subnetIId.SystemId, "GetSubnet()") + + if strings.EqualFold(subnetIId.SystemId, "") { + newErr := fmt.Errorf("Invalid Subnet SystemId!!") + cblogger.Error(newErr.Error()) + LoggingError(callLogInfo, newErr) + return irs.SubnetInfo{}, newErr + } + + subnet, err := subnets.Get(vpcHandler.NetworkClient, subnetIId.SystemId).Extract() + if err != nil { + cblogger.Errorf("Failed to Get Subnet with SystemId [%s] : %v", subnetIId.SystemId, err) + LoggingError(callLogInfo, err) + return irs.SubnetInfo{}, nil + } + subnetInfo := vpcHandler.MappingSubnetInfo(*subnet) + return *subnetInfo, nil +} + +func (vpcHandler *NhnCloudVPCHandler) AddSubnet(vpcIID irs.IID, subnetInfo irs.SubnetInfo) (irs.VPCInfo, error) { + cblogger.Info("NHN Cloud cloud driver: called AddSubnet()!!") + return irs.VPCInfo{}, errors.New("Does not support AddSubnet() yet!!") +} + +func (vpcHandler *NhnCloudVPCHandler) RemoveSubnet(vpcIID irs.IID, subnetIID irs.IID) (bool, error) { + cblogger.Info("NHN Cloud cloud driver: called RemoveSubnet()!!") + return true, errors.New("Does not support RemoveSubnet() yet!!") +} + +func (vpcHandler *NhnCloudVPCHandler) DeleteSubnet(subnetIId irs.IID) (bool, error) { + cblogger.Info("NHN Cloud cloud driver: called DeleteSubnet()!!") + + // HisCall logging + callLogInfo := GetCallLogScheme(vpcHandler.RegionInfo.Region, call.VPCSUBNET, subnetIId.SystemId, "DeleteSubnet()") + + //To check whether the Subnet exists. + cblogger.Infof("subnetIId.SystemId to Delete : [%s]", subnetIId.SystemId) + + subnetInfo, err := vpcHandler.GetSubnet(subnetIId) + if err != nil { + cblogger.Errorf("Failed to Find the Subnet with the SystemID. : [%s] : %v", subnetIId.SystemId, err) + LoggingError(callLogInfo, err) + return false, err + } else { + cblogger.Infof("Succeeded in Deleting the Subnet : [%s]", subnetInfo.IId.SystemId) + } + return true, nil +} + +func (vpcHandler *NhnCloudVPCHandler) MappingVpcInfo(nvpc NetworkWithExt) *irs.VPCInfo { + // Mapping VPC info. + vpcInfo := irs.VPCInfo { + IId: irs.IID{ + NameId: nvpc.Name, + SystemId: nvpc.ID, + }, + } + vpcInfo.IPv4_CIDR = "N/A" + + var External string + if nvpc.External { + External = "Yes" + } else if !nvpc.External { + External = "No" + } + + keyValueList := []irs.KeyValue{ + {Key: "Status", Value: nvpc.Status}, + {Key: "External_Network", Value: External}, + } + vpcInfo.KeyValueList = keyValueList + + // cblogger.Info("# nvpc.Subnets : ") + // spew.Dump(nvpc.Subnets) + + // Get Subnet info list. + var subnetInfoList []irs.SubnetInfo + var wait sync.WaitGroup + for _, subnetId := range nvpc.Subnets { + wait.Add(1) + go func(subnetId string) { + defer wait.Done() + subnetInfo, err := vpcHandler.GetSubnet(irs.IID{SystemId: subnetId}) + if err != nil { + cblogger.Errorf("Failed to Get Subnet info with the subnetId [%s]. [%v]", subnetId, err) + // continue + } + subnetInfoList = append(subnetInfoList, subnetInfo) + }(subnetId) + } + wait.Wait() + + vpcInfo.SubnetInfoList = subnetInfoList + // cblogger.Info("# vpcInfo.SubnetInfoList : ") + // spew.Dump(vpcInfo.SubnetInfoList) + return &vpcInfo +} + +func (vpcHandler *NhnCloudVPCHandler) MappingSubnetInfo(subnet subnets.Subnet) *irs.SubnetInfo { + // spew.Dump(subnet) + subnetInfo := irs.SubnetInfo{ + IId: irs.IID{ + NameId: subnet.Name, + SystemId: subnet.ID, + }, + IPv4_CIDR: subnet.CIDR, + } + + keyValueList := []irs.KeyValue{ + {Key: "VPCId", Value: subnet.NetworkID}, + } + subnetInfo.KeyValueList = keyValueList + return &subnetInfo +} diff --git a/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/nhn_rest_utils.go b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/nhn_rest_utils.go new file mode 100644 index 000000000..b78795601 --- /dev/null +++ b/cloud-control-manager/cloud-driver/drivers/nhncloud/resources/nhn_rest_utils.go @@ -0,0 +1,309 @@ +package resources + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// (변경필요) 기존에 받아온 토큰이 있으면 반환하고 없으면 새로 받아온 토큰을 반환한다. +func GetToken(host string, tenant_id string, username string, password string) (string, error) { + url := "/v2.0/tokens" + + data := `{ + "auth": { + "tenantId": "%s", + "passwordCredentials": { + "username": "%s", + "password": "%s" + } + } + }` + data = fmt.Sprintf(data, tenant_id, username, password) + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(data))) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + var payload map[string]interface{} + json.Unmarshal(bytes, &payload) + token := payload["access"].(map[string]interface{})["token"].(map[string]interface{})["id"].(string) + + return token, nil +} + +func CreateCluster(host string, token string, payload string) (string, error) { + + url := "clusters" + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(payload))) + if err != nil { + panic(err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), err +} + +func GetClusters(host string, token string) (string, error) { + + url := "clusters" + + req, err := http.NewRequest(http.MethodGet, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), err +} + +func GetCluster(host string, token string, cluster_id_or_name string) (string, error) { + + url := "clusters/" + cluster_id_or_name + + req, err := http.NewRequest(http.MethodGet, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func DeleteCluster(host string, token string, cluster_id_or_name string) (string, error) { + + url := "clusters/" + cluster_id_or_name + + req, err := http.NewRequest(http.MethodDelete, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func ResizeCluster(host string, token string, cluster_id_or_name string, payload string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/actions/resize" + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(payload))) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func GetNodeGroups(host string, token string, cluster_id_or_name string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups" + + req, err := http.NewRequest(http.MethodGet, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func GetNodeGroup(host string, token string, cluster_id_or_name string, node_group_id_or_name string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups/" + node_group_id_or_name + + req, err := http.NewRequest(http.MethodGet, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func CreateNodeGroup(host string, token string, cluster_id_or_name string, payload string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups" + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(payload))) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func DeleteNodeGroup(host string, token string, cluster_id_or_name string, node_group_id_or_name string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups/" + node_group_id_or_name + + req, err := http.NewRequest(http.MethodDelete, host+url, nil) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func ChangeNodeGroupAutoScaler(host string, token string, cluster_id_or_name string, node_group_id_or_name string, payload string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups/" + node_group_id_or_name + "/autoscale" + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(payload))) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +} + +func UpgradeCluster(host string, token string, cluster_id_or_name string, node_group_id_or_name string, payload string) (string, error) { + + url := "clusters/" + cluster_id_or_name + "/nodegroups/" + node_group_id_or_name + "/upgrade" + + req, err := http.NewRequest(http.MethodPost, host+url, bytes.NewBuffer([]byte(payload))) + if err != nil { + return "", err + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("OpenStack-API-Version", "container-infra latest") + req.Header.Set("X-Auth-Token", token) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + bytes, _ := io.ReadAll(res.Body) + + return string(bytes), nil +}