Extension for rebuild-for-ha
Adds an extension for blueprint rebuild-for-ha Evacuation of VM from a failed host for HA. New api supports recreate/rebuild modes. With shared storage, VM is recreated on the target from disk. Otherwise, rebuild from original image is performed. DocImpact Change-Id: Id7e3e68dbaaf74e9314440d98195a92454078fd9 Co-authored-by: Oshrit Feder <oshritf@il.ibm.com>
This commit is contained in:
		@@ -184,6 +184,14 @@
 | 
			
		||||
            "namespace": "http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1",
 | 
			
		||||
            "updated": "2011-09-01T00:00:00+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "alias": "os-evacuate",
 | 
			
		||||
            "description": "Enables server evacuation",
 | 
			
		||||
            "links": [],
 | 
			
		||||
            "name": "Evacuate",
 | 
			
		||||
            "namespace": "http://docs.openstack.org/compute/ext/evacuate/api/v2",
 | 
			
		||||
            "updated": "2012-12-05T00:00:00+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "alias": "os-fixed-ips",
 | 
			
		||||
            "description": "Fixed IPs support.",
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,9 @@
 | 
			
		||||
  <extension alias="os-deferred-delete" updated="2011-09-01T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1" name="DeferredDelete">
 | 
			
		||||
    <description>Instance deferred delete.</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
  <extension alias="os-evacuate" updated="2012-12-05T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/evacuate/api/v2" name="Evacuate">
 | 
			
		||||
    <description>Enables server evacuation</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
  <extension alias="os-fixed-ips" updated="2012-10-18T13:25:27-06:00" namespace="http://docs.openstack.org/compute/ext/fixed_ips/api/v2" name="FixedIPs">
 | 
			
		||||
    <description>Fixed IPs support.</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								doc/api_samples/os-evacuate/server-evacuate-req.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								doc/api_samples/os-evacuate/server-evacuate-req.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "evacuate": {
 | 
			
		||||
        "host": "TargetHost",
 | 
			
		||||
        "adminPass": "MySecretPass",
 | 
			
		||||
        "onSharedStorage": "True"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								doc/api_samples/os-evacuate/server-evacuate-req.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								doc/api_samples/os-evacuate/server-evacuate-req.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<rescue xmlns="http://docs.openstack.org/compute/api/v2"
 | 
			
		||||
    host="TargetHost"
 | 
			
		||||
    adminPass="MySecretPass"
 | 
			
		||||
    onSharedStorage="True"/>
 | 
			
		||||
							
								
								
									
										3
									
								
								doc/api_samples/os-evacuate/server-evacuate-resp.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								doc/api_samples/os-evacuate/server-evacuate-resp.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "adminPass": "MySecretPass"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								doc/api_samples/os-evacuate/server-evacuate-resp.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								doc/api_samples/os-evacuate/server-evacuate-resp.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
<?xml version='1.0' encoding='UTF-8'?>
 | 
			
		||||
<adminPass>MySecretPass</adminPass>
 | 
			
		||||
							
								
								
									
										16
									
								
								doc/api_samples/os-evacuate/server-post-req.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								doc/api_samples/os-evacuate/server-post-req.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "server" : {
 | 
			
		||||
        "name" : "new-server-test",
 | 
			
		||||
        "imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
 | 
			
		||||
        "flavorRef" : "http://openstack.example.com/openstack/flavors/1",
 | 
			
		||||
        "metadata" : {
 | 
			
		||||
            "My Server Name" : "Apache1"
 | 
			
		||||
        },
 | 
			
		||||
        "personality" : [
 | 
			
		||||
            {
 | 
			
		||||
                "path" : "/etc/banner.txt",
 | 
			
		||||
                "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								doc/api_samples/os-evacuate/server-post-req.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								doc/api_samples/os-evacuate/server-post-req.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b" flavorRef="http://openstack.example.com/openstack/flavors/1" name="new-server-test">
 | 
			
		||||
  <metadata>
 | 
			
		||||
    <meta key="My Server Name">Apache1</meta>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <personality>
 | 
			
		||||
    <file path="/etc/banner.txt">
 | 
			
		||||
        ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
 | 
			
		||||
        dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
 | 
			
		||||
        IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
 | 
			
		||||
        c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
 | 
			
		||||
        QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
 | 
			
		||||
        ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
 | 
			
		||||
        dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
 | 
			
		||||
        c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
 | 
			
		||||
        b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
 | 
			
		||||
    </file>
 | 
			
		||||
  </personality>
 | 
			
		||||
</server>
 | 
			
		||||
							
								
								
									
										16
									
								
								doc/api_samples/os-evacuate/server-post-resp.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								doc/api_samples/os-evacuate/server-post-resp.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "server": {
 | 
			
		||||
        "adminPass": "xjDVAYHmc34s",
 | 
			
		||||
        "id": "784f5005-bec9-4c22-8c42-5a7dcba88d82",
 | 
			
		||||
        "links": [
 | 
			
		||||
            {
 | 
			
		||||
                "href": "http://openstack.example.com/v2/openstack/servers/784f5005-bec9-4c22-8c42-5a7dcba88d82",
 | 
			
		||||
                "rel": "self"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "href": "http://openstack.example.com/openstack/servers/784f5005-bec9-4c22-8c42-5a7dcba88d82",
 | 
			
		||||
                "rel": "bookmark"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								doc/api_samples/os-evacuate/server-post-resp.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								doc/api_samples/os-evacuate/server-post-resp.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version='1.0' encoding='UTF-8'?>
 | 
			
		||||
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="70b4263f-bae1-4ac4-a1c5-e5bb193767bc" adminPass="ttv8YPD3tRPm">
 | 
			
		||||
  <metadata/>
 | 
			
		||||
  <atom:link href="http://openstack.example.com/v2/openstack/servers/70b4263f-bae1-4ac4-a1c5-e5bb193767bc" rel="self"/>
 | 
			
		||||
  <atom:link href="http://openstack.example.com/openstack/servers/70b4263f-bae1-4ac4-a1c5-e5bb193767bc" rel="bookmark"/>
 | 
			
		||||
</server>
 | 
			
		||||
@@ -40,6 +40,7 @@
 | 
			
		||||
    "compute_extension:createserverext": "",
 | 
			
		||||
    "compute_extension:deferred_delete": "",
 | 
			
		||||
    "compute_extension:disk_config": "",
 | 
			
		||||
    "compute_extension:evacuate": "rule:admin_api",
 | 
			
		||||
    "compute_extension:extended_server_attributes": "rule:admin_api",
 | 
			
		||||
    "compute_extension:extended_status": "",
 | 
			
		||||
    "compute_extension:extended_availability_zone": "",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								nova/api/openstack/compute/contrib/evacuate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								nova/api/openstack/compute/contrib/evacuate.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
#   Copyright 2013 OpenStack, LLC.
 | 
			
		||||
#
 | 
			
		||||
#   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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from webob import exc
 | 
			
		||||
 | 
			
		||||
from nova.api.openstack import common
 | 
			
		||||
from nova.api.openstack import extensions
 | 
			
		||||
from nova.api.openstack import wsgi
 | 
			
		||||
from nova import compute
 | 
			
		||||
from nova import exception
 | 
			
		||||
from nova.openstack.common import log as logging
 | 
			
		||||
from nova import utils
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
authorize = extensions.extension_authorizer('compute', 'evacuate')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Controller(wsgi.Controller):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(Controller, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.compute_api = compute.API()
 | 
			
		||||
 | 
			
		||||
    @wsgi.action('evacuate')
 | 
			
		||||
    def _evacuate(self, req, id, body):
 | 
			
		||||
        """
 | 
			
		||||
        Permit admins to evacuate a server from a failed host
 | 
			
		||||
        to a new one.
 | 
			
		||||
        """
 | 
			
		||||
        context = req.environ["nova.context"]
 | 
			
		||||
        if not context.is_admin:
 | 
			
		||||
            msg = _("Instance evacuate is admin only functionality")
 | 
			
		||||
            raise exc.HTTPForbidden(explanation=msg)
 | 
			
		||||
        authorize(context)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            if len(body) != 1:
 | 
			
		||||
                raise exc.HTTPBadRequest(_("Malformed request body"))
 | 
			
		||||
 | 
			
		||||
            evacuate_body = body["evacuate"]
 | 
			
		||||
            host = evacuate_body["host"]
 | 
			
		||||
            on_shared_storage = utils.bool_from_str(
 | 
			
		||||
                                            evacuate_body["onSharedStorage"])
 | 
			
		||||
 | 
			
		||||
            if 'adminPass' in evacuate_body:
 | 
			
		||||
                # check that if requested to evacuate server on shared storage
 | 
			
		||||
                # password not specified
 | 
			
		||||
                if on_shared_storage:
 | 
			
		||||
                    msg = _("admin password can't be changed on existing disk")
 | 
			
		||||
                    raise exc.HTTPBadRequest(explanation=msg)
 | 
			
		||||
 | 
			
		||||
                password = evacuate_body['adminPass']
 | 
			
		||||
            elif not on_shared_storage:
 | 
			
		||||
                password = utils.generate_password()
 | 
			
		||||
 | 
			
		||||
        except (TypeError, KeyError):
 | 
			
		||||
            msg = _("host and onSharedStorage must be specified.")
 | 
			
		||||
            raise exc.HTTPBadRequest(explanation=msg)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            instance = self.compute_api.get(context, id)
 | 
			
		||||
            self.compute_api.evacuate(context, instance, host,
 | 
			
		||||
                                      on_shared_storage, password)
 | 
			
		||||
        except exception.InstanceInvalidState as state_error:
 | 
			
		||||
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 | 
			
		||||
                    'evacuate')
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            msg = _("Error in evacuate, %s") % e
 | 
			
		||||
            LOG.exception(msg, instance=instance)
 | 
			
		||||
            raise exc.HTTPBadRequest(explanation=msg)
 | 
			
		||||
 | 
			
		||||
        if password:
 | 
			
		||||
            return {'adminPass': password}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Evacuate(extensions.ExtensionDescriptor):
 | 
			
		||||
    """Enables server evacuation."""
 | 
			
		||||
 | 
			
		||||
    name = "Evacuate"
 | 
			
		||||
    alias = "os-evacuate"
 | 
			
		||||
    namespace = "http://docs.openstack.org/compute/ext/evacuate/api/v2"
 | 
			
		||||
    updated = "2013-01-06T00:00:00+00:00"
 | 
			
		||||
 | 
			
		||||
    def get_controller_extensions(self):
 | 
			
		||||
        controller = Controller()
 | 
			
		||||
        extension = extensions.ControllerExtension(self, 'servers', controller)
 | 
			
		||||
        return [extension]
 | 
			
		||||
							
								
								
									
										156
									
								
								nova/tests/api/openstack/compute/contrib/test_evacuate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								nova/tests/api/openstack/compute/contrib/test_evacuate.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
#   Copyright 2013 OpenStack LLC.
 | 
			
		||||
#
 | 
			
		||||
#   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 uuid
 | 
			
		||||
 | 
			
		||||
import webob
 | 
			
		||||
 | 
			
		||||
from nova.compute import api as compute_api
 | 
			
		||||
from nova.compute import vm_states
 | 
			
		||||
from nova import context
 | 
			
		||||
from nova.openstack.common import cfg
 | 
			
		||||
from nova.openstack.common import jsonutils
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova.tests.api.openstack import fakes
 | 
			
		||||
 | 
			
		||||
CONF = cfg.CONF
 | 
			
		||||
CONF.import_opt('password_length', 'nova.utils')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fake_compute_api(*args, **kwargs):
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fake_compute_api_get(self, context, instance_id):
 | 
			
		||||
    return {
 | 
			
		||||
        'id': 1,
 | 
			
		||||
        'uuid': instance_id,
 | 
			
		||||
        'vm_state': vm_states.ACTIVE,
 | 
			
		||||
        'task_state': None, 'host': 'host1'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvacuateTest(test.TestCase):
 | 
			
		||||
 | 
			
		||||
    _methods = ('resize', 'evacuate')
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(EvacuateTest, self).setUp()
 | 
			
		||||
        self.stubs.Set(compute_api.API, 'get', fake_compute_api_get)
 | 
			
		||||
        self.UUID = uuid.uuid4()
 | 
			
		||||
        for _method in self._methods:
 | 
			
		||||
            self.stubs.Set(compute_api.API, _method, fake_compute_api)
 | 
			
		||||
 | 
			
		||||
    def test_evacuate_instance_with_no_target(self):
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        ctxt.user_id = 'fake'
 | 
			
		||||
        ctxt.project_id = 'fake'
 | 
			
		||||
        ctxt.is_admin = True
 | 
			
		||||
        app = fakes.wsgi_app(fake_auth_context=ctxt)
 | 
			
		||||
        req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID)
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
        req.body = jsonutils.dumps({
 | 
			
		||||
            'evacuate': {
 | 
			
		||||
                'onSharedStorage': 'False',
 | 
			
		||||
                'adminPass': 'MyNewPass'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        req.content_type = 'application/json'
 | 
			
		||||
        res = req.get_response(app)
 | 
			
		||||
        self.assertEqual(res.status_int, 400)
 | 
			
		||||
 | 
			
		||||
    def test_evacuate_instance_with_target(self):
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        ctxt.user_id = 'fake'
 | 
			
		||||
        ctxt.project_id = 'fake'
 | 
			
		||||
        ctxt.is_admin = True
 | 
			
		||||
        app = fakes.wsgi_app(fake_auth_context=ctxt)
 | 
			
		||||
        uuid = self.UUID
 | 
			
		||||
        req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid)
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
        req.body = jsonutils.dumps({
 | 
			
		||||
            'evacuate': {
 | 
			
		||||
                'host': 'my_host',
 | 
			
		||||
                'onSharedStorage': 'false',
 | 
			
		||||
                'adminPass': 'MyNewPass'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        req.content_type = 'application/json'
 | 
			
		||||
 | 
			
		||||
        def fake_update(inst, context, instance,
 | 
			
		||||
                        task_state, expected_task_state):
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(compute_api.API, 'update', fake_update)
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(app)
 | 
			
		||||
        self.assertEqual(resp.status_int, 200)
 | 
			
		||||
        resp_json = jsonutils.loads(resp.body)
 | 
			
		||||
        self.assertEqual("MyNewPass", resp_json['adminPass'])
 | 
			
		||||
 | 
			
		||||
    def test_evacuate_shared_and_pass(self):
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        ctxt.user_id = 'fake'
 | 
			
		||||
        ctxt.project_id = 'fake'
 | 
			
		||||
        ctxt.is_admin = True
 | 
			
		||||
        app = fakes.wsgi_app(fake_auth_context=ctxt)
 | 
			
		||||
        uuid = self.UUID
 | 
			
		||||
        req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid)
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
        req.body = jsonutils.dumps({
 | 
			
		||||
            'evacuate': {
 | 
			
		||||
                'host': 'my_host',
 | 
			
		||||
                'onSharedStorage': 'True',
 | 
			
		||||
                'adminPass': 'MyNewPass'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        req.content_type = 'application/json'
 | 
			
		||||
 | 
			
		||||
        def fake_update(inst, context, instance,
 | 
			
		||||
                        task_state, expected_task_state):
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(compute_api.API, 'update', fake_update)
 | 
			
		||||
 | 
			
		||||
        res = req.get_response(app)
 | 
			
		||||
        self.assertEqual(res.status_int, 400)
 | 
			
		||||
 | 
			
		||||
    def test_evacuate_not_shared_pass_generated(self):
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        ctxt.user_id = 'fake'
 | 
			
		||||
        ctxt.project_id = 'fake'
 | 
			
		||||
        ctxt.is_admin = True
 | 
			
		||||
        app = fakes.wsgi_app(fake_auth_context=ctxt)
 | 
			
		||||
        uuid = self.UUID
 | 
			
		||||
        req = webob.Request.blank('/v2/fake/servers/%s/action' % uuid)
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
        req.body = jsonutils.dumps({
 | 
			
		||||
            'evacuate': {
 | 
			
		||||
                'host': 'my_host',
 | 
			
		||||
                'onSharedStorage': 'False',
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        req.content_type = 'application/json'
 | 
			
		||||
 | 
			
		||||
        def fake_update(inst, context, instance,
 | 
			
		||||
                        task_state, expected_task_state):
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(compute_api.API, 'update', fake_update)
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(app)
 | 
			
		||||
        self.assertEqual(resp.status_int, 200)
 | 
			
		||||
        resp_json = jsonutils.loads(resp.body)
 | 
			
		||||
        self.assertEqual(CONF.password_length, len(resp_json['adminPass']))
 | 
			
		||||
@@ -167,6 +167,7 @@ class ExtensionControllerTest(ExtensionTestCase):
 | 
			
		||||
            "DeferredDelete",
 | 
			
		||||
            "DiskConfig",
 | 
			
		||||
            "ExtendedAvailabilityZone",
 | 
			
		||||
            "Evacuate",
 | 
			
		||||
            "ExtendedStatus",
 | 
			
		||||
            "ExtendedServerAttributes",
 | 
			
		||||
            "FixedIPs",
 | 
			
		||||
 
 | 
			
		||||
@@ -117,6 +117,7 @@ policy_data = """
 | 
			
		||||
    "compute_extension:createserverext": "",
 | 
			
		||||
    "compute_extension:deferred_delete": "",
 | 
			
		||||
    "compute_extension:disk_config": "",
 | 
			
		||||
    "compute_extension:evacuate": "",
 | 
			
		||||
    "compute_extension:extended_server_attributes": "",
 | 
			
		||||
    "compute_extension:extended_status": "",
 | 
			
		||||
    "compute_extension:extended_availability_zone": "",
 | 
			
		||||
 
 | 
			
		||||
@@ -184,6 +184,14 @@
 | 
			
		||||
            "namespace": "http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1",
 | 
			
		||||
            "updated": "%(timestamp)s"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "alias": "os-evacuate",
 | 
			
		||||
            "description": "%(text)s",
 | 
			
		||||
            "links": [],
 | 
			
		||||
            "name": "Evacuate",
 | 
			
		||||
            "namespace": "http://docs.openstack.org/compute/ext/evacuate/api/v2",
 | 
			
		||||
            "updated": "%(timestamp)s"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "alias": "os-fixed-ips",
 | 
			
		||||
            "description": "Fixed IPs support.",
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +69,9 @@
 | 
			
		||||
  <extension alias="os-deferred-delete" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1" name="DeferredDelete">
 | 
			
		||||
    <description>%(text)s</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
  <extension alias="os-evacuate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/evacuate/api/v2" name="Evacuate">
 | 
			
		||||
    <description>%(text)s</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
  <extension alias="os-fixed-ips" name="FixedIPs" namespace="http://docs.openstack.org/compute/ext/fixed_ips/api/v2" updated="2012-10-18T13:25:27-06:00">
 | 
			
		||||
    <description>Fixed IPs support.</description>
 | 
			
		||||
  </extension>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "evacuate": {
 | 
			
		||||
        "host": "%(host)s",
 | 
			
		||||
        "adminPass": "%(adminPass)s",
 | 
			
		||||
        "onSharedStorage": "%(onSharedStorage)s"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<evacuate xmlns="http://docs.openstack.org/compute/api/v2"
 | 
			
		||||
   host="%(host)s"
 | 
			
		||||
   adminPass="%(adminPass)s"
 | 
			
		||||
   onSharedStorage="%(onSharedStorage)s"/>
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "adminPass": "%(password)s"
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
<adminPass>%(password)s</adminPass>
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "server" : {
 | 
			
		||||
        "name" : "new-server-test",
 | 
			
		||||
        "imageRef" : "%(host)s/openstack/images/%(image_id)s",
 | 
			
		||||
        "flavorRef" : "%(host)s/openstack/flavors/1",
 | 
			
		||||
        "metadata" : {
 | 
			
		||||
            "My Server Name" : "Apache1"
 | 
			
		||||
        },
 | 
			
		||||
        "personality" : [
 | 
			
		||||
            {
 | 
			
		||||
                "path" : "/etc/banner.txt",
 | 
			
		||||
                "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="%(host)s/openstack/images/%(image_id)s" flavorRef="%(host)s/openstack/flavors/1" name="new-server-test">
 | 
			
		||||
  <metadata>
 | 
			
		||||
    <meta key="My Server Name">Apache1</meta>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <personality>
 | 
			
		||||
    <file path="/etc/banner.txt">
 | 
			
		||||
        ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
 | 
			
		||||
        dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
 | 
			
		||||
        IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
 | 
			
		||||
        c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
 | 
			
		||||
        QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
 | 
			
		||||
        ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
 | 
			
		||||
        dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
 | 
			
		||||
        c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
 | 
			
		||||
        b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
 | 
			
		||||
    </file>
 | 
			
		||||
  </personality>
 | 
			
		||||
</server>
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "server": {
 | 
			
		||||
        "adminPass": "%(password)s",
 | 
			
		||||
        "id": "%(id)s",
 | 
			
		||||
        "links": [
 | 
			
		||||
            {
 | 
			
		||||
                "href": "%(host)s/v2/openstack/servers/%(uuid)s",
 | 
			
		||||
                "rel": "self"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "href": "%(host)s/openstack/servers/%(uuid)s",
 | 
			
		||||
                "rel": "bookmark"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version='1.0' encoding='UTF-8'?>
 | 
			
		||||
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="%(id)s" adminPass="%(password)s">
 | 
			
		||||
  <metadata/>
 | 
			
		||||
  <atom:link href="%(host)s/v2/openstack/servers/%(uuid)s" rel="self"/>
 | 
			
		||||
  <atom:link href="%(host)s/openstack/servers/%(uuid)s" rel="bookmark"/>
 | 
			
		||||
</server>
 | 
			
		||||
@@ -42,6 +42,7 @@ from nova.openstack.common.log import logging
 | 
			
		||||
from nova.openstack.common import timeutils
 | 
			
		||||
import nova.quota
 | 
			
		||||
from nova.scheduler import driver
 | 
			
		||||
from nova.servicegroup import api as service_group_api
 | 
			
		||||
from nova import test
 | 
			
		||||
from nova.tests.api.openstack.compute.contrib import test_fping
 | 
			
		||||
from nova.tests.api.openstack.compute.contrib import test_networks
 | 
			
		||||
@@ -2881,3 +2882,36 @@ class ExtendedAvailabilityZoneJsonTests(ServersSampleBase):
 | 
			
		||||
 | 
			
		||||
class ExtendedAvailabilityZoneXmlTests(ExtendedAvailabilityZoneJsonTests):
 | 
			
		||||
    ctype = 'xml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvacuateJsonTest(ServersSampleBase):
 | 
			
		||||
 | 
			
		||||
    extension_name = ("nova.api.openstack.compute.contrib"
 | 
			
		||||
                      ".evacuate.Evacuate")
 | 
			
		||||
 | 
			
		||||
    def test_server_evacuate(self):
 | 
			
		||||
        uuid = self._post_server()
 | 
			
		||||
 | 
			
		||||
        req_subs = {
 | 
			
		||||
            'host': 'TargetHost',
 | 
			
		||||
            "adminPass": "MySecretPass",
 | 
			
		||||
            "onSharedStorage": 'False'
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        def fake_service_is_up(self, service):
 | 
			
		||||
            """Simulate validation of instance host is down."""
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(service_group_api.API, 'service_is_up',
 | 
			
		||||
                       fake_service_is_up)
 | 
			
		||||
 | 
			
		||||
        response = self._do_post('servers/%s/action' % uuid,
 | 
			
		||||
                                 'server-evacuate-req', req_subs)
 | 
			
		||||
        self.assertEqual(response.status, 200)
 | 
			
		||||
        subs = self._get_regexes()
 | 
			
		||||
        return self._verify_response('server-evacuate-resp', subs,
 | 
			
		||||
                                     response)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EvacuateXmlTest(EvacuateJsonTest):
 | 
			
		||||
    ctype = 'xml'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user