-
Notifications
You must be signed in to change notification settings - Fork 555
Add migrations list support in compute service #3244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Patzifist
wants to merge
3
commits into
gophercloud:main
Choose a base branch
from
Patzifist:osmigrations
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package migrations | ||
|
||
/* | ||
Package migrations provides the ability to list data on migrations. | ||
|
||
Example to List os-migrations: | ||
|
||
pages, err := List(client, nil).AllPages(context.TODO()) | ||
if err != nil { | ||
panic("fail to get migration pages") | ||
} | ||
|
||
migrations, err := ExtractMigrations(pages) | ||
if err != nil { | ||
panic("fail to list migrations") | ||
} | ||
|
||
for _, migration := range migrations { | ||
fmt.Println(migration) | ||
} | ||
|
||
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package migrations | ||
|
||
import ( | ||
"github.com/gophercloud/gophercloud/v2" | ||
"github.com/gophercloud/gophercloud/v2/pagination" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
type ListOptsBuilder interface { | ||
ToMigrationsListQuery() (string, error) | ||
} | ||
|
||
type ListOpts struct { | ||
// The source/destination compute node of migration to filter | ||
Host *string `q:"host"` | ||
// The uuid of the instance that migration is operated on to filter | ||
InstanceID *string `q:"instance_uuid"` | ||
// The type of migration to filter. Valid values are: evacuation, live-migration, migration, resize | ||
MigrationType *string `q:"migration_type"` | ||
// The source compute node of migration to filter | ||
SourceCompute *string `q:"source_compute"` | ||
// The status of migration to filter | ||
Status *string `q:"status"` | ||
// Requests a page size of items | ||
Limit *int `q:"limit"` | ||
// The UUID of the last-seen migration | ||
Marker *string `q:"marker"` | ||
// Filters the response by a date and time stamp when the migration last changed | ||
ChangesSince *time.Time `q:"changes-since"` | ||
// Filters the response by a date and time stamp when the migration last changed | ||
ChangesBefore *time.Time `q:"changes-before"` | ||
// Filter the migrations by the given user ID | ||
UserID *string `q:"user_id"` | ||
// Filter the migrations by the given project ID | ||
ProjectID *string `q:"project_id"` | ||
} | ||
|
||
func (opts ListOpts) ToMigrationsListQuery() (string, error) { | ||
q, err := gophercloud.BuildQueryString(opts) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
params := q.Query() | ||
|
||
if opts.ChangesSince != nil { | ||
params.Add("changes-since", opts.ChangesSince.Format(time.RFC3339)) | ||
} | ||
|
||
if opts.ChangesBefore != nil { | ||
params.Add("changes-before", opts.ChangesBefore.Format(time.RFC3339)) | ||
} | ||
|
||
q = &url.URL{RawQuery: params.Encode()} | ||
return q.String(), nil | ||
} | ||
|
||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { | ||
reqUrl := listURL(client) | ||
if opts != nil { | ||
query, err := opts.ToMigrationsListQuery() | ||
if err != nil { | ||
return pagination.Pager{Err: err} | ||
} | ||
reqUrl += query | ||
} | ||
|
||
return pagination.NewPager(client, reqUrl, func(r pagination.PageResult) pagination.Page { | ||
return MigrationPage{pagination.SinglePageBase(r)} | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package migrations | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/gophercloud/gophercloud/v2" | ||
"github.com/gophercloud/gophercloud/v2/pagination" | ||
"time" | ||
) | ||
|
||
// Migration represents the details of a migration. | ||
type Migration struct { | ||
// The date and time when the resource was created | ||
CreatedAt time.Time `json:"-"` | ||
// The target compute for a migration | ||
DestCompute string `json:"dest_compute"` | ||
// The target host for a migration | ||
DestHost string `json:"dest_host"` | ||
// The target node for a migration. | ||
DestNode string `json:"dest_node"` | ||
// The ID of the server migration | ||
Id int64 `json:"id"` | ||
// The UUID of the server | ||
InstanceID string `json:"instance_uuid"` | ||
// In resize case, the flavor ID for resizing the server | ||
NewInstanceTypeId int64 `json:"new_instance_type_id"` | ||
// The flavor ID of the server when the migration was started | ||
OldInstanceTypeId int64 `json:"old_instance_type_id"` | ||
// The source compute for a migration | ||
SourceCompute string `json:"source_compute"` | ||
// The source node for a migration | ||
SourceNode string `json:"source_node"` | ||
// The current status of the migration | ||
Status string `json:"status"` | ||
// The date and time when the resource was updated | ||
UpdatedAt time.Time `json:"-"` | ||
// The type of the server migration. This is one of live-migration, migration, resize and evacuation | ||
MigrationType string `json:"migration_type"` | ||
// The UUID of the migration | ||
Uuid string `json:"uuid"` | ||
// The ID of the user which initiated the server migration | ||
UserId string `json:"user_id"` | ||
// The ID of the user which initiated the server migration | ||
ProjectId string `json:"project_id"` | ||
} | ||
|
||
// UnmarshalJSON converts our JSON API response into our migration struct | ||
func (i *Migration) UnmarshalJSON(b []byte) error { | ||
type tmp Migration | ||
var s struct { | ||
tmp | ||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` | ||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` | ||
} | ||
err := json.Unmarshal(b, &s) | ||
if err != nil { | ||
return err | ||
} | ||
*i = Migration(s.tmp) | ||
|
||
i.UpdatedAt = time.Time(s.UpdatedAt) | ||
i.CreatedAt = time.Time(s.CreatedAt) | ||
return err | ||
} | ||
|
||
type MigrationPage struct { | ||
pagination.SinglePageBase | ||
} | ||
|
||
func (r MigrationPage) IsEmpty() (bool, error) { | ||
migrations, err := ExtractMigrations(r) | ||
return len(migrations) == 0, err | ||
} | ||
|
||
func ExtractMigrations(r pagination.Page) ([]Migration, error) { | ||
var resp []Migration | ||
err := ExtractMigrationsInto(r, &resp) | ||
return resp, err | ||
} | ||
|
||
func ExtractMigrationsInto(r pagination.Page, v any) error { | ||
return r.(MigrationPage).Result.ExtractIntoSlicePtr(v, "migrations") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// migrations unit tests | ||
package testing |
107 changes: 107 additions & 0 deletions
107
openstack/compute/v2/migrations/testing/fixtures_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package testing | ||
|
||
import ( | ||
"fmt" | ||
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/migrations" | ||
th "github.com/gophercloud/gophercloud/v2/testhelper" | ||
"github.com/gophercloud/gophercloud/v2/testhelper/client" | ||
"net/http" | ||
"testing" | ||
"time" | ||
) | ||
|
||
// ListExpected represents an expected response from a List request. | ||
var ListExpected = []migrations.Migration{ | ||
{ | ||
Id: 2, | ||
CreatedAt: time.Date(2024, 11, 28, 8, 15, 6, 000000, time.UTC), | ||
DestCompute: "hci0", | ||
DestHost: "192.168.1.41", | ||
DestNode: "hci0", | ||
InstanceID: "6ba1f91a-50cc-48cd-9ce6-310991acb08d", | ||
NewInstanceTypeId: 1, | ||
OldInstanceTypeId: 1, | ||
SourceCompute: "compute0", | ||
SourceNode: "compute0", | ||
Status: "completed", | ||
UpdatedAt: time.Date(2024, 11, 28, 8, 15, 28, 000000, time.UTC), | ||
MigrationType: "live-migration", | ||
Uuid: "dde90a82-3059-4369-8bda-e0ba92713c54", | ||
UserId: "admin", | ||
ProjectId: "admin", | ||
}, | ||
{ | ||
Id: 1, | ||
CreatedAt: time.Date(2024, 11, 28, 8, 10, 02, 000000, time.UTC), | ||
DestCompute: "compute0", | ||
DestHost: "192.168.1.42", | ||
DestNode: "compute0", | ||
InstanceID: "6ba1f91a-50cc-48cd-9ce6-310991acb08d", | ||
NewInstanceTypeId: 1, | ||
OldInstanceTypeId: 1, | ||
SourceCompute: "hci0", | ||
SourceNode: "hci0", | ||
Status: "completed", | ||
UpdatedAt: time.Date(2024, 11, 28, 8, 10, 34, 000000, time.UTC), | ||
MigrationType: "live-migration", | ||
Uuid: "dde90a82-3059-4369-8bda-e0ba92713c54", | ||
UserId: "admin", | ||
ProjectId: "admin", | ||
}, | ||
} | ||
|
||
// HandleMigrationListSuccessfully sets up the test server to respond to a List request. | ||
func HandleMigrationListSuccessfully(t *testing.T) { | ||
th.Mux.HandleFunc("/os-migrations", func(w http.ResponseWriter, r *http.Request) { | ||
th.TestMethod(t, r, "GET") | ||
th.TestHeader(t, r, "X-Auth-Token", client.TokenID) | ||
|
||
w.Header().Add("Content-Type", "application/json") | ||
fmt.Fprintf(w, `{ | ||
"migrations": [ | ||
{ | ||
"id": 2, | ||
"uuid": "dde90a82-3059-4369-8bda-e0ba92713c54", | ||
"source_compute": "compute0", | ||
"dest_compute": "hci0", | ||
"source_node": "compute0", | ||
"dest_node": "hci0", | ||
"dest_host": "192.168.1.41", | ||
"old_instance_type_id": 1, | ||
"new_instance_type_id": 1, | ||
"instance_uuid": "6ba1f91a-50cc-48cd-9ce6-310991acb08d", | ||
"status": "completed", | ||
"migration_type": "live-migration", | ||
"user_id": "admin", | ||
"project_id": "admin", | ||
"created_at": "2024-11-28T08:15:06.000000", | ||
"updated_at": "2024-11-28T08:15:28.000000" | ||
}, | ||
{ | ||
"id": 1, | ||
"uuid": "dde90a82-3059-4369-8bda-e0ba92713c54", | ||
"source_compute": "hci0", | ||
"dest_compute": "compute0", | ||
"source_node": "hci0", | ||
"dest_node": "compute0", | ||
"dest_host": "192.168.1.42", | ||
"old_instance_type_id": 1, | ||
"new_instance_type_id": 1, | ||
"instance_uuid": "6ba1f91a-50cc-48cd-9ce6-310991acb08d", | ||
"status": "completed", | ||
"migration_type": "live-migration", | ||
"user_id": "admin", | ||
"project_id": "admin", | ||
"created_at": "2024-11-28T08:10:02.000000", | ||
"updated_at": "2024-11-28T08:10:34.000000" | ||
} | ||
], | ||
"migrations_links": [ | ||
{ | ||
"rel": "next", | ||
"href": "http://192.168.122.161:8774/v2.1/os-migrations?instance_uuid=6ba1f91a-50cc-48cd-9ce6-310991acb08d&limit=1&marker=dde90a82-3059-4369-8bda-e0ba92713c54" | ||
} | ||
] | ||
}`) | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package testing | ||
|
||
import ( | ||
"context" | ||
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/migrations" | ||
"github.com/gophercloud/gophercloud/v2/pagination" | ||
th "github.com/gophercloud/gophercloud/v2/testhelper" | ||
"github.com/gophercloud/gophercloud/v2/testhelper/client" | ||
"testing" | ||
) | ||
|
||
func TestList(t *testing.T) { | ||
th.SetupHTTP() | ||
defer th.TeardownHTTP() | ||
HandleMigrationListSuccessfully(t) | ||
|
||
expected := ListExpected | ||
pages := 0 | ||
err := migrations.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { | ||
pages++ | ||
|
||
actual, err := migrations.ExtractMigrations(page) | ||
th.AssertNoErr(t, err) | ||
|
||
if len(actual) != 2 { | ||
t.Fatalf("Expected 2 migrations, got %d", len(actual)) | ||
} | ||
th.CheckDeepEquals(t, expected, actual) | ||
|
||
return true, nil | ||
}) | ||
th.AssertNoErr(t, err) | ||
th.CheckEquals(t, 1, pages) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package migrations | ||
|
||
import "github.com/gophercloud/gophercloud/v2" | ||
|
||
func listURL(c *gophercloud.ServiceClient) string { | ||
return c.ServiceURL("os-migrations") | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CI fails on this line:
I can't immediately spot what the issue is, the code looks fine, and this should be running as admin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You found your issue already :)
We'll need to add another compute for the nova job to test migrations. @stephenfin do you have an idea if that's easily feasible?
@Patzifist in case you want to give it a shot, the job is configured in https://github.com/gophercloud/gophercloud/blob/master/.github/workflows/functional-compute.yaml.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest disabling the test and merging the request. what do you think about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the lag anwering your question.
Rather than deleting a test that is otherwise good, could we instead skip it if there's less than 2 computes?