Skip to content

Commit

Permalink
Add admin_pass as attribute to CloudServer
Browse files Browse the repository at this point in the history
This stores the admin password returned by nova() as an encrypted
property on the CloudServer to allow template authors to display them
as outputs of a stack.

Co-Authored-By: Anderson Mesquita <andersonvom@gmail.com>
Implements: blueprint custom-adminpass (partial)
Change-Id: Ic11f1ddcd114631b92f87d5760ab7a6761ba40e4
  • Loading branch information
arbylee and andersonvom committed Apr 7, 2014
1 parent 5b57317 commit 4e7280c
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 6 deletions.
20 changes: 20 additions & 0 deletions contrib/rackspace/rackspace/resources/cloud_server.py
Expand Up @@ -118,6 +118,7 @@ class CloudServer(server.Server):
{
'distro': _('The Linux distribution on the server.'),
'privateIPv4': _('The private IPv4 address of the server.'),
'admin_pass': _('The administrator password for the server.'),
}
)

Expand Down Expand Up @@ -455,8 +456,27 @@ def _resolve_attribute(self, name):
return self.distro
if name == 'privateIPv4':
return nova_utils.get_ip(self.server, 'private', 4)
if name == 'admin_pass':
try:
return db_api.resource_data_get(self, self.ADMIN_PASS)
except exception.NotFound:
logger.info(_('Administrator password not'
'found for server: %s') % self.name)
return ''
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 hasattr(server, 'adminPass') and server.adminPass:
db_api.resource_data_set(self, self.ADMIN_PASS,
server.adminPass,
redact=True)

return server


def resource_mapping():
return {'Rackspace::Cloud::Server': CloudServer}
Expand Down
78 changes: 72 additions & 6 deletions contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py
Expand Up @@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.

import mock
import mox
import paramiko

Expand Down Expand Up @@ -153,7 +154,6 @@ def _setup_test_server(self, return_server, name, image_id=None,
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)

return_server.adminPass = "foobar"
server._private_key = rsa_key
server.t = server.stack.resolve_runtime_data(server.t)

Expand Down Expand Up @@ -199,11 +199,8 @@ def _update_test_server(self, return_server, name, exit_code=0):
cloud_server.CloudServer.nova().MultipleTimes().AndReturn(self.fc)

def _mock_metadata_os_distro(self):
image_data = self.m.CreateMockAnything()
image_data.metadata = {'os_distro': 'centos'}
self.m.StubOutWithMock(self.fc.images, 'get')
self.fc.images.get(mox.IgnoreArg()).MultipleTimes().\
AndReturn(image_data)
image_data = mock.Mock(metadata={'os_distro': 'centos'})
self.fc.images.get = mock.Mock(return_value=image_data)

def test_script_raw_userdata(self):
stack_name = 'raw_userdata_s'
Expand Down Expand Up @@ -950,3 +947,72 @@ def test_eof_error_failed(self):
"tries", str(exc))

self.m.VerifyAll()

@mock.patch.object(clients.OpenStackClients, 'nova')
@mock.patch.object(cloud_server.db_api, 'resource_data_set')
def test_create_store_admin_pass_resource_data(self,
mock_data_set,
mock_nova):
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)

server = cloud_server.CloudServer('WebServer',
t['Resources']['WebServer'], stack)
server._sftp_files = mock.Mock()
server._run_ssh_command = mock.Mock(return_value=0)

mock_nova.return_value = self.fc
server.t = server.stack.resolve_runtime_data(server.t)
self.fc.servers.create = mock.Mock(return_value=return_server)

scheduler.TaskRunner(server.create)()
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
'autogenerated', redact=True)
self.assertIn(expected_call, mock_data_set.call_args_list)

@mock.patch.object(clients.OpenStackClients, 'nova')
@mock.patch.object(cloud_server.db_api, 'resource_data_set')
def test_create_without_adminPass_attribute(self,
mock_data_set,
mock_nova):
self._mock_metadata_os_distro()
return_server = self.fc.servers.list()[1]
stack_name = 'admin_pass_s'
(t, stack) = self._setup_test_stack(stack_name)

server = cloud_server.CloudServer('WebServer',
t['Resources']['WebServer'], stack)
server._sftp_files = mock.Mock()
server._run_ssh_command = mock.Mock(return_value=0)

mock_nova.return_value = self.fc
server.t = server.stack.resolve_runtime_data(server.t)
self.fc.servers.create = mock.Mock(return_value=return_server)

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(cloud_server.db_api, 'resource_data_get')
def test_server_handles_server_without_password(self, mock_data_get):
mock_data_get.side_effect = exception.NotFound('foo')
stack_name = 'admin_pass_s'
(t, stack) = self._setup_test_stack(stack_name)

server = cloud_server.CloudServer('WebServer',
t['Resources']['WebServer'], stack)
self.assertEqual('', server.FnGetAtt('admin_pass'))

@mock.patch.object(cloud_server.db_api, 'resource_data_get')
def test_server_has_admin_pass_attribute_available(self, mock_data_get):
mock_data_get.return_value = 'foo'
stack_name = 'admin_pass_s'
(t, stack) = self._setup_test_stack(stack_name)

server = cloud_server.CloudServer('WebServer',
t['Resources']['WebServer'], stack)
self.assertEqual('foo', server.FnGetAtt('admin_pass'))

0 comments on commit 4e7280c

Please sign in to comment.