Support server unshelve to specific availability zone

Closes-Bug: #2100345
Change-Id: I6d16b9251ea342ea921c9a508965eba681ca76e8
This commit is contained in:
Sina Sadeghi
2025-02-27 14:29:27 +11:00
committed by Stephen Finucane
parent 149e96917f
commit 5a0f3f1dd5
5 changed files with 61 additions and 43 deletions

View File

@@ -42,6 +42,7 @@ from openstack.identity.v3 import user as _user
from openstack.network.v2 import security_group as _sg
from openstack import proxy
from openstack import resource
from openstack import types
from openstack import utils
from openstack import warnings as os_warnings
@@ -1194,7 +1195,9 @@ class Proxy(proxy.Proxy):
server = self._get_resource(_server.Server, server)
server.shelve_offload(self)
def unshelve_server(self, server, *, host=None):
def unshelve_server(
self, server, *, host=None, availability_zone=types.UNSET
):
"""Unshelves or restores a shelved server.
Policy defaults enable only users with administrative role or the
@@ -1208,7 +1211,7 @@ class Proxy(proxy.Proxy):
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.unshelve(self, host=host)
server.unshelve(self, host=host, availability_zone=availability_zone)
def trigger_server_crash_dump(self, server):
"""Trigger a crash dump in a server.

View File

@@ -19,18 +19,10 @@ from openstack.compute.v2 import volume_attachment
from openstack import exceptions
from openstack.image.v2 import image
from openstack import resource
from openstack import types
from openstack import utils
# Workaround Python's lack of an undefined sentinel
# https://python-patterns.guide/python/sentinel-object/
class Unset:
def __bool__(self) -> ty.Literal[False]:
return False
UNSET: Unset = Unset()
CONSOLE_TYPE_ACTION_MAPPING = {
'novnc': 'os-getVNCConsole',
'xvpvnc': 'os-getVNCConsole',
@@ -52,11 +44,6 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin):
allow_delete = True
allow_list = True
# Sentinel used to differentiate API called without parameter or None
# Ex unshelve API can be called without an availability_zone or with
# availability_zone = None to unpin the az.
_sentinel = object()
_query_mapping = resource.QueryParameters(
"auto_disk_config",
"availability_zone",
@@ -409,17 +396,17 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin):
self,
session,
image,
name=UNSET,
admin_password=UNSET,
preserve_ephemeral=UNSET,
access_ipv4=UNSET,
access_ipv6=UNSET,
metadata=UNSET,
user_data=UNSET,
key_name=UNSET,
description=UNSET,
trusted_image_certificates=UNSET,
hostname=UNSET,
name=types.UNSET,
admin_password=types.UNSET,
preserve_ephemeral=types.UNSET,
access_ipv4=types.UNSET,
access_ipv6=types.UNSET,
metadata=types.UNSET,
user_data=types.UNSET,
key_name=types.UNSET,
description=types.UNSET,
trusted_image_certificates=types.UNSET,
hostname=types.UNSET,
):
"""Rebuild the server with the given arguments.
@@ -448,27 +435,27 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin):
:returns: The updated server.
"""
action = {'imageRef': resource.Resource._get_id(image)}
if preserve_ephemeral is not UNSET:
if preserve_ephemeral is not types.UNSET:
action['preserve_ephemeral'] = preserve_ephemeral
if name is not UNSET:
if name is not types.UNSET:
action['name'] = name
if admin_password is not UNSET:
if admin_password is not types.UNSET:
action['adminPass'] = admin_password
if access_ipv4 is not UNSET:
if access_ipv4 is not types.UNSET:
action['accessIPv4'] = access_ipv4
if access_ipv6 is not UNSET:
if access_ipv6 is not types.UNSET:
action['accessIPv6'] = access_ipv6
if metadata is not UNSET:
if metadata is not types.UNSET:
action['metadata'] = metadata
if user_data is not UNSET:
if user_data is not types.UNSET:
action['user_data'] = user_data
if key_name is not UNSET:
if key_name is not types.UNSET:
action['key_name'] = key_name
if description is not UNSET:
if description is not types.UNSET:
action['description'] = description
if trusted_image_certificates is not UNSET:
if trusted_image_certificates is not types.UNSET:
action['trusted_image_certificates'] = trusted_image_certificates
if hostname is not UNSET:
if hostname is not types.UNSET:
action['hostname'] = hostname
body = {'rebuild': action}
@@ -820,7 +807,7 @@ class Server(resource.Resource, metadata.MetadataMixin, tag.TagMixin):
body = {"shelveOffload": None}
self._action(session, body)
def unshelve(self, session, availability_zone=_sentinel, host=None):
def unshelve(self, session, availability_zone=types.UNSET, host=None):
"""Unshelve the server.
:param session: The session to use for making this request.

View File

@@ -43,6 +43,7 @@ from openstack.identity.v3 import project
from openstack import proxy as proxy_base
from openstack.tests.unit import base
from openstack.tests.unit import test_proxy_base
from openstack import types
from openstack import warnings as os_warnings
@@ -1371,6 +1372,7 @@ class TestCompute(TestComputeProxy):
expected_args=[self.proxy],
expected_kwargs={
"host": None,
"availability_zone": types.UNSET,
},
)
@@ -1379,11 +1381,9 @@ class TestCompute(TestComputeProxy):
"openstack.compute.v2.server.Server.unshelve",
self.proxy.unshelve_server,
method_args=["value"],
method_kwargs={"host": "HOST2"},
method_kwargs={"host": "HOST2", "availability_zone": "AZ2"},
expected_args=[self.proxy],
expected_kwargs={
"host": "HOST2",
},
expected_kwargs={"host": "HOST2", "availability_zone": "AZ2"},
)
def test_server_trigger_dump(self):

23
openstack/types.py Normal file
View File

@@ -0,0 +1,23 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import typing as ty
# Workaround Python's lack of an undefined sentinel
# https://python-patterns.guide/python/sentinel-object/
class Unset:
def __bool__(self) -> ty.Literal[False]:
return False
UNSET: Unset = Unset()

View File

@@ -0,0 +1,5 @@
---
fixes:
- |
Fixed the issue that unshelving a server to a specific availability zone
was failed due to unhandled ``availability_zone`` option.