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:
Jason Dunsmore 2015-02-24 16:09:16 -06:00
parent 095b641958
commit 158e27c538
2 changed files with 3 additions and 272 deletions

View File

@ -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():

View File

@ -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'