Add more server operations based on Nova API

This patch adds some more APIs to the Nova server resource.

Change-Id: Ic1caeda91bf4e95d4488b009f2d7e9fe14c23649
This commit is contained in:
tengqm 2016-08-24 04:10:53 -04:00
parent ddd8139a99
commit ccb5582717
6 changed files with 646 additions and 1 deletions

View File

@ -568,6 +568,211 @@ class Proxy(proxy2.BaseProxy):
security_group_id = resource2.Resource._get_id(security_group)
server.remove_security_group(self.session, security_group_id)
def add_fixed_ip_to_server(self, server, network_id):
"""Adds a fixed IP address to a server instance.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param network_id: The ID of the network from which a fixed IP address
is about to be allocated.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.add_fixed_ip(self.session, network_id)
def remove_fixed_ip_from_server(self, server, address):
"""Removes a fixed IP address from a server instance.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param address: The fixed IP address to be disassociated from the
server.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.remove_fixed_ip(self.session, address)
def add_floating_ip_to_server(self, server, address, fixed_address=None):
"""Adds a floating IP address to a server instance.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param address: The floating IP address to be added to the server.
:param fixed_address: The fixed IP address to be associated with the
floating IP address. Used when the server is
connected to multiple networks.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.add_floating_ip(self.session, address,
fixed_address=fixed_address)
def remove_floating_ip_from_server(self, server, address):
"""Removes a floating IP address from a server instance.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param address: The floating IP address to be disassociated from the
server.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.remove_floating_ip(self.session, address)
def pause_server(self, server):
"""Pauses a server and changes its status to ``PAUSED``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.pause(self.session)
def unpause_server(self, server):
"""Unpauses a paused server and changes its status to ``ACTIVE``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.unpause(self.session)
def suspend_server(self, server):
"""Suspends a server and changes its status to ``SUSPENDED``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.suspend(self.session)
def resume_server(self, server):
"""Resumes a suspended server and changes its status to ``ACTIVE``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.resume(self.session)
def lock_server(self, server):
"""Locks a server.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.lock(self.session)
def unlock_server(self, server):
"""Unlocks a locked server.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.unlock(self.session)
def rescue_server(self, server, admin_pass=None, image_ref=None):
"""Puts a server in rescue mode and changes it status to ``RESCUE``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param admin_pass: The password for the rescued server. If you omit
this parameter, the operation generates a new
password.
:param image_ref: The image reference to use to rescue your server.
This can be the image ID or its full URL. If you
omit this parameter, the base image reference will
be used.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.rescue(self.session, admin_pass=admin_pass, image_ref=image_ref)
def unrescue_server(self, server):
"""Unrescues a server and changes its status to ``ACTIVE``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.unrescue(self.session)
def evacuate_server(self, server, host=None, admin_pass=None, force=None):
"""Evacuates a server from a failed host to a new host.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param host: An optional parameter specifying the name or ID of the
host to which the server is evacuated.
:param admin_pass: An optional parameter specifying the administrative
password to access the evacuated or rebuilt server.
:param force: Force an evacuation by not verifying the provided
destination host by the scheduler. (New in API version
2.29).
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.evacuate(self.session, host=host, admin_pass=admin_pass,
force=force)
def start_server(self, server):
"""Starts a stopped server and changes its state to ``ACTIVE``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.start(self.session)
def stop_server(self, server):
"""Stops a running server and changes its state to ``SHUTOFF``.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.stop(self.session)
def shelve_server(self, server):
"""Shelves a server.
All associated data and resources are kept but anything still in
memory is not retained. Policy defaults enable only users with
administrative role or the owner of the server to perform this
operation. Cloud provides could change this permission though.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.shelve(self.session)
def unshelve_server(self, server):
"""Unselves or restores a shelved server.
Policy defaults enable only users with administrative role or the
owner of the server to perform this operation. Cloud provides could
change this permission though.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:returns: None
"""
server = self._get_resource(_server.Server, server)
server.unshelve(self.session)
def wait_for_server(self, server, status='ACTIVE', failures=['ERROR'],
interval=2, wait=120):
return resource2.wait_for_status(self.session, server, status,

View File

@ -209,6 +209,86 @@ class Server(resource2.Resource, metadata.MetadataMixin):
body = {"os-resetState": {"state": state}}
self._action(session, body)
def add_fixed_ip(self, session, network_id):
body = {"addFixedIp": {"networkId": network_id}}
self._action(session, body)
def remove_fixed_ip(self, session, address):
body = {"removeFixedIp": {"address": address}}
self._action(session, body)
def add_floating_ip(self, session, address, fixed_address=None):
body = {"addFloatingIp": {"address": address}}
if fixed_address is not None:
body['addFloatingIp']['fixed_address'] = fixed_address
self._action(session, body)
def remove_floating_ip(self, session, address):
body = {"removeFloatingIp": {"address": address}}
self._action(session, body)
def pause(self, session):
body = {"pause": None}
self._action(session, body)
def unpause(self, session):
body = {"unpause": None}
self._action(session, body)
def suspend(self, session):
body = {"suspend": None}
self._action(session, body)
def resume(self, session):
body = {"resume": None}
self._action(session, body)
def lock(self, session):
body = {"lock": None}
self._action(session, body)
def unlock(self, session):
body = {"unlock": None}
self._action(session, body)
def rescue(self, session, admin_pass=None, image_ref=None):
body = {"rescue": {}}
if admin_pass is not None:
body["rescue"]["adminPass"] = admin_pass
if image_ref is not None:
body["rescue"]["rescue_image_ref"] = image_ref
self._action(session, body)
def unrescue(self, session):
body = {"unrescue": None}
self._action(session, body)
def evacuate(self, session, host=None, admin_pass=None, force=None):
body = {"evacuate": {}}
if host is not None:
body["evacuate"]["host"] = host
if admin_pass is not None:
body["evacuate"]["adminPass"] = admin_pass
if force is not None:
body["evacuate"]["force"] = force
self._action(session, body)
def start(self, session):
body = {"os-start": None}
self._action(session, body)
def stop(self, session):
body = {"os-stop": None}
self._action(session, body)
def shelve(self, session):
body = {"shelve": None}
self._action(session, body)
def unshelve(self, session):
body = {"unshelve": None}
self._action(session, body)
class ServerDetail(Server):
base_path = '/servers/detail'

View File

@ -286,6 +286,121 @@ class TestComputeProxy(test_proxy_base2.TestProxyBase):
expected_kwargs={"metadata": {"k1": "v1"},
"image": id})
def test_add_fixed_ip_to_server(self):
self._verify("openstack.compute.v2.server.Server.add_fixed_ip",
self.proxy.add_fixed_ip_to_server,
method_args=["value", "network-id"],
expected_args=["network-id"])
def test_fixed_ip_from_server(self):
self._verify("openstack.compute.v2.server.Server.remove_fixed_ip",
self.proxy.remove_fixed_ip_from_server,
method_args=["value", "address"],
expected_args=["address"])
def test_floating_ip_to_server(self):
self._verify("openstack.compute.v2.server.Server.add_floating_ip",
self.proxy.add_floating_ip_to_server,
method_args=["value", "floating-ip"],
expected_args=["floating-ip"],
expected_kwargs={'fixed_address': None})
def test_add_floating_ip_to_server_with_fixed_addr(self):
self._verify("openstack.compute.v2.server.Server.add_floating_ip",
self.proxy.add_floating_ip_to_server,
method_args=["value", "floating-ip", 'fixed-addr'],
expected_args=["floating-ip"],
expected_kwargs={'fixed_address': 'fixed-addr'})
def test_remove_floating_ip_from_server(self):
self._verify("openstack.compute.v2.server.Server.remove_floating_ip",
self.proxy.remove_floating_ip_from_server,
method_args=["value", "address"],
expected_args=["address"])
def test_server_pause(self):
self._verify("openstack.compute.v2.server.Server.pause",
self.proxy.pause_server,
method_args=["value"])
def test_server_unpause(self):
self._verify("openstack.compute.v2.server.Server.unpause",
self.proxy.unpause_server,
method_args=["value"])
def test_server_suspend(self):
self._verify("openstack.compute.v2.server.Server.suspend",
self.proxy.suspend_server,
method_args=["value"])
def test_server_resume(self):
self._verify("openstack.compute.v2.server.Server.resume",
self.proxy.resume_server,
method_args=["value"])
def test_server_lock(self):
self._verify("openstack.compute.v2.server.Server.lock",
self.proxy.lock_server,
method_args=["value"])
def test_server_unlock(self):
self._verify("openstack.compute.v2.server.Server.unlock",
self.proxy.unlock_server,
method_args=["value"])
def test_server_rescue(self):
self._verify("openstack.compute.v2.server.Server.rescue",
self.proxy.rescue_server,
method_args=["value"],
expected_kwargs={"admin_pass": None, "image_ref": None})
def test_server_rescue_with_options(self):
self._verify("openstack.compute.v2.server.Server.rescue",
self.proxy.rescue_server,
method_args=["value", 'PASS', 'IMG'],
expected_kwargs={"admin_pass": 'PASS',
"image_ref": 'IMG'})
def test_server_unrescue(self):
self._verify("openstack.compute.v2.server.Server.unrescue",
self.proxy.unrescue_server,
method_args=["value"])
def test_server_evacuate(self):
self._verify("openstack.compute.v2.server.Server.evacuate",
self.proxy.evacuate_server,
method_args=["value"],
expected_kwargs={"host": None, "admin_pass": None,
"force": None})
def test_server_evacuate_with_options(self):
self._verify("openstack.compute.v2.server.Server.evacuate",
self.proxy.evacuate_server,
method_args=["value", 'HOST2', 'NEW_PASS', True],
expected_kwargs={"host": "HOST2",
"admin_pass": 'NEW_PASS',
"force": True})
def test_server_start(self):
self._verify("openstack.compute.v2.server.Server.start",
self.proxy.start_server,
method_args=["value"])
def test_server_stop(self):
self._verify("openstack.compute.v2.server.Server.stop",
self.proxy.stop_server,
method_args=["value"])
def test_server_shelve(self):
self._verify("openstack.compute.v2.server.Server.shelve",
self.proxy.shelve_server,
method_args=["value"])
def test_server_unshelve(self):
self._verify("openstack.compute.v2.server.Server.unshelve",
self.proxy.unshelve_server,
method_args=["value"])
def test_availability_zones(self):
self.verify_list_no_kwargs(self.proxy.availability_zones,
az.AvailabilityZone,

View File

@ -320,3 +320,247 @@ class TestServer(testtools.TestCase):
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_add_fixed_ip(self):
sot = server.Server(**EXAMPLE)
res = sot.add_fixed_ip(self.sess, "NETWORK-ID")
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"addFixedIp": {"networkId": "NETWORK-ID"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_remove_fixed_ip(self):
sot = server.Server(**EXAMPLE)
res = sot.remove_fixed_ip(self.sess, "ADDRESS")
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"removeFixedIp": {"address": "ADDRESS"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_add_floating_ip(self):
sot = server.Server(**EXAMPLE)
res = sot.add_floating_ip(self.sess, "FLOATING-IP")
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"addFloatingIp": {"address": "FLOATING-IP"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_add_floating_ip_with_fixed_addr(self):
sot = server.Server(**EXAMPLE)
res = sot.add_floating_ip(self.sess, "FLOATING-IP", "FIXED-ADDR")
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"addFloatingIp": {"address": "FLOATING-IP",
"fixed_address": "FIXED-ADDR"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_remove_floating_ip(self):
sot = server.Server(**EXAMPLE)
res = sot.remove_floating_ip(self.sess, "I-AM-FLOATING")
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"removeFloatingIp": {"address": "I-AM-FLOATING"}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_pause(self):
sot = server.Server(**EXAMPLE)
res = sot.pause(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"pause": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_unpause(self):
sot = server.Server(**EXAMPLE)
res = sot.unpause(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"unpause": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_suspend(self):
sot = server.Server(**EXAMPLE)
res = sot.suspend(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"suspend": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_resume(self):
sot = server.Server(**EXAMPLE)
res = sot.resume(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"resume": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_lock(self):
sot = server.Server(**EXAMPLE)
res = sot.lock(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"lock": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_unlock(self):
sot = server.Server(**EXAMPLE)
res = sot.unlock(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"unlock": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_rescue(self):
sot = server.Server(**EXAMPLE)
res = sot.rescue(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"rescue": {}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_rescue_with_options(self):
sot = server.Server(**EXAMPLE)
res = sot.rescue(self.sess, admin_pass='SECRET', image_ref='IMG-ID')
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"rescue": {'adminPass': 'SECRET',
'rescue_image_ref': 'IMG-ID'}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_unrescue(self):
sot = server.Server(**EXAMPLE)
res = sot.unrescue(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"unrescue": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_evacuate(self):
sot = server.Server(**EXAMPLE)
res = sot.evacuate(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"evacuate": {}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_evacuate_with_options(self):
sot = server.Server(**EXAMPLE)
res = sot.evacuate(self.sess, host='HOST2', admin_pass='NEW_PASS',
force=True)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"evacuate": {'host': 'HOST2', 'adminPass': 'NEW_PASS',
'force': True}}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_start(self):
sot = server.Server(**EXAMPLE)
res = sot.start(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"os-start": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_stop(self):
sot = server.Server(**EXAMPLE)
res = sot.stop(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"os-stop": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_shelve(self):
sot = server.Server(**EXAMPLE)
res = sot.shelve(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"shelve": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)
def test_unshelve(self):
sot = server.Server(**EXAMPLE)
res = sot.unshelve(self.sess)
self.assertIsNone(res)
url = 'servers/IDENTIFIER/action'
body = {"unshelve": None}
headers = {'Accept': ''}
self.sess.post.assert_called_with(
url, endpoint_filter=sot.service, json=body, headers=headers)

View File

@ -278,7 +278,7 @@ class TestSession(testtools.TestCase):
sot = session.Session(None)
endpoint = session.Session._Endpoint(root, versions)
rv = sot._get_version_match(endpoint, session.Version(2, 0), "service")
self.assertEqual(rv, root+match)
self.assertEqual(rv, root + match)
def test__get_version_match_project_id(self):
match = "http://devstack/v2"

View File

@ -44,5 +44,6 @@ commands = python setup.py test --coverage --coverage-package-name=openstack --t
commands = python setup.py build_sphinx
[flake8]
ignore=D100,D101,D102,D103,D104,D105,D200,D202,D204,D205,D211,D301,D400,D401
show-source = True
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build