Add support for fetching console logs from servers
Added get_server_console method to fetch the console log from a Server. On clouds that do not expose this feature, a debug line will be logged and an empty string will be returned. Change-Id: Ifb973c7d382118d3a8b6bf406cb7ed463b057180
This commit is contained in:
parent
c40cc19381
commit
7b3b959882
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- Added get_server_console method to fetch the console
|
||||
log from a Server. On clouds that do not expose this
|
||||
feature, a debug line will be logged and an empty
|
||||
string will be returned.
|
@ -152,6 +152,11 @@ class ServerListSecurityGroups(task_manager.Task):
|
||||
return client.nova_client.servers.list_security_group(**self.args)
|
||||
|
||||
|
||||
class ServerConsoleGet(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.nova_client.servers.get_console_output(**self.args)
|
||||
|
||||
|
||||
class ServerGet(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.nova_client.servers.get(**self.args)
|
||||
|
@ -1969,6 +1969,40 @@ class OpenStackCloud(object):
|
||||
return _utils._get_entity(
|
||||
self.search_security_groups, name_or_id, filters)
|
||||
|
||||
def get_server_console(self, server, length=None):
|
||||
"""Get the console log for a server.
|
||||
|
||||
:param server: The server to fetch the console log for. Can be either
|
||||
a server dict or the Name or ID of the server.
|
||||
:param int length: The number of lines you would like to retrieve from
|
||||
the end of the log. (optional, defaults to all)
|
||||
|
||||
:returns: A string containing the text of the console log or an
|
||||
empty string if the cloud does not support console logs.
|
||||
:raises: OpenStackCloudException if an invalid server argument is given
|
||||
or if something else unforseen happens
|
||||
"""
|
||||
|
||||
if not isinstance(server, dict):
|
||||
server = self.get_server(server)
|
||||
|
||||
if not server:
|
||||
raise OpenStackCloudException(
|
||||
"Console log requested for invalid server")
|
||||
|
||||
try:
|
||||
return self.manager.submitTask(
|
||||
_tasks.ServerConsoleGet(server=server['id'], length=length),
|
||||
raw=True)
|
||||
except nova_exceptions.BadRequest:
|
||||
return ""
|
||||
except OpenStackCloudException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise OpenStackCloudException(
|
||||
"Unable to get console log for {server}: {exception}".format(
|
||||
server=server['id'], exception=str(e)))
|
||||
|
||||
def get_server(self, name_or_id=None, filters=None, detailed=False):
|
||||
"""Get a server by name or ID.
|
||||
|
||||
|
@ -92,7 +92,7 @@ class Task(object):
|
||||
elif (not isinstance(self._result, bool) and
|
||||
not isinstance(self._result, int) and
|
||||
not isinstance(self._result, float) and
|
||||
not isinstance(self._result, str) and
|
||||
not isinstance(self._result, six.string_types) and
|
||||
not isinstance(self._result, set) and
|
||||
not isinstance(self._result, tuple) and
|
||||
not isinstance(self._result, types.GeneratorType)):
|
||||
|
@ -19,9 +19,12 @@ test_compute
|
||||
Functional tests for `shade` compute methods.
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
from shade import exc
|
||||
from shade.tests.functional import base
|
||||
from shade.tests.functional.util import pick_flavor, pick_image
|
||||
from shade import _utils
|
||||
|
||||
|
||||
class TestCompute(base.BaseFunctionalTestCase):
|
||||
@ -67,6 +70,40 @@ class TestCompute(base.BaseFunctionalTestCase):
|
||||
self.demo_cloud.delete_server(self.server_name, wait=True))
|
||||
self.assertIsNone(self.demo_cloud.get_server(self.server_name))
|
||||
|
||||
def test_get_server_console(self):
|
||||
self.addCleanup(self._cleanup_servers_and_volumes, self.server_name)
|
||||
server = self.demo_cloud.create_server(
|
||||
name=self.server_name,
|
||||
image=self.image,
|
||||
flavor=self.flavor,
|
||||
wait=True)
|
||||
for _ in _utils._iterate_timeout(
|
||||
5, "Did not get more than 0 lines in the console log"):
|
||||
log = self.demo_cloud.get_server_console(server=server)
|
||||
self.assertTrue(isinstance(log, six.string_types))
|
||||
if len(log) > 0:
|
||||
break
|
||||
|
||||
def test_get_server_console_name_or_id(self):
|
||||
self.addCleanup(self._cleanup_servers_and_volumes, self.server_name)
|
||||
self.demo_cloud.create_server(
|
||||
name=self.server_name,
|
||||
image=self.image,
|
||||
flavor=self.flavor,
|
||||
wait=True)
|
||||
for _ in _utils._iterate_timeout(
|
||||
5, "Did not get more than 0 lines in the console log"):
|
||||
log = self.demo_cloud.get_server_console(server=self.server_name)
|
||||
self.assertTrue(isinstance(log, six.string_types))
|
||||
if len(log) > 0:
|
||||
break
|
||||
|
||||
def test_get_server_console_bad_server(self):
|
||||
self.assertRaises(
|
||||
exc.OpenStackCloudException,
|
||||
self.demo_cloud.get_server_console,
|
||||
server=self.server_name)
|
||||
|
||||
def test_create_and_delete_server_with_admin_pass(self):
|
||||
self.addCleanup(self._cleanup_servers_and_volumes, self.server_name)
|
||||
server = self.demo_cloud.create_server(
|
||||
|
61
shade/tests/unit/test_server_console.py
Normal file
61
shade/tests/unit/test_server_console.py
Normal file
@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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 mock
|
||||
import novaclient.exceptions as nova_exceptions
|
||||
|
||||
import shade
|
||||
from shade.tests.unit import base
|
||||
from shade.tests import fakes
|
||||
|
||||
|
||||
class TestServerConsole(base.TestCase):
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
def test_get_server_console_dict(self, mock_nova):
|
||||
server = dict(id='12345')
|
||||
self.cloud.get_server_console(server)
|
||||
|
||||
mock_nova.servers.list.assert_not_called()
|
||||
mock_nova.servers.get_console_output.assert_called_once_with(
|
||||
server='12345', length=None)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'has_service')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
def test_get_server_console_name_or_id(self, mock_nova, mock_has_service):
|
||||
server = '12345'
|
||||
|
||||
fake_server = fakes.FakeServer(server, '', 'ACTIVE')
|
||||
mock_nova.servers.get.return_value = fake_server
|
||||
mock_nova.servers.list.return_value = [fake_server]
|
||||
mock_has_service.return_value = False
|
||||
|
||||
self.cloud.get_server_console(server)
|
||||
|
||||
mock_nova.servers.get_console_output.assert_called_once_with(
|
||||
server='12345', length=None)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
def test_get_server_console_no_console(self, mock_nova):
|
||||
server = dict(id='12345')
|
||||
exc = nova_exceptions.BadRequest(
|
||||
'There is no such action: os-getConsoleOutput')
|
||||
mock_nova.servers.get_console_output.side_effect = exc
|
||||
log = self.cloud.get_server_console(server)
|
||||
|
||||
self.assertEqual('', log)
|
||||
mock_nova.servers.list.assert_not_called()
|
||||
mock_nova.servers.get_console_output.assert_called_once_with(
|
||||
server='12345', length=None)
|
Loading…
Reference in New Issue
Block a user