Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1034 from zenhack/forward-console
Browse files Browse the repository at this point in the history
Move show_console over to obmd
  • Loading branch information
naved001 authored Sep 5, 2018
2 parents c4057f7 + 00a4063 commit 83c8f6d
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 90 deletions.
32 changes: 4 additions & 28 deletions hil/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,6 @@ def node_power_cycle(node, force=False):
"""
node = get_or_404(model.Node, node)
get_auth_backend().require_project_access(node.project)
if not node.obm_is_enabled():
raise errors.BlockedError("OBM is not enabled")
return _obmd_redirect(node, '/power_cycle')


Expand All @@ -291,8 +289,6 @@ def _node_power_on_off(node, op):
"""
node = get_or_404(model.Node, node)
get_auth_backend().require_project_access(node.project)
if not node.obm_is_enabled():
raise errors.BlockedError("OBM is not enabled")
return _obmd_redirect(node, '/power_' + op)


Expand All @@ -315,8 +311,6 @@ def node_set_bootdev(node, bootdev):
"""Set the node's boot device."""
node = get_or_404(model.Node, node)
get_auth_backend().require_project_access(node.project)
if not node.obm_is_enabled():
raise errors.BlockedError("OBM is not enabled")
return _obmd_redirect(node, '/boot_device')


Expand Down Expand Up @@ -1337,28 +1331,8 @@ def list_active_extensions():
def show_console(nodename):
"""Show the contents of the console log."""
node = get_or_404(model.Node, nodename)
log = node.obm.get_console()
if log is None:
raise errors.NotFoundError(
'The console log for %s does not exist.' % nodename)
return log


@rest_call('PUT', '/node/<nodename>/console', Schema({'nodename': basestring}))
def start_console(nodename):
"""Start logging output from the console."""
node = get_or_404(model.Node, nodename)
node.obm.start_console()


@rest_call('DELETE', '/node/<nodename>/console', Schema({
'nodename': basestring,
}))
def stop_console(nodename):
"""Stop logging output from the console and delete the log."""
node = get_or_404(model.Node, nodename)
node.obm.stop_console()
node.obm.delete_console()
get_auth_backend().require_project_access(node.project)
return _obmd_redirect(node, '/console')


# Helper functions #
Expand Down Expand Up @@ -1402,6 +1376,8 @@ def get_or_404(cls, name):


def _obmd_redirect(node, path):
if not node.obm_is_enabled():
raise errors.BlockedError("OBM is not enabled")
return flask.redirect(
node.obmd_uri + path + '?token=' + node.obmd_node_token,
# 307 is important, since it requires that the client not change
Expand Down
3 changes: 1 addition & 2 deletions hil/cli/client_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import sys
import os
import requests

from hil.client.client import Client, RequestsHTTPClient, KeystoneHTTPClient

Expand Down Expand Up @@ -74,7 +73,7 @@ def setup_http_client():
except (ImportError, KeyError):
pass
# Finally, fall back to no authentication:
http_client = requests.Session()
http_client = RequestsHTTPClient()
return Client(ep, http_client), http_client


Expand Down
24 changes: 9 additions & 15 deletions hil/cli/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,13 @@ def node_console():
@node_console.command(name='show', short_help='Show console')
@click.argument('node')
def node_show_console(node):
"""Display console log for <node>"""
print(client.node.show_console(node))
"""Display console log for <node>

@node_console.command(name='start', short_help='Start console')
@click.argument('node')
def node_start_console(node):
"""Start logging console output from <node>"""
client.node.start_console(node)


@node_console.command(name='stop', short_help='Stop console')
@click.argument('node')
def node_stop_console(node):
"""Stop logging console output from <node> and delete the log"""
client.node.stop_console(node)
This will stream data from the console to standard output; press
Ctrl+C to stop.
"""
try:
for data in client.node.show_console(node):
sys.stdout.write(data)
except KeyboardInterrupt:
pass
26 changes: 11 additions & 15 deletions hil/client/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,28 +168,24 @@ def metadata_delete(self, node, label):

@check_reserved_chars()
def show_console(self, node):
"""Display console log for <node> """
"""Stream the console for <node>.
Unlike most of the other API calls, rather than returning a json
value, this returns an iterator allowing the caller to read the
console in a streaming fashion. The iterator's next() method
returns the next chunk of data from the console, when it is
available.
"""
url = self.object_url('node', node, 'console')
response = self.httpClient.request('GET', url)
# we don't call check_response here because we want to return the
# raw byte stream, rather than converting it to json.
# raw byte stream, rather than reading the whole thing in and
# converting it to json.
if 200 <= response.status_code < 300:
return response.content
return response.body
raise FailedAPICallException(error_type=response.status_code,
message=response.content)

@check_reserved_chars()
def start_console(self, node):
"""Start logging console output from <node> """
url = self.object_url('node', node, 'console')
return self.check_response(self.httpClient.request('PUT', url))

@check_reserved_chars()
def stop_console(self, node):
"""Stop logging console output from <node> and delete the log"""
url = self.object_url('node', node, 'console')
return self.check_response(self.httpClient.request('DELETE', url))

def show_networking_action(self, status_id):
"""Returns the status of the networking action"""
url = self.object_url('networking_action', status_id)
Expand Down
12 changes: 8 additions & 4 deletions tests/integration/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,15 @@ def test_cli(obmd_cfg):

# Test that obm related calls run succesfully
hil('node', 'bootdev', NODE1, 'A')
hil('node', 'console', 'start', NODE1)
output = hil('node', 'console', 'show', NODE1)
assert output.strip('\n') == 'Some console output'

hil('node', 'console', 'stop', NODE1)
console_proc = subprocess.Popen(
['hil', 'node', 'console', 'show', NODE1],
stdout=subprocess.PIPE,
)
for i in range(10):
assert console_proc.stdout.readline() == str(i) + '\n'
console_proc.kill()

hil('node', 'power', 'off', NODE1)
hil('node', 'power', 'on', NODE1)
hil('node', 'power', 'cycle', NODE1)
Expand Down
42 changes: 16 additions & 26 deletions tests/integration/client_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,43 +345,33 @@ def test_metadata_delete(self):
C.node.metadata_set("free_node_0", "EK", "pk")
assert C.node.metadata_delete("free_node_0", "EK") is None

def test_node_start_console(self):
"""(successful) call to node_start_console"""
assert C.node.start_console('manhattan_node_1') is None

def test_node_start_console_reserved_chars(self):
""" test for catching illegal argument characters"""
with pytest.raises(BadArgumentError):
C.node.start_console('node-/%]01')

def test_node_show_console(self):
def test_node_show_console(self, obmd_node):
"""various calls to node_show_console"""

# show console without starting should fail.
# show console without enabling the obm.
with pytest.raises(FailedAPICallException):
C.node.show_console('manhattan_node_1')
C.node.show_console(obmd_node)

C.node.enable_obm(obmd_node)

C.node.start_console('manhattan_node_1')
assert C.node.show_console('manhattan_node_1') == 'Some console output'
# Read in a prefix of the output from the console; the obmd mock driver
# keeps counting forever.
console_stream = C.node.show_console(obmd_node)
expected = '\n'.join([str(i) for i in range(10)])
actual = ''
while len(actual) < len(expected):
actual += console_stream.next()
assert actual.startswith(expected)

C.node.disable_obm(obmd_node)

C.node.stop_console('manhattan_node_1')
with pytest.raises(FailedAPICallException):
C.node.show_console('manhattan_node_1')
C.node.show_console(obmd_node)

def test_node_show_console_reserved_chars(self):
"""test for cataching illegal argument characters"""
with pytest.raises(BadArgumentError):
C.node.show_console('node-/%]01')

def test_node_stop_console(self):
"""(successful) call to node_stop_console"""
assert C.node.stop_console('manhattan_node_1') is None

def test_node_stop_console_reserved_chars(self):
""" test for catching illegal argument characters"""
with pytest.raises(BadArgumentError):
C.node.stop_console('node-/%]01')

def test_node_connect_network(self):
"""(successful) call to node_connect_network"""
response = C.node.connect_network(
Expand Down
12 changes: 12 additions & 0 deletions tests/integration/obmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def test_power_operations(mock_node):
api.node_power_cycle(mock_node, force=True)
with pytest.raises(errors.BlockedError):
api.node_power_cycle(mock_node, force=False)
with pytest.raises(errors.BlockedError):
api.show_console(mock_node)

# Now let's enable it and try again.
api.node_enable_disable_obm(mock_node, enabled=True)
Expand All @@ -144,3 +146,13 @@ def _power_cycle(force):
)
_power_cycle(True)
_power_cycle(False)

resp = _follow_redirect('GET', api.show_console(mock_node), stream=True)
# Read the first chunk of the output from the console to make sure it
# looks right:
i = 0
for line in resp.iter_lines():
assert line == str(i)
if i >= 10:
break
i += 1

0 comments on commit 83c8f6d

Please sign in to comment.