From e793177ba1e4f25bacb37b934134a11901baa38a Mon Sep 17 00:00:00 2001
From: Anton Sidelnikov <53078276+anton-sidelnikov@users.noreply.github.com>
Date: Mon, 21 Oct 2024 16:01:56 +0200
Subject: [PATCH] [Feat.] DirectConnect V3 implementation (#719)

---
 acceptance/clients/clients.go                 |  16 +-
 .../openstack/dcaas/v3/hosted_connect_test.go |  64 ++++++
 .../dcaas/v3/virtual_gateway_test.go          |  90 ++++++++
 .../dcaas/v3/virtual_interface_test.go        | 112 ++++++++++
 openstack/client.go                           |  10 +
 openstack/dcaas/v3/hosted-connect/Create.go   |  83 ++++++++
 openstack/dcaas/v3/hosted-connect/Delete.go   |  10 +
 openstack/dcaas/v3/hosted-connect/Get.go      |  19 ++
 openstack/dcaas/v3/hosted-connect/List.go     |  61 ++++++
 openstack/dcaas/v3/hosted-connect/Update.go   |  39 ++++
 openstack/dcaas/v3/virtual-gateway/Create.go  |  50 +++++
 openstack/dcaas/v3/virtual-gateway/Delete.go  |  10 +
 openstack/dcaas/v3/virtual-gateway/Get.go     |  17 ++
 openstack/dcaas/v3/virtual-gateway/List.go    |  77 +++++++
 openstack/dcaas/v3/virtual-gateway/Update.go  |  43 ++++
 .../dcaas/v3/virtual-interface/Create.go      |  79 +++++++
 .../dcaas/v3/virtual-interface/CreatePeer.go  |  49 +++++
 .../dcaas/v3/virtual-interface/Delete.go      |  13 ++
 .../dcaas/v3/virtual-interface/DeletePeer.go  |  10 +
 openstack/dcaas/v3/virtual-interface/Get.go   |  18 ++
 openstack/dcaas/v3/virtual-interface/List.go  | 196 ++++++++++++++++++
 .../dcaas/v3/virtual-interface/Update.go      |  50 +++++
 .../dcaas/v3/virtual-interface/UpdatePeer.go  |  34 +++
 23 files changed, 1149 insertions(+), 1 deletion(-)
 create mode 100644 acceptance/openstack/dcaas/v3/hosted_connect_test.go
 create mode 100644 acceptance/openstack/dcaas/v3/virtual_gateway_test.go
 create mode 100644 acceptance/openstack/dcaas/v3/virtual_interface_test.go
 create mode 100644 openstack/dcaas/v3/hosted-connect/Create.go
 create mode 100644 openstack/dcaas/v3/hosted-connect/Delete.go
 create mode 100644 openstack/dcaas/v3/hosted-connect/Get.go
 create mode 100644 openstack/dcaas/v3/hosted-connect/List.go
 create mode 100644 openstack/dcaas/v3/hosted-connect/Update.go
 create mode 100644 openstack/dcaas/v3/virtual-gateway/Create.go
 create mode 100644 openstack/dcaas/v3/virtual-gateway/Delete.go
 create mode 100644 openstack/dcaas/v3/virtual-gateway/Get.go
 create mode 100644 openstack/dcaas/v3/virtual-gateway/List.go
 create mode 100644 openstack/dcaas/v3/virtual-gateway/Update.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/Create.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/CreatePeer.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/Delete.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/DeletePeer.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/Get.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/List.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/Update.go
 create mode 100644 openstack/dcaas/v3/virtual-interface/UpdatePeer.go

diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index 011ee8bac..e8d17e54e 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -151,7 +151,7 @@ func NewCTSV3Client() (*golangsdk.ServiceClient, error) {
 }
 
 // NewDCaaSV2Client returns a *ServiceClient for making calls
-// to the OpenStack v2 API. An error will be returned
+// to the OpenStack DCaaS v2 API. An error will be returned
 // if authentication or client creation was not possible.
 func NewDCaaSV2Client() (*golangsdk.ServiceClient, error) {
 	cc, err := CloudAndClient()
@@ -164,6 +164,20 @@ func NewDCaaSV2Client() (*golangsdk.ServiceClient, error) {
 	})
 }
 
+// NewDCaaSV3Client returns a *ServiceClient for making calls
+// to the OpenStack DCaaS v3 API. An error will be returned
+// if authentication or client creation was not possible.
+func NewDCaaSV3Client() (*golangsdk.ServiceClient, error) {
+	cc, err := CloudAndClient()
+	if err != nil {
+		return nil, err
+	}
+
+	return openstack.NewDCaaSV3(cc.ProviderClient, golangsdk.EndpointOpts{
+		Region: cc.RegionName,
+	})
+}
+
 // NewDNSV2Client returns a *ServiceClient for making calls
 // to the OpenStack Compute v2 API. An error will be returned
 // if authentication or client creation was not possible.
diff --git a/acceptance/openstack/dcaas/v3/hosted_connect_test.go b/acceptance/openstack/dcaas/v3/hosted_connect_test.go
new file mode 100644
index 000000000..2a81fc7d5
--- /dev/null
+++ b/acceptance/openstack/dcaas/v3/hosted_connect_test.go
@@ -0,0 +1,64 @@
+package v3
+
+import (
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
+	hosted_connect "github.com/opentelekomcloud/gophertelekomcloud/openstack/dcaas/v3/hosted-connect"
+	th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
+)
+
+func TestHostedConnectLifecycle(t *testing.T) {
+	t.Skip("This API only available in eu-ch2 region for now")
+	hostingId := os.Getenv("DCAAS_HOSTING_ID")
+	if hostingId == "" {
+		// hostingId = "45d7cbf9-b78e-4273-9a16-68f772b6c71d"
+		t.Skip("DCAAS_HOSTING_ID must be set for test")
+	}
+
+	client, err := clients.NewDCaaSV3Client()
+	th.AssertNoErr(t, err)
+
+	// Create a hosted connect
+	name := strings.ToLower(tools.RandomString("test-hosted-connect", 5))
+	createOpts := hosted_connect.CreateOpts{
+		Name:             name,
+		Description:      "hosted",
+		Bandwidth:        10,
+		Vlan:             441,
+		ResourceTenantId: client.ProjectID,
+		HostingID:        hostingId,
+	}
+	t.Logf("Attempting to create DCaaSv3 hosted connect")
+	created, err := hosted_connect.Create(client, createOpts)
+	th.AssertNoErr(t, err)
+
+	t.Logf("Attempting to retrieve list of DCaaSv3 hosted connects")
+	l, err := hosted_connect.List(client, hosted_connect.ListOpts{ID: []string{created.ID}})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, name, l[0].Name)
+
+	t.Logf("Attempting to retrieve DCaaSv3 hosted connect: %s", created.ID)
+	c, err := hosted_connect.Get(client, created.ID)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, name, c.Name)
+
+	t.Logf("Attempting to update DCaaSv3 hosted connect: %s", created.ID)
+	u, err := hosted_connect.Update(client, created.ID, hosted_connect.UpdateOpts{
+		Description: "update",
+		Bandwidth:   20,
+	})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, "update", u.Description)
+	th.AssertEquals(t, 20, u.Bandwidth)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete DCaaSv3 hosted connect: %s", created.ID)
+
+		err = hosted_connect.Delete(client, created.ID)
+		th.AssertNoErr(t, err)
+	})
+}
diff --git a/acceptance/openstack/dcaas/v3/virtual_gateway_test.go b/acceptance/openstack/dcaas/v3/virtual_gateway_test.go
new file mode 100644
index 000000000..82f74bc23
--- /dev/null
+++ b/acceptance/openstack/dcaas/v3/virtual_gateway_test.go
@@ -0,0 +1,90 @@
+package v3
+
+import (
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/pointerto"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/dcaas/v3/virtual-gateway"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v1/vpcs"
+	th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
+)
+
+func TestVirtualGatewayListing(t *testing.T) {
+	t.Skip("This API only available in eu-ch2 region for now")
+	client, err := clients.NewDCaaSV3Client()
+	th.AssertNoErr(t, err)
+
+	opts := virtual_gateway.ListOpts{}
+	vgwList, err := virtual_gateway.List(client, opts)
+	th.AssertNoErr(t, err)
+
+	for _, vgw := range vgwList {
+		tools.PrintResource(t, vgw)
+	}
+}
+
+func TestVirtualGatewayLifecycle(t *testing.T) {
+	t.Skip("This API only available in eu-ch2 region for now")
+	vpcID := os.Getenv("OS_VPC_ID")
+	if vpcID == "" {
+		t.Skip("OS_VPC_ID necessary for this test")
+	}
+	client, err := clients.NewDCaaSV3Client()
+	th.AssertNoErr(t, err)
+
+	clientNet, err := clients.NewNetworkV1Client()
+	th.AssertNoErr(t, err)
+	vpc, err := vpcs.Get(clientNet, vpcID).Extract()
+	th.AssertNoErr(t, err)
+
+	t.Logf("Attempting to create DCaaSv3 virtual gateway")
+	name := strings.ToLower(tools.RandomString("acc-virtual-gateway-v3-", 5))
+	createOpts := virtual_gateway.CreateOpts{
+		Name:         name,
+		VpcId:        vpcID,
+		Description:  "acc-virtual-gateway-v3",
+		LocalEpGroup: []string{vpc.CIDR},
+		Tags: []tags.ResourceTag{
+			{
+				Key:   "TestKey",
+				Value: "TestValue",
+			},
+		},
+	}
+
+	created, err := virtual_gateway.Create(client, createOpts)
+	th.AssertNoErr(t, err)
+
+	t.Logf("Attempting to obtain DCaaSv3 virtual gateway: %s", created.ID)
+	vgw, err := virtual_gateway.Get(client, created.ID)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, name, vgw.Name)
+
+	t.Logf("Attempting to update DCaaSv3 virtual gateway: %s", created.ID)
+	nameUpdated := strings.ToLower(tools.RandomString("acc-virtual-gateway-v3-up", 5))
+	updateOpts := virtual_gateway.UpdateOpts{
+		Name:        nameUpdated,
+		Description: pointerto.String("acc-virtual-gateway-v3-updated"),
+	}
+	updated, err := virtual_gateway.Update(client, created.ID, updateOpts)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, nameUpdated, updated.Name)
+
+	t.Logf("Attempting to obtain list of DCaaSv3 virtual gateways: %s", created.ID)
+	gateways, err := virtual_gateway.List(client, virtual_gateway.ListOpts{
+		VpcId: vpcID,
+	})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, len(gateways))
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete DCaaSv3 virtual gateway: %s", created.ID)
+		err = virtual_gateway.Delete(client, created.ID)
+		th.AssertNoErr(t, err)
+	})
+}
diff --git a/acceptance/openstack/dcaas/v3/virtual_interface_test.go b/acceptance/openstack/dcaas/v3/virtual_interface_test.go
new file mode 100644
index 000000000..a7344edd5
--- /dev/null
+++ b/acceptance/openstack/dcaas/v3/virtual_interface_test.go
@@ -0,0 +1,112 @@
+package v3
+
+import (
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/pointerto"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/dcaas/v3/virtual-gateway"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/dcaas/v3/virtual-interface"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/networking/v1/vpcs"
+	th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
+)
+
+func TestVirtualInterfaceListing(t *testing.T) {
+	t.Skip("This API only available in eu-ch2 region for now")
+	client, err := clients.NewDCaaSV3Client()
+	th.AssertNoErr(t, err)
+
+	// List virtual interfaces
+	opts := virtual_interface.ListOpts{}
+	viList, err := virtual_interface.List(client, opts)
+	th.AssertNoErr(t, err)
+
+	for _, vi := range viList {
+		tools.PrintResource(t, vi)
+	}
+}
+
+func TestVirtualInterfaceLifecycle(t *testing.T) {
+	t.Skip("This API only available in eu-ch2 region for now")
+	dcID := os.Getenv("DIRECT_CONNECT_ID")
+	vpcID := os.Getenv("OS_VPC_ID")
+	if vpcID == "" && dcID == "" {
+		t.Skip("DIRECT_CONNECT_ID and OS_VPC_ID necessary for this test")
+	}
+	client, err := clients.NewDCaaSV3Client()
+	th.AssertNoErr(t, err)
+
+	clientNet, err := clients.NewNetworkV1Client()
+	th.AssertNoErr(t, err)
+	vpc, err := vpcs.Get(clientNet, vpcID).Extract()
+	th.AssertNoErr(t, err)
+
+	t.Logf("Attempting to create DCaaSv3 virtual gateway")
+	name := strings.ToLower(tools.RandomString("acc-virtual-gateway-v3-", 5))
+	createOpts := virtual_gateway.CreateOpts{
+		Name:         name,
+		VpcId:        vpcID,
+		Description:  "acc-virtual-gateway-v3",
+		LocalEpGroup: []string{vpc.CIDR},
+		Tags: []tags.ResourceTag{
+			{
+				Key:   "TestKey",
+				Value: "TestValue",
+			},
+		},
+	}
+	vg, err := virtual_gateway.Create(client, createOpts)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		err = virtual_gateway.Delete(client, vg.ID)
+		th.AssertNoErr(t, err)
+	})
+
+	t.Logf("Attempting to create DCaaSv3 virtual interface")
+	nameVi := tools.RandomString("acc-virtual-interface-", 5)
+	viOpts := virtual_interface.CreateOpts{
+		Name:              nameVi,
+		DirectConnectID:   dcID,
+		VgwId:             vg.ID,
+		Type:              "private",
+		ServiceType:       "vpc",
+		VLAN:              100,
+		Bandwidth:         5,
+		LocalGatewayV4IP:  "16.16.16.1/30",
+		RemoteGatewayV4IP: "16.16.16.2/30",
+		RouteMode:         "static",
+		RemoteEpGroup:     []string{"16.16.16.0/30"},
+	}
+	created, err := virtual_interface.Create(client, viOpts)
+	th.AssertNoErr(t, err)
+
+	t.Logf("Attempting to obtain DCaaSv3 virtual interface: %s", created.ID)
+	vi, err := virtual_interface.Get(client, created.ID)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, "16.16.16.0/30", vi.RemoteEpGroup[0])
+
+	t.Logf("Attempting to update DCaaSv3 virtual interface: %s", created.ID)
+	updated, err := virtual_interface.Update(client, created.ID, virtual_interface.UpdateOpts{
+		Name:        name + "-updated",
+		Description: pointerto.String("New description"),
+	})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, name+"-updated", updated.Name)
+	th.AssertEquals(t, "New description", updated.Description)
+
+	t.Logf("Attempting to obtain list of DCaaSv3 virtual interfaces")
+	viList, err := virtual_interface.List(client, virtual_interface.ListOpts{})
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, 1, len(viList))
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete DCaaSv3 virtual interface: %s", created.ID)
+		err = virtual_interface.Delete(client, created.ID)
+		th.AssertNoErr(t, err)
+	})
+}
diff --git a/openstack/client.go b/openstack/client.go
index ccea0f292..1916afe50 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -700,6 +700,16 @@ func NewDCaaSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*g
 	return initClientOpts(client, eo, "dcaas")
 }
 
+// NewDCaaSV3 creates a ServiceClient that may be used to access the v1 Distributed Message Service.
+func NewDCaaSV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
+	sc, err := initClientOpts(client, eo, "dcaas")
+	if err != nil {
+		return nil, err
+	}
+	sc.Endpoint = strings.Replace(sc.Endpoint, "v2.0", "v3/"+client.ProjectID, 1)
+	return sc, err
+}
+
 // NewDMSServiceV1 creates a ServiceClient that may be used to access the v1 Distributed Message Service.
 func NewDMSServiceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
 	return initClientOpts(client, eo, "dmsv1")
diff --git a/openstack/dcaas/v3/hosted-connect/Create.go b/openstack/dcaas/v3/hosted-connect/Create.go
new file mode 100644
index 000000000..90edfbcb9
--- /dev/null
+++ b/openstack/dcaas/v3/hosted-connect/Create.go
@@ -0,0 +1,83 @@
+package hosted_connect
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type CreateOpts struct {
+	// Specifies the hosted connection name.
+	Name string `json:"name,omitempty"`
+	// Provides supplementary information about the hosted connection.
+	Description string `json:"description,omitempty"`
+	// Specifies the bandwidth of the hosted connection, in Mbit/s.
+	Bandwidth int `json:"bandwidth" required:"true"`
+	// Specifies the ID of the operations connection on which the hosted connection is created.
+	HostingID string `json:"hosting_id" required:"true"`
+	// Specifies the VLAN allocated to the hosted connection.
+	Vlan int `json:"vlan" required:"true"`
+	// Specifies the project ID of the specified tenant for whom a hosted connection is to be created.
+	ResourceTenantId string `json:"resource_tenant_id" required:"true"`
+	// Specifies the location of the on-premises facility at the other end of the connection,
+	// specific to the street or data center name.
+	PeerLocation string `json:"peer_location,omitempty"`
+}
+
+type HostedConnect struct {
+	// Specifies the hosted connection ID.
+	ID string `json:"id"`
+	// Specifies the project ID.
+	TenantID string `json:"tenant_id"`
+	// Specifies the connection name.
+	Name string `json:"name"`
+	// Provides supplementary information about the connection.
+	Description string `json:"description"`
+	// Specifies the connection bandwidth, in Mbit/s.
+	Bandwidth int `json:"bandwidth"`
+	// Specifies information about the Direct Connect location.
+	Location string `json:"location"`
+	// Specifies the location of the on-premises facility at the other end of the connection,
+	// specific to the street or data center name.
+	PeerLocation string `json:"peer_location"`
+	// Specifies the ID of the operations connection on which the hosted connection is created.
+	HostingId string `json:"hosting_id"`
+	// Specifies the provider of the leased line.
+	Provider string `json:"provider"`
+	// Specifies the provider of the leased line.
+	AdminStateUp bool `json:"admin_state_up"`
+	// Specifies the VLAN allocated to the hosted connection.
+	Vlan int `json:"vlan"`
+	// Specifies the operating status.
+	Status string `json:"status"`
+	// Specifies when the connection was requested.
+	// The UTC time format is yyyy-MM-ddTHH:mm:ss.SSSZ.
+	ApplyTime string `json:"apply_time"`
+	// Specifies when the connection was created.
+	// The UTC time format is yyyy-MM-ddTHH:mm:ss.SSSZ.
+	CreatedAt string `json:"create_time"`
+	// Specifies the carrier status. The status can be ACTIVE or DOWN.
+	ProviderStatus string `json:"provider_status"`
+	// Specifies the type of the port used by the connection.
+	// The value can be 1G, 10G, 40G, or 100G.
+	PortType string `json:"port_type"`
+	// Specifies the type of the connection. The value is hosted.
+	Type string `json:"type"`
+}
+
+func Create(c *golangsdk.ServiceClient, opts CreateOpts) (*HostedConnect, error) {
+	b, err := build.RequestBody(opts, "hosted_connect")
+	if err != nil {
+		return nil, err
+	}
+	raw, err := c.Post(c.ServiceURL("dcaas", "hosted-connects"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{201},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res HostedConnect
+	err = extract.IntoStructPtr(raw.Body, &res, "hosted_connect")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/hosted-connect/Delete.go b/openstack/dcaas/v3/hosted-connect/Delete.go
new file mode 100644
index 000000000..cd046e7f4
--- /dev/null
+++ b/openstack/dcaas/v3/hosted-connect/Delete.go
@@ -0,0 +1,10 @@
+package hosted_connect
+
+import golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+
+func Delete(c *golangsdk.ServiceClient, id string) (err error) {
+	_, err = c.Delete(c.ServiceURL("dcaas", "hosted-connects", id), &golangsdk.RequestOpts{
+		OkCodes: []int{200, 201, 204},
+	})
+	return
+}
diff --git a/openstack/dcaas/v3/hosted-connect/Get.go b/openstack/dcaas/v3/hosted-connect/Get.go
new file mode 100644
index 000000000..43b72e982
--- /dev/null
+++ b/openstack/dcaas/v3/hosted-connect/Get.go
@@ -0,0 +1,19 @@
+package hosted_connect
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+// Get retrieves a particular hosted connect based on its unique ID.
+func Get(c *golangsdk.ServiceClient, id string) (*HostedConnect, error) {
+	// GET /v3/{project_id}/dcaas/hosted-connects/{hosted_connect_id}
+	raw, err := c.Get(c.ServiceURL("dcaas", "hosted-connects", id), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var res HostedConnect
+	err = extract.IntoStructPtr(raw.Body, &res, "hosted_connect")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/hosted-connect/List.go b/openstack/dcaas/v3/hosted-connect/List.go
new file mode 100644
index 000000000..42c1d0db5
--- /dev/null
+++ b/openstack/dcaas/v3/hosted-connect/List.go
@@ -0,0 +1,61 @@
+package hosted_connect
+
+import (
+	"bytes"
+
+	"github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+	"github.com/opentelekomcloud/gophertelekomcloud/pagination"
+)
+
+type ListOpts struct {
+	// virtual gateway by ID
+	ID []string `q:"id,omitempty"`
+	// Specifies the number of records returned on each page. Value range: 1-2000
+	Limit int `q:"limit,omitempty"`
+	// Specifies the ID of the last resource record on the previous page. If this parameter is left blank, the first page is queried.
+	// This parameter must be used together with limit.
+	Marker string `q:"marker,omitempty"`
+	// Specifies the list of fields to be displayed.
+	Fields []interface{} `q:"fields,omitempty"`
+	// Specifies the sorting order of returned results. The value can be asc (default) or desc.
+	SortDir string `q:"sort_dir,omitempty"`
+	// Specifies the field for sorting.
+	SortKey string `q:"sort_key,omitempty"`
+	// Specifies operations connection ID by which hosted connections are queried.
+	HostingId []string `q:"hosting_id,omitempty"`
+	// Specifies the resource name by which instances are queried. You can specify multiple names.
+	Name []string `q:"name,omitempty"`
+}
+
+// List is used to obtain the virtual gateway list
+func List(client *golangsdk.ServiceClient, opts ListOpts) ([]HostedConnect, error) {
+	url, err := golangsdk.NewURLBuilder().WithEndpoints("dcaas", "hosted-connects").WithQueryParams(&opts).Build()
+	if err != nil {
+		return nil, err
+	}
+	pages, err := pagination.Pager{
+		Client:     client,
+		InitialURL: client.ServiceURL(url.String()),
+		CreatePage: func(r pagination.NewPageResult) pagination.NewPage {
+			return HcPage{NewSinglePageBase: pagination.NewSinglePageBase{NewPageResult: r}}
+		},
+	}.NewAllPages()
+
+	if err != nil {
+		return nil, err
+	}
+	return ExtractHCs(pages)
+}
+
+type HcPage struct {
+	pagination.NewSinglePageBase
+}
+
+func ExtractHCs(r pagination.NewPage) ([]HostedConnect, error) {
+	var s struct {
+		Connects []HostedConnect `json:"hosted_connects"`
+	}
+	err := extract.Into(bytes.NewReader((r.(HcPage)).Body), &s)
+	return s.Connects, err
+}
diff --git a/openstack/dcaas/v3/hosted-connect/Update.go b/openstack/dcaas/v3/hosted-connect/Update.go
new file mode 100644
index 000000000..dfd055534
--- /dev/null
+++ b/openstack/dcaas/v3/hosted-connect/Update.go
@@ -0,0 +1,39 @@
+package hosted_connect
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type UpdateOpts struct {
+	// Provides supplementary information about the connection.
+	Description string `json:"description,omitempty"`
+	// Specifies the connection name.
+	Name string `json:"name,omitempty"`
+	// Specifies the bandwidth of the hosted connection, in Mbit/s.
+	Bandwidth int `json:"bandwidth,omitempty"`
+	// Specifies the location of the on-premises facility
+	// at the other end of the connection, specific to the street or data center name.
+	PeerLocation string `json:"peer_location,omitempty"`
+}
+
+// Update is an operation which modifies the attributes of the specified hosted connect
+func Update(c *golangsdk.ServiceClient, id string, opts UpdateOpts) (*HostedConnect, error) {
+	// PUT /v3/{project_id}/dcaas/hosted-connects/{hosted_connect_id}
+	b, err := build.RequestBody(opts, "hosted_connect")
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := c.Put(c.ServiceURL("dcaas", "hosted-connects", id), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{200, 202},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res HostedConnect
+	err = extract.IntoStructPtr(raw.Body, &res, "hosted_connect")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-gateway/Create.go b/openstack/dcaas/v3/virtual-gateway/Create.go
new file mode 100644
index 000000000..b2efba3a1
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-gateway/Create.go
@@ -0,0 +1,50 @@
+package virtual_gateway
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+)
+
+type CreateOpts struct {
+	// The ID of the VPC connected to the virtual gateway.
+	VpcId string `json:"vpc_id" required:"true"`
+	// The list of IPv4 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroup []string `json:"local_ep_group" required:"true"`
+	// The list of IPv6 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroupIpv6 []string `json:"local_ep_group_ipv6,omitempty"`
+	// Specifies the name of the virtual gateway.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name,omitempty"`
+	// Specifies the description of the virtual gateway.
+	// The description contain a maximum of 64 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description string `json:"description,omitempty"`
+	// The local BGP ASN of the virtual gateway.
+	BgpAsn int `json:"bgp_asn,omitempty"`
+	// The key/value pairs to associate with the virtual gateway.
+	Tags []tags.ResourceTag `json:"tags,omitempty"`
+}
+
+func Create(c *golangsdk.ServiceClient, opts CreateOpts) (*VirtualGateway, error) {
+	b, err := build.RequestBody(opts, "virtual_gateway")
+	if err != nil {
+		return nil, err
+	}
+	raw, err := c.Post(c.ServiceURL("dcaas", "virtual-gateways"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{201},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res VirtualGateway
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_gateway")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-gateway/Delete.go b/openstack/dcaas/v3/virtual-gateway/Delete.go
new file mode 100644
index 000000000..5af2a98bd
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-gateway/Delete.go
@@ -0,0 +1,10 @@
+package virtual_gateway
+
+import golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+
+func Delete(c *golangsdk.ServiceClient, id string) (err error) {
+	_, err = c.Delete(c.ServiceURL("dcaas", "virtual-gateways", id), &golangsdk.RequestOpts{
+		OkCodes: []int{200, 201, 204},
+	})
+	return
+}
diff --git a/openstack/dcaas/v3/virtual-gateway/Get.go b/openstack/dcaas/v3/virtual-gateway/Get.go
new file mode 100644
index 000000000..47c7a703e
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-gateway/Get.go
@@ -0,0 +1,17 @@
+package virtual_gateway
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+func Get(c *golangsdk.ServiceClient, id string) (*VirtualGateway, error) {
+	raw, err := c.Get(c.ServiceURL("dcaas", "virtual-gateways", id), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var res VirtualGateway
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_gateway")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-gateway/List.go b/openstack/dcaas/v3/virtual-gateway/List.go
new file mode 100644
index 000000000..0c27d3a08
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-gateway/List.go
@@ -0,0 +1,77 @@
+package virtual_gateway
+
+import (
+	"github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+)
+
+type ListOpts struct {
+	// virtual gateway by ID
+	ID string `q:"id,omitempty"`
+	// Specifies the number of records returned on each page. Value range: 1-2000
+	Limit int `q:"limit,omitempty"`
+	// Specifies the ID of the last resource record on the previous page. If this parameter is left blank, the first page is queried.
+	// This parameter must be used together with limit.
+	Marker string `q:"marker,omitempty"`
+	// Specifies the list of fields to be displayed.
+	Fields []interface{} `q:"fields,omitempty"`
+	// Specifies the sorting order of returned results. The value can be asc (default) or desc.
+	SortDir string `q:"sort_dir,omitempty"`
+	// Specifies the field for sorting.
+	SortKey string `q:"sort_key,omitempty"`
+	// Specifies the VPC ID by which virtual gateways are queried.
+	VpcId string `q:"vpc_id,omitempty"`
+}
+
+// List is used to obtain the virtual gateway list
+func List(client *golangsdk.ServiceClient, opts ListOpts) ([]VirtualGateway, error) {
+	url, err := golangsdk.NewURLBuilder().WithEndpoints("dcaas", "virtual-gateways").WithQueryParams(&opts).Build()
+	if err != nil {
+		return nil, err
+	}
+
+	// GET https://{Endpoint}/v3/{project_id}/virtual-gateways
+	raw, err := client.Get(client.ServiceURL(url.String()), nil, openstack.StdRequestOpts())
+	if err != nil {
+		return nil, err
+	}
+
+	var res []VirtualGateway
+	err = extract.IntoSlicePtr(raw.Body, &res, "virtual_gateways")
+	return res, err
+}
+
+type VirtualGateway struct {
+	// The ID of the virtual gateway.
+	ID string `json:"id"`
+	// The ID of the VPC connected to the virtual gateway.
+	VpcId string `json:"vpc_id"`
+	// The project ID to which the virtual gateway belongs.
+	TenantId string `json:"tenant_id"`
+	// Specifies the name of the virtual gateway.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name"`
+	// Specifies the description of the virtual gateway.
+	// The description contain a maximum of 64 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description string `json:"description"`
+	// The type of virtual gateway.
+	Type string `json:"type"`
+	// The list of IPv4 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroup []string `json:"local_ep_group"`
+	// The list of IPv6 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroupIpv6 []string `json:"local_ep_group_ipv6"`
+	// The current status of the virtual gateway.
+	Status string `json:"status"`
+	// The local BGP ASN of the virtual gateway.
+	BgpAsn int `json:"bgp_asn"`
+	// The key/value pairs to associate with the virtual gateway.
+	Tags []tags.ResourceTag `json:"tags"`
+}
diff --git a/openstack/dcaas/v3/virtual-gateway/Update.go b/openstack/dcaas/v3/virtual-gateway/Update.go
new file mode 100644
index 000000000..08e0fa64f
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-gateway/Update.go
@@ -0,0 +1,43 @@
+package virtual_gateway
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type UpdateOpts struct {
+	// The list of IPv4 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroup []string `json:"local_ep_group,omitempty"`
+	// The list of IPv6 subnets from the virtual gateway to access cloud services, which is usually the CIDR block of
+	// the VPC.
+	LocalEpGroupIpv6 []string `json:"local_ep_group_ipv6,omitempty"`
+	// Specifies the name of the virtual gateway.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name,omitempty"`
+	// Specifies the description of the virtual gateway.
+	// The description contain a maximum of 64 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description *string `json:"description,omitempty"`
+}
+
+func Update(c *golangsdk.ServiceClient, id string, opts UpdateOpts) (*VirtualGateway, error) {
+	b, err := build.RequestBody(opts, "virtual_gateway")
+	if err != nil {
+		return nil, err
+	}
+	raw, err := c.Put(c.ServiceURL("dcaas", "virtual-gateways", id), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{200, 202},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res VirtualGateway
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_gateway")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/Create.go b/openstack/dcaas/v3/virtual-interface/Create.go
new file mode 100644
index 000000000..7791f36ee
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/Create.go
@@ -0,0 +1,79 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type CreateOpts struct {
+	// Specifies the project ID of another tenant, which is used to create virtual interfaces across tenants.
+	ResourceTenantID string `json:"resource_tenant_id,omitempty"`
+	// Specifies the virtual interface name.
+	Name string `json:"name,omitempty"`
+	// Provides supplementary information about the virtual interface.
+	Description string `json:"description,omitempty"`
+	// Specifies the ID of the connection associated with the virtual interface.
+	// When creating a virtual interface, you need to specify direct_connect_id or lag_id.
+	// This parameter is mandatory when LAG is not supported at the site.
+	DirectConnectID string `json:"direct_connect_id,omitempty"`
+	// Specifies the type of the virtual interface. The value is private.
+	Type string `json:"type" required:"true"`
+	// Specifies the type of the access gateway. You do not need to set this parameter.
+	ServiceType string `json:"service_type,omitempty"`
+	// Specifies the customer VLAN to be connected.
+	// If you select a hosted connection, the VLAN must be the same as that of the hosted connection.
+	VLAN int `json:"vlan" required:"true"`
+	// Specifies the virtual interface bandwidth.
+	Bandwidth int `json:"bandwidth" required:"true"`
+	// Specifies the IPv4 interface address of the gateway used on the cloud.
+	// This parameter is mandatory if address_family is set to an IPv4 address.
+	LocalGatewayV4IP string `json:"local_gateway_v4_ip,omitempty"`
+	// Specifies the IPv4 interface address of the gateway on the on-premises network.
+	// This parameter is mandatory if address_family is set to an IPv4 address.
+	RemoteGatewayV4IP string `json:"remote_gateway_v4_ip,omitempty"`
+	// Specifies the address family of the virtual interface.
+	// The value can be IPv4 or IPv6.
+	AddressFamily string `json:"address_family,omitempty"`
+	// Specifies the IPv6 interface address of the gateway used on the cloud.
+	// This parameter is mandatory if address_family is set to an IPv6 address.
+	LocalGatewayV6IP string `json:"local_gateway_v6_ip,omitempty"`
+	// Specifies the IPv6 interface address of the gateway on the on-premises network.
+	// This parameter is mandatory if address_family is set to an IPv6 address.
+	RemoteGatewayV6IP string `json:"remote_gateway_v6_ip,omitempty"`
+	// Specifies the ID of the virtual gateway connected by the virtual interface.
+	VgwId string `json:"vgw_id,omitempty" required:"true"`
+	// Specifies the routing mode. The value can be static or bgp.
+	RouteMode string `json:"route_mode" required:"true"`
+	// Specifies the ASN of the BGP peer on the customer side.
+	BGPASN int `json:"bgp_asn,omitempty"`
+	// Specifies the MD5 password of the BGP peer.
+	BGPMD5 string `json:"bgp_md5,omitempty"`
+	// Specifies the remote subnet list, which records the CIDR blocks used in the on-premises data center.
+	RemoteEpGroup []string `json:"remote_ep_group" required:"true"`
+	// Specifies the subnets that access Internet services through a connection.
+	ServiceEpGroup []string `json:"service_ep_group,omitempty"`
+	// Specifies whether to enable BFD.
+	EnableBfd bool `json:"enable_bfd,omitempty"`
+	// Specifies whether to enable NQA.
+	EnableNqa bool `json:"enable_nqa,omitempty"`
+	// Specifies the ID of the LAG associated with the virtual interface.
+	LagId string `json:"lag_id,omitempty"`
+}
+
+func Create(c *golangsdk.ServiceClient, opts CreateOpts) (*VirtualInterface, error) {
+	b, err := build.RequestBody(opts, "virtual_interface")
+	if err != nil {
+		return nil, err
+	}
+	raw, err := c.Post(c.ServiceURL("dcaas", "virtual-interfaces"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{201},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res VirtualInterface
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_interface")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/CreatePeer.go b/openstack/dcaas/v3/virtual-interface/CreatePeer.go
new file mode 100644
index 000000000..77ff12272
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/CreatePeer.go
@@ -0,0 +1,49 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type CreatePeerOpts struct {
+	// Specifies the name of the virtual interface peer.
+	Name string `json:"name" required:"true"`
+	// Provides supplementary information about the virtual interface peer.
+	Description string `json:"description,omitempty"`
+	// Specifies the gateway address of the virtual interface peer used on the cloud.
+	LocalGatewayIP string `json:"local_gateway_ip" required:"true"`
+	// Specifies the IPv4 interface address of the gateway on the on-premises network.
+	// This parameter is mandatory if address_family is set to an IPv4 address.
+	RemoteGatewayIP string `json:"remote_gateway_ip" required:"true"`
+	// Specifies the address family of the virtual interface.
+	// The value can be IPv4 or IPv6.
+	AddressFamily string `json:"address_family" required:"true"`
+	// Specifies the routing mode. The value can be static or bgp.
+	RouteMode string `json:"route_mode" required:"true"`
+	// Specifies the ASN of the BGP peer on the customer side.
+	BGPASN int `json:"bgp_asn,omitempty"`
+	// Specifies the MD5 password of the BGP peer.
+	BGPMD5 string `json:"bgp_md5,omitempty"`
+	// Specifies the remote subnet list, which records the CIDR blocks used in the on-premises data center.
+	RemoteEpGroup []string `json:"remote_ep_group,omitempty"`
+	// Specifies the ID of the virtual interface corresponding to the virtual interface peer.
+	VifId string `json:"vif_id" required:"true"`
+}
+
+func CreatePeer(c *golangsdk.ServiceClient, opts CreatePeerOpts) (*VifPeer, error) {
+	b, err := build.RequestBody(opts, "vif_peer")
+	if err != nil {
+		return nil, err
+	}
+	raw, err := c.Post(c.ServiceURL("dcaas", "vif-peers"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{201},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res VifPeer
+	err = extract.IntoStructPtr(raw.Body, &res, "vif_peer")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/Delete.go b/openstack/dcaas/v3/virtual-interface/Delete.go
new file mode 100644
index 000000000..a35ce6567
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/Delete.go
@@ -0,0 +1,13 @@
+package virtual_interface
+
+import golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+
+// Delete will permanently delete a particular VirtualInterface based on its
+// unique ID.
+
+func Delete(c *golangsdk.ServiceClient, id string) (err error) {
+	_, err = c.Delete(c.ServiceURL("dcaas", "virtual-interfaces", id), &golangsdk.RequestOpts{
+		OkCodes: []int{200, 201, 204},
+	})
+	return
+}
diff --git a/openstack/dcaas/v3/virtual-interface/DeletePeer.go b/openstack/dcaas/v3/virtual-interface/DeletePeer.go
new file mode 100644
index 000000000..89dbf19d8
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/DeletePeer.go
@@ -0,0 +1,10 @@
+package virtual_interface
+
+import golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+
+func DeletePeer(c *golangsdk.ServiceClient, id string) (err error) {
+	_, err = c.Delete(c.ServiceURL("dcaas", "vif-peers", id), &golangsdk.RequestOpts{
+		OkCodes: []int{200, 201, 204},
+	})
+	return
+}
diff --git a/openstack/dcaas/v3/virtual-interface/Get.go b/openstack/dcaas/v3/virtual-interface/Get.go
new file mode 100644
index 000000000..231266998
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/Get.go
@@ -0,0 +1,18 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+// Get retrieves a particular virtual gateway based on its unique ID.
+func Get(client *golangsdk.ServiceClient, id string) (*VirtualInterface, error) {
+	raw, err := client.Get(client.ServiceURL("dcaas", "virtual-interfaces", id), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var res VirtualInterface
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_interface")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/List.go b/openstack/dcaas/v3/virtual-interface/List.go
new file mode 100644
index 000000000..0baab6eb5
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/List.go
@@ -0,0 +1,196 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+)
+
+type ListOpts struct {
+	ID string `q:"id"`
+}
+
+// VirtualInterface is the structure that represents the details of the virtual interface.
+type VirtualInterface struct {
+	// The ID of the virtual interface.
+	ID string `json:"id"`
+	// Specifies the name of the virtual interface.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name"`
+	// Manage status.
+	// The valid values are 'true' and 'false'.
+	AdminStateUp bool `json:"admin_state_up"`
+	// The ingress bandwidth size of the virtual interface.
+	Bandwidth int `json:"bandwidth"`
+	// The creation time of the virtual interface.
+	CreatedAt string `json:"create_time"`
+	// The latest update time of the virtual interface.
+	UpdatedAt string `json:"update_time"`
+	// Specifies the description of the virtual interface.
+	// The description contain a maximum of 128 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description string `json:"description"`
+	// The ID of the direct connection associated with the virtual interface.
+	DirectConnectId string `json:"direct_connect_id"`
+	// The service type of the virtual interface.
+	ServiceType string `json:"service_type"`
+	// The current status of the virtual interface.
+	// The valid values are as follows:
+	// + ACTIVE
+	// + DOWN
+	// + BUILD
+	// + ERROR
+	// + PENDING_CREATE
+	// + PENDING_UPDATE
+	// + PENDING_DELETE
+	// + DELETED
+	// + AUTHORIZATION
+	// + REJECTED
+	Status string `json:"status"`
+	// The ID of the target tenant ID, which is used for cross tenant virtual interface creation.
+	TenantId string `json:"tenant_id"`
+	// The type of the virtual interface.
+	Type string `json:"type"`
+	// The ID of the virtual gateway to which the virtual interface is connected.
+	VgwId string `json:"vgw_id"`
+	// The VLAN for constom side.
+	Vlan int `json:"vlan"`
+	// The route specification of the remote VIF network.
+	RouteLimit int `json:"route_limit"`
+	// Whether to enable the Bidirectional Forwarding Detection (BFD) function.
+	EnableBfd bool `json:"enable_bfd"`
+	// Whether to enable the Network Quality Analysis (NQA) function.
+	EnableNqa bool `json:"enable_nqa"`
+	// The ID of the Intelligent EdgeSite (IES) associated with the virtual interface.
+	IesId string `json:"ies_id"`
+	// The ID of the link aggregation group (LAG) associated with the virtual interface.
+	LagId string `json:"lag_id"`
+	// The ID of the local gateway (LGW) associated with the virtual interface.
+	LgwId string `json:"lgw_id"`
+	// The local BGP ASN in client side.
+	BgpAsn int `json:"bgp_asn"`
+	// The (MD5) password for the local BGP.
+	BgpMd5 string `json:"bgp_md5"`
+	// The attributed Device ID.
+	DeviceId string `json:"device_id"`
+	// The IPv4 address of the virtual interface in cloud side.
+	LocalGatewayV4Ip string `json:"local_gateway_v4_ip"`
+	// The IPv4 address of the virtual interface in client side.
+	RemoteGatewayV4Ip string `json:"remote_gateway_v4_ip"`
+	// The address family type.
+	AddressFamily string `json:"address_family"`
+	// The IPv6 address of the virtual interface in cloud side.
+	LocalGatewayV6Ip string `json:"local_gateway_v6_ip"`
+	// The IPv6 address of the virtual interface in client side.
+	RemoteGatewayV6Ip string `json:"remote_gateway_v6_ip"`
+	// The CIDR list of remote subnets.
+	RemoteEpGroup []string `json:"remote_ep_group"`
+	// The CIDR list of subnets in service side.
+	ServiceEpGroup []string `json:"service_ep_group"`
+	// The route mode of the virtual interface.
+	RouteMode string `json:"route_mode"`
+	// Whether limit rate.
+	RateLimit bool `json:"rate_limit"`
+	// The VLAN for constom side.
+	VifPeers []VifPeer `json:"vif_peers"`
+	// The Peer details of the VIF.
+	ExtendAttribute VifExtendAttribute `json:"extend_attribute"`
+	// The enterprise project ID to which the virtual interface belongs.
+	EnterpriseProjectId string `json:"enterprise_project_id"`
+	// The key/value pairs to associate with the virtual interface.
+	Tags []tags.ResourceTag `json:"tags"`
+}
+
+type VifPeer struct {
+	// Resource ID.
+	ID string `json:"id"`
+	// The ID of the target tenant ID, which is used for cross tenant virtual interface creation.
+	TenantId string `json:"tenant_id"`
+	// Specifies the name of the VIF peer.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name"`
+	// Specifies the description of the virtual interface.
+	// The description contain a maximum of 128 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description string `json:"description"`
+	// The address family type.
+	AddressFamily string `json:"address_family"`
+	// Local gateway IP.
+	LocalGatewayIp string `json:"local_gateway_ip"`
+	// Remote gateway IP.
+	RemoteGatewayIp string `json:"remote_gateway_ip"`
+	// The routing mode, which can be static or bgp.
+	RouteMode string `json:"route_mode"`
+	// BGP ASN.
+	BgpAsn int `json:"bgp_asn"`
+	// BGP MD5 password.
+	BgpMd5 string `json:"bgp_md5"`
+	// The CIDR list of remote subnets.
+	RemoteEpGroup []string `json:"remote_ep_group"`
+	// The CIDR list of subnets in service side.
+	ServiceEpGroup []string `json:"service_ep_group"`
+	// Attributed Device ID.
+	DeviceId string `json:"device_id"`
+	// Whether to enable BFD.
+	EnableBfd bool `json:"enable_bfd"`
+	// Whether to enable NQA.
+	EnableNqa bool `json:"enable_nqa"`
+	// Attributed Device ID.
+	BgpRouteLimit int `json:"bgp_route_limit"`
+	// Attributed Device ID.
+	BgpStatus string `json:"bgp_status"`
+	// The status of the virtual interface peer.
+	Status string `json:"status"`
+	// The virtual interface ID corresponding to the VIF peer.
+	VifId string `json:"vif_id"`
+	// The number of received BGP routes if BGP routing is used.
+	ReceiveRouteNum int `json:"receive_route_num"`
+}
+
+type VifExtendAttribute struct {
+	// The availability detection types for virtual interface.
+	// + nqa
+	// + bfd
+	HaType string `json:"ha_type"`
+	// The specific configuration mode detected for virtual interface.
+	// + auto_single
+	// + auto_multi
+	// + static_single
+	// + static_multi
+	// + enhance_nqa
+	HaMode string `json:"ha_mode"`
+	// The detection retries.
+	DetectMultiplier string `json:"detect_multiplier"`
+	// The reception time interval for detection.
+	MinRxInterval string `json:"min_rx_interval"`
+	// To transmit time interval for detection.
+	MinTxInterval string `json:"min_tx_interval"`
+	// The identifier of the detected remote side, used for static BFD.
+	RemoteDisclaim string `json:"remote_disclaim"`
+	// The identifier of the detected local side, used for static BFD.
+	LocalDisclaim string `json:"local_disclaim"`
+}
+
+func List(client *golangsdk.ServiceClient, opts ListOpts) ([]VirtualInterface, error) {
+	url, err := golangsdk.NewURLBuilder().WithEndpoints("dcaas", "virtual-interfaces").WithQueryParams(&opts).Build()
+	if err != nil {
+		return nil, err
+	}
+
+	// GET https://{Endpoint}/v2.0/{project_id}/virtual-interfaces?id={id}
+	raw, err := client.Get(client.ServiceURL(url.String()), nil, openstack.StdRequestOpts())
+	if err != nil {
+		return nil, err
+	}
+
+	var res []VirtualInterface
+	err = extract.IntoSlicePtr(raw.Body, &res, "virtual_interfaces")
+	return res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/Update.go b/openstack/dcaas/v3/virtual-interface/Update.go
new file mode 100644
index 000000000..6b41f4206
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/Update.go
@@ -0,0 +1,50 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type UpdateOpts struct {
+	// Specifies the name of the virtual interface.
+	// The valid length is limited from 0 to 64, only chinese and english letters, digits, hyphens (-), underscores (_)
+	// and dots (.) are allowed.
+	// The name must start with a chinese or english letter, and the Chinese characters must be in **UTF-8** or
+	// **Unicode** format.
+	Name string `json:"name,omitempty"`
+	// Specifies the description of the virtual interface.
+	// The description contain a maximum of 128 characters and the angle brackets (< and >) are not allowed.
+	// Chinese characters must be in **UTF-8** or **Unicode** format.
+	Description *string `json:"description,omitempty"`
+	// The ingress bandwidth size of the virtual interface.
+	Bandwidth int `json:"bandwidth,omitempty"`
+	// The CIDR list of remote subnets.
+	RemoteEpGroup []string `json:"remote_ep_group,omitempty"`
+	// The CIDR list of subnets in service side.
+	ServiceEpGroup []string `json:"service_ep_group,omitempty"`
+	// Whether to enable the Bidirectional Forwarding Detection (BFD) function.
+	EnableBfd *bool `json:"enable_bfd,omitempty"`
+	// Whether to enable the Network Quality Analysis (NQA) function.
+	EnableNqa *bool `json:"enable_nqa,omitempty"`
+	// The status of the virtual interface to be changed.
+	Status string `json:"status,omitempty"`
+}
+
+// Update is an operation which modifies the attributes of the specified
+func Update(c *golangsdk.ServiceClient, id string, opts UpdateOpts) (*VirtualInterface, error) {
+	b, err := build.RequestBody(opts, "virtual_interface")
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := c.Put(c.ServiceURL("dcaas", "virtual-interfaces", id), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{200, 202},
+	})
+	if err != nil {
+		return nil, err
+	}
+	var res VirtualInterface
+	err = extract.IntoStructPtr(raw.Body, &res, "virtual_interface")
+	return &res, err
+}
diff --git a/openstack/dcaas/v3/virtual-interface/UpdatePeer.go b/openstack/dcaas/v3/virtual-interface/UpdatePeer.go
new file mode 100644
index 000000000..028194ed1
--- /dev/null
+++ b/openstack/dcaas/v3/virtual-interface/UpdatePeer.go
@@ -0,0 +1,34 @@
+package virtual_interface
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type UpdatePeerOpts struct {
+	// Specifies the name of the virtual interface peer.
+	Name string `json:"name" required:"true"`
+	// Provides supplementary information about the virtual interface peer.
+	Description string `json:"description,omitempty"`
+	// Specifies the remote subnet list, which records the CIDR blocks used in the on-premises data center.
+	RemoteEpGroup []string `json:"remote_ep_group,omitempty"`
+	// Specifies the ID of the virtual interface corresponding to the virtual interface peer.
+}
+
+func UpdatePeer(c *golangsdk.ServiceClient, id string, opts UpdatePeerOpts) (*VifPeer, error) {
+	b, err := build.RequestBody(opts, "vif_peer")
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := c.Put(c.ServiceURL("dcaas", "vif-peers", id), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{200, 202},
+	})
+	if err != nil {
+		return nil, err
+	}
+	var res VifPeer
+	err = extract.IntoStructPtr(raw.Body, &res, "vif_peer")
+	return &res, err
+}