Register CloudServer as OS::Nova::Server
Register CloudServer as OS::Nova::Server so it waits on the RackConnect and Managed Cloud automations. Remove the extra attributes and properties from CloudServer so it is compatible with OS::Nova::Server. Change-Id: Idded2458291d8321f0ff404be360322d984595e6
This commit is contained in:
parent
095b641958
commit
158e27c538
|
@ -11,17 +11,12 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.common.i18n import _LW
|
||||
from heat.engine import attributes
|
||||
from heat.engine import properties
|
||||
from heat.engine.resources import server
|
||||
from heat.engine import support
|
||||
|
||||
try:
|
||||
import pyrax # noqa
|
||||
|
@ -35,11 +30,6 @@ LOG = logging.getLogger(__name__)
|
|||
class CloudServer(server.Server):
|
||||
"""Resource for Rackspace Cloud Servers."""
|
||||
|
||||
support_status = support.SupportStatus(
|
||||
support.DEPRECATED,
|
||||
_('Use OS::Nova::Server instead.'),
|
||||
)
|
||||
|
||||
# Managed Cloud automation statuses
|
||||
MC_STATUS_IN_PROGRESS = 'In Progress'
|
||||
MC_STATUS_COMPLETE = 'Complete'
|
||||
|
@ -51,78 +41,11 @@ class CloudServer(server.Server):
|
|||
RC_STATUS_FAILED = 'FAILED'
|
||||
RC_STATUS_UNPROCESSABLE = 'UNPROCESSABLE'
|
||||
|
||||
# Admin Pass Properties
|
||||
SAVE_ADMIN_PASS = 'save_admin_pass'
|
||||
|
||||
properties_schema = copy.deepcopy(server.Server.properties_schema)
|
||||
properties_schema.update(
|
||||
{
|
||||
SAVE_ADMIN_PASS: properties.Schema(
|
||||
properties.Schema.BOOLEAN,
|
||||
_('True if the system should remember the admin password; '
|
||||
'False otherwise.'),
|
||||
default=False
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
NEW_ATTRIBUTES = (
|
||||
DISTRO, PRIVATE_IP_V4, ADMIN_PASS_ATTR,
|
||||
) = (
|
||||
'distro', 'privateIPv4', 'admin_pass',
|
||||
)
|
||||
|
||||
ATTRIBUTES = copy.deepcopy(server.Server.ATTRIBUTES)
|
||||
ATTRIBUTES += NEW_ATTRIBUTES
|
||||
|
||||
attributes_schema = copy.deepcopy(server.Server.attributes_schema)
|
||||
attributes_schema.update(
|
||||
{
|
||||
DISTRO: attributes.Schema(
|
||||
_('The Linux distribution on the server.')
|
||||
),
|
||||
PRIVATE_IP_V4: attributes.Schema(
|
||||
_('The private IPv4 address of the server.')
|
||||
),
|
||||
ADMIN_PASS_ATTR: attributes.Schema(
|
||||
_('The administrator password for the server.'),
|
||||
cache_mode=attributes.Schema.CACHE_NONE
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(CloudServer, self).__init__(name, json_snippet, stack)
|
||||
self._server = None
|
||||
self._distro = None
|
||||
self._image = None
|
||||
self._managed_cloud_started_event_sent = False
|
||||
self._rack_connect_started_event_sent = False
|
||||
|
||||
@property
|
||||
def server(self):
|
||||
"""Return the Cloud Server object."""
|
||||
if self._server is None:
|
||||
self._server = self.nova().servers.get(self.resource_id)
|
||||
return self._server
|
||||
|
||||
@property
|
||||
def distro(self):
|
||||
"""Return the Linux distribution for this server."""
|
||||
image = self.properties.get(self.IMAGE)
|
||||
if self._distro is None and image:
|
||||
image_data = self.nova().images.get(self.image)
|
||||
self._distro = image_data.metadata['os_distro']
|
||||
return self._distro
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
"""Return the server's image ID."""
|
||||
image = self.properties.get(self.IMAGE)
|
||||
if image and self._image is None:
|
||||
self._image = self.client_plugin('glance').get_image_id(image)
|
||||
return self._image
|
||||
|
||||
def _config_drive(self):
|
||||
user_data = self.properties.get(self.USER_DATA)
|
||||
config_drive = self.properties.get(self.CONFIG_DRIVE)
|
||||
|
@ -219,51 +142,9 @@ class CloudServer(server.Server):
|
|||
|
||||
return True
|
||||
|
||||
def _resolve_attribute(self, name):
|
||||
if name == self.DISTRO:
|
||||
return self.distro
|
||||
if name == self.PRIVATE_IP_V4:
|
||||
return self.client_plugin().get_ip(self.server, 'private', 4)
|
||||
if name == self.ADMIN_PASS_ATTR:
|
||||
return self.data().get(self.ADMIN_PASS_ATTR, '')
|
||||
return super(CloudServer, self)._resolve_attribute(name)
|
||||
|
||||
def handle_create(self):
|
||||
server = super(CloudServer, self).handle_create()
|
||||
|
||||
# Server will not have an adminPass attribute if Nova's
|
||||
# "enable_instance_password" config option is turned off
|
||||
if (self.properties.get(self.SAVE_ADMIN_PASS) and
|
||||
hasattr(server, 'adminPass') and server.adminPass):
|
||||
self.data_set(self.ADMIN_PASS,
|
||||
server.adminPass,
|
||||
redact=True)
|
||||
|
||||
return server
|
||||
|
||||
def handle_check(self):
|
||||
server = self._check_server_status()
|
||||
checks = []
|
||||
|
||||
if 'rack_connect' in self.context.roles:
|
||||
rc_status = self._check_rack_connect_complete(server)
|
||||
checks.append(
|
||||
{'attr': 'rackconnect complete', 'expected': True,
|
||||
'current': rc_status}
|
||||
)
|
||||
|
||||
if 'rax_managed' in self.context.roles:
|
||||
mc_status = self._check_managed_cloud_complete(server)
|
||||
checks.append(
|
||||
{'attr': 'managed_cloud complete', 'expected': True,
|
||||
'current': mc_status}
|
||||
)
|
||||
|
||||
self._verify_check_conditions(checks)
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {'Rackspace::Cloud::Server': CloudServer}
|
||||
return {'OS::Nova::Server': CloudServer}
|
||||
|
||||
|
||||
def available_resource_mapping():
|
||||
|
|
|
@ -44,7 +44,7 @@ wp_template = '''
|
|||
},
|
||||
"Resources" : {
|
||||
"WebServer": {
|
||||
"Type": "Rackspace::Cloud::Server",
|
||||
"Type": "OS::Nova::Server",
|
||||
"Properties": {
|
||||
"image" : "CentOS 5.2",
|
||||
"flavor" : "256 MB Server",
|
||||
|
@ -75,7 +75,7 @@ class CloudServersTest(common.HeatTestCase):
|
|||
# Test environment may not have pyrax client library installed and if
|
||||
# pyrax is not installed resource class would not be registered.
|
||||
# So register resource provider class explicitly for unit testing.
|
||||
resource._register_class("Rackspace::Cloud::Server",
|
||||
resource._register_class("OS::Nova::Server",
|
||||
cloud_server.CloudServer)
|
||||
|
||||
def _mock_get_image_id_success(self, imageId):
|
||||
|
@ -325,156 +325,6 @@ class CloudServersTest(common.HeatTestCase):
|
|||
self.assertEqual('Error: Unknown Managed Cloud automation status: FOO',
|
||||
six.text_type(exc))
|
||||
|
||||
def _prepare_server_check(self):
|
||||
templ, stack = self._setup_test_stack('server_check')
|
||||
server = self.fc.servers.list()[1]
|
||||
res = stack['WebServer']
|
||||
res.nova = mock.Mock()
|
||||
res.nova().servers.get = mock.Mock(return_value=server)
|
||||
return res
|
||||
|
||||
def test_check_rackconnect(self):
|
||||
res = self._prepare_server_check()
|
||||
res._check_rack_connect_complete = mock.Mock(return_value=True)
|
||||
self.ctx.roles = ['rack_connect']
|
||||
|
||||
scheduler.TaskRunner(res.check)()
|
||||
self.assertEqual((res.CHECK, res.COMPLETE), res.state)
|
||||
|
||||
def test_check_rackconnect_failure(self):
|
||||
self.ctx.roles = ['rack_connect']
|
||||
res = self._prepare_server_check()
|
||||
res._check_active = mock.Mock(return_value=True)
|
||||
res._check_rack_connect_complete = mock.Mock(return_value=False)
|
||||
|
||||
exc = self.assertRaises(exception.ResourceFailure,
|
||||
scheduler.TaskRunner(res.check))
|
||||
self.assertIn('False', six.text_type(exc))
|
||||
self.assertEqual((res.CHECK, res.FAILED), res.state)
|
||||
|
||||
def test_check_managed_cloud(self):
|
||||
res = self._prepare_server_check()
|
||||
res._check_managed_cloud_complete = mock.Mock(return_value=True)
|
||||
self.ctx.roles = ['rax_managed']
|
||||
|
||||
scheduler.TaskRunner(res.check)()
|
||||
self.assertEqual((res.CHECK, res.COMPLETE), res.state)
|
||||
|
||||
def test_check_managed_cloud_failure(self):
|
||||
res = self._prepare_server_check()
|
||||
res._check_managed_cloud_complete = mock.Mock(return_value=False)
|
||||
self.ctx.roles = ['rax_managed']
|
||||
|
||||
exc = self.assertRaises(exception.ResourceFailure,
|
||||
scheduler.TaskRunner(res.check))
|
||||
self.assertIn('False', six.text_type(exc))
|
||||
self.assertEqual((res.CHECK, res.FAILED), res.state)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_store_admin_pass_resource_data(self,
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
stack_name = 'admin_pass_s'
|
||||
(t, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
t.t['Resources']['WebServer']['Properties']['save_admin_pass'] = True
|
||||
resource_defns = t.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('image_id')
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(server.ADMIN_PASS,
|
||||
'autogenerated', redact=True)
|
||||
self.assertIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_save_admin_pass_is_false(self, mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
stack_name = 'admin_pass_s'
|
||||
(t, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
t.t['Resources']['WebServer']['Properties']['save_admin_pass'] = False
|
||||
resource_defns = t.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('image_id')
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, mock.ANY)
|
||||
self.assertNotIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_save_admin_pass_defaults_to_false(self,
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
stack_name = 'admin_pass_s'
|
||||
(t, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
t.t['Resources']['WebServer']['Properties']['save_admin_pass'] = None
|
||||
resource_defns = t.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('image_id')
|
||||
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, mock.ANY)
|
||||
self.assertNotIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_without_adminPass_attribute(self,
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
stack_name = 'admin_pass_s'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('image_id')
|
||||
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, redact=mock.ANY)
|
||||
self.assertNotIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data')
|
||||
def test_server_handles_server_without_password(self, mock_data_get):
|
||||
mock_data_get.return_value = {}
|
||||
stack_name = 'admin_pass_s'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
self.assertEqual('', server.FnGetAtt('admin_pass'))
|
||||
|
||||
@mock.patch.object(resource.Resource, 'data')
|
||||
def test_server_has_admin_pass_attribute_available(self, mock_data_get):
|
||||
mock_data_get.return_value = {'admin_pass': 'foo'}
|
||||
stack_name = 'admin_pass_s'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
self.assertEqual('foo', server.FnGetAtt('admin_pass'))
|
||||
|
||||
def _test_server_config_drive(self, user_data, config_drive, result):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
stack_name = 'no_user_data'
|
||||
|
|
Loading…
Reference in New Issue