Repository: gophercloud/gophercloud

Issue:
Baremetal: add support for new virtual media GET API
Introduced in https://review.opendev.org/c/openstack/ironic/+/921657 

Hints from issue comments:
@github-actions: Thank you for reporting your first issue! Be sure that we will be looking at it, but keep in mind
this sometimes takes a while.
Please let the maintainers know if your issue has not got enough attention after a few days.
If any doubt, please consult our issue [tutorial](https://github.com/gophercloud/gophercloud/blob/main/docs/contributor-tutorial/step-02-issues.md).@Sharpz7: This is great! Thank you. I am tagging https://github.com/gophercloud/gophercloud/issues/1429 so this gets seen there.

Solution patch:
diff --git a/internal/acceptance/clients/conditions.go b/internal/acceptance/clients/conditions.go
index 619d31a94..c48ac208a 100644
--- a/internal/acceptance/clients/conditions.go
+++ b/internal/acceptance/clients/conditions.go
@@ -119,7 +119,7 @@ func getReleaseFromEnv(t *testing.T) string {
 // release. Releases are named such as 'stable/mitaka', master, etc.
 func SkipRelease(t *testing.T, release string) {
 	current := getReleaseFromEnv(t)
-	if current == release {
+	if current == strings.TrimPrefix(release, "stable/") {
 		t.Skipf("this is not supported in %s", release)
 	}
 }
diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go
index cddd269a3..6268f5037 100644
--- a/openstack/baremetal/v1/nodes/requests.go
+++ b/openstack/baremetal/v1/nodes/requests.go
@@ -1003,6 +1003,18 @@ func DetachVirtualMedia(ctx context.Context, client *gophercloud.ServiceClient,
 	return
 }
 
+// Request the list of virtual media devices attached to the Node.
+// Requires microversion 1.93 or later.
+func GetVirtualMedia(ctx context.Context, client *gophercloud.ServiceClient, id string) (r VirtualMediaGetResult) {
+
+	resp, err := client.Get(ctx, virtualMediaURL(client, id), &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+
+	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+	return
+}
+
 // VirtualInterfaceOpts defines options for attaching a VIF to a node
 type VirtualInterfaceOpts struct {
 	// The UUID or name of the VIF
diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go
index 3e1976d3e..b33517a38 100644
--- a/openstack/baremetal/v1/nodes/results.go
+++ b/openstack/baremetal/v1/nodes/results.go
@@ -711,6 +711,11 @@ type VirtualMediaDetachResult struct {
 	gophercloud.ErrResult
 }
 
+// Requires microversion 1.93 or later.
+type VirtualMediaGetResult struct {
+	gophercloud.Result
+}
+
 // VirtualInterfaceAttachResult is the response from an AttachVirtualInterface operation.
 type VirtualInterfaceAttachResult struct {
 	gophercloud.ErrResult



Test patch:
diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go
index 1151135e6..be48ee919 100644
--- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go
+++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go
@@ -219,7 +219,7 @@ func TestNodesVirtualMedia(t *testing.T) {
 
 	client, err := clients.NewBareMetalV1Client()
 	th.AssertNoErr(t, err)
-	client.Microversion = "1.89"
+	client.Microversion = "1.93"
 
 	node, err := CreateNode(t, client)
 	th.AssertNoErr(t, err)
@@ -241,6 +241,15 @@ func TestNodesVirtualMedia(t *testing.T) {
 
 	err = nodes.DetachVirtualMedia(context.TODO(), client, node.UUID, nodes.DetachVirtualMediaOpts{}).ExtractErr()
 	th.AssertNoErr(t, err)
+
+	err = nodes.GetVirtualMedia(context.TODO(), client, node.UUID).Err
+	// Since Virtual Media GET api call is synchronous, we get a HTTP 400
+	// response as CreateNode has ipmi driver hardcoded, but the api is
+	// only supported by the redfish driver
+	// (TODO: hroyrh) fix this once redfish driver is used in the tests
+	if node.Driver == "redfish" {
+		th.AssertNoErr(t, err)
+	}
 }
 
 func TestNodesServicingHold(t *testing.T) {
diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go
index 1df437516..5930be2a1 100644
--- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go
+++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go
@@ -216,6 +216,8 @@ func TestLayer3RouterAgents(t *testing.T) {
 }
 
 func TestLayer3RouterRevision(t *testing.T) {
+	// https://bugs.launchpad.net/neutron/+bug/2101871
+	clients.SkipRelease(t, "stable/2023.2")
 	client, err := clients.NewNetworkV2Client()
 	th.AssertNoErr(t, err)
 
diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go
index 282d5dbc7..371a4f00a 100644
--- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go
+++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go
@@ -900,6 +900,28 @@ const NodeVirtualMediaAttachBodyWithSource = `
 }
 `
 
+const NodeVirtualMediaGetBodyAttached = `
+{
+    "image": "https://example.com/image",
+    "inserted": true,
+    "media_types": [
+      "CD",
+      "DVD"
+    ]
+}
+`
+
+const NodeVirtualMediaGetBodyNotAttached = `
+{
+    "image": "",
+    "inserted": false,
+    "media_types": [
+      "CD",
+      "DVD"
+    ]
+}
+`
+
 var (
 	createdAtFoo, _      = time.Parse(time.RFC3339, "2019-01-31T19:59:28+00:00")
 	createdAtBar, _      = time.Parse(time.RFC3339, "2019-01-31T19:59:29+00:00")
@@ -1850,6 +1872,19 @@ func HandleDetachVirtualMediaSuccessfully(t *testing.T, withType bool) {
 	})
 }
 
+func HandleGetVirtualMediaSuccessfully(t *testing.T, attached bool) {
+	th.Mux.HandleFunc("/nodes/1234asdf/vmedia", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		w.WriteHeader(http.StatusOK)
+		if attached {
+			fmt.Fprint(w, NodeVirtualMediaGetBodyAttached)
+		} else {
+			fmt.Fprint(w, NodeVirtualMediaGetBodyNotAttached)
+		}
+	})
+}
+
 // HandleListVirtualInterfacesSuccessfully sets up the test server to respond to a ListVirtualInterfaces request
 func HandleListVirtualInterfacesSuccessfully(t *testing.T) {
 	th.Mux.HandleFunc("/nodes/1234asdf/vifs",
diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go
index 487c35017..24452be0c 100644
--- a/openstack/baremetal/v1/nodes/testing/requests_test.go
+++ b/openstack/baremetal/v1/nodes/testing/requests_test.go
@@ -830,6 +830,26 @@ func TestVirtualMediaDetachWithTypes(t *testing.T) {
 	th.AssertNoErr(t, err)
 }
 
+func TestVirtualMediaGetAttached(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetVirtualMediaSuccessfully(t, true)
+
+	c := client.ServiceClient()
+	err := nodes.GetVirtualMedia(context.TODO(), c, "1234asdf").Err
+	th.AssertNoErr(t, err)
+}
+
+func TestVirtualMediaGetNotAttached(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetVirtualMediaSuccessfully(t, false)
+
+	c := client.ServiceClient()
+	err := nodes.GetVirtualMedia(context.TODO(), c, "1234asdf").Err
+	th.AssertNoErr(t, err)
+}
+
 func TestListVirtualInterfaces(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()