Skip to content

Commit c03b9a8

Browse files
committed
Add server event list and show commands
OSC server event is similar to nova's instance action commands. Server event is the event record that had been done on a server, include: event type(create, delete, reboot and so on), event result(success, error), start time, finish time and so on. These are important information for server maintains. Change-Id: I8111091f46a0d2755728d8f9d43cc0dfe8842d13 Closes-Bug: #1642030
1 parent ead615f commit c03b9a8

8 files changed

Lines changed: 485 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
============
2+
server event
3+
============
4+
5+
Server event is the event record that had been done on a server, include: event
6+
type(create, delete, reboot and so on), event result(success, error), start
7+
time, finish time and so on. These are important information for server
8+
maintains.
9+
10+
Compute v2
11+
12+
server event list
13+
-----------------
14+
15+
List recent events of a server
16+
17+
.. program:: server event list
18+
.. code:: bash
19+
20+
openstack server event list
21+
<server>
22+
23+
.. describe:: <server>
24+
25+
Server to list events (name or ID)
26+
27+
server event show
28+
-----------------
29+
30+
Show server event details
31+
32+
.. program:: server event show
33+
.. code:: bash
34+
35+
openstack server event show
36+
<server>
37+
<request-id>
38+
39+
.. describe:: <server>
40+
41+
Server to show event details (name or ID)
42+
43+
.. describe:: <request-id>
44+
45+
Request ID of the event to show (ID only)

doc/source/commands.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ referring to both Compute and Volume quotas.
137137
* ``server``: (**Compute**) virtual machine instance
138138
* ``server backup``: (**Compute**) backup server disk image by using snapshot method
139139
* ``server dump``: (**Compute**) a dump file of a server created by features like kdump
140+
* ``server event``: (**Compute**) events of a server
140141
* ``server group``: (**Compute**) a grouping of servers
141142
* ``server image``: (**Compute**) saved server disk image
142143
* ``service``: (**Identity**) a cloud service
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright 2017 Huawei, Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
16+
"""Compute v2 Server operation event implementations"""
17+
18+
import logging
19+
import six
20+
21+
from osc_lib.command import command
22+
from osc_lib import utils
23+
24+
from openstackclient.i18n import _
25+
26+
27+
LOG = logging.getLogger(__name__)
28+
29+
30+
class ListServerEvent(command.Lister):
31+
_description = _("List recent events of a server")
32+
33+
def get_parser(self, prog_name):
34+
parser = super(ListServerEvent, self).get_parser(prog_name)
35+
parser.add_argument(
36+
'server',
37+
metavar='<server>',
38+
help=_('Server to list events (name or ID)'),
39+
)
40+
parser.add_argument(
41+
'--long',
42+
action='store_true',
43+
default=False,
44+
help=_("List additional fields in output")
45+
)
46+
return parser
47+
48+
def take_action(self, parsed_args):
49+
compute_client = self.app.client_manager.compute
50+
server_id = utils.find_resource(compute_client.servers,
51+
parsed_args.server).id
52+
data = compute_client.instance_action.list(server_id)
53+
54+
if parsed_args.long:
55+
columns = (
56+
'request_id',
57+
'instance_uuid',
58+
'action',
59+
'start_time',
60+
'message',
61+
'project_id',
62+
'user_id',
63+
)
64+
column_headers = (
65+
'Request ID',
66+
'Server ID',
67+
'Action',
68+
'Start Time',
69+
'Message',
70+
'Project ID',
71+
'User ID',
72+
)
73+
else:
74+
columns = (
75+
'request_id',
76+
'instance_uuid',
77+
'action',
78+
'start_time',
79+
)
80+
column_headers = (
81+
'Request ID',
82+
'Server ID',
83+
'Action',
84+
'Start Time',
85+
)
86+
87+
return (column_headers,
88+
(utils.get_item_properties(
89+
s, columns,
90+
) for s in data))
91+
92+
93+
class ShowServerEvent(command.ShowOne):
94+
_description = _("Show server event details")
95+
96+
def get_parser(self, prog_name):
97+
parser = super(ShowServerEvent, self).get_parser(prog_name)
98+
parser.add_argument(
99+
'server',
100+
metavar='<server>',
101+
help=_('Server to show event details (name or ID)'),
102+
)
103+
parser.add_argument(
104+
'request_id',
105+
metavar='<request-id>',
106+
help=_('Request ID of the event to show (ID only)'),
107+
)
108+
return parser
109+
110+
def take_action(self, parsed_args):
111+
compute_client = self.app.client_manager.compute
112+
server_id = utils.find_resource(compute_client.servers,
113+
parsed_args.server).id
114+
action_detail = compute_client.instance_action.get(
115+
server_id, parsed_args.request_id)
116+
117+
return zip(*sorted(six.iteritems(action_detail._info)))
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright 2017 Huawei, Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
16+
import json
17+
import uuid
18+
19+
from openstackclient.tests.functional import base
20+
from openstackclient.tests.functional.compute.v2 import test_server
21+
22+
23+
class ServerEventTests(base.TestCase):
24+
"""Functional tests for server event."""
25+
26+
def setUp(self):
27+
super(ServerEventTests, self).setUp()
28+
_flavor = test_server.ServerTests.get_flavor()
29+
_image = test_server.ServerTests.get_image()
30+
_network = test_server.ServerTests.get_network()
31+
self.server_name = uuid.uuid4().hex
32+
cmd_output = json.loads(self.openstack(
33+
'server create -f json ' +
34+
'--flavor ' + _flavor + ' ' +
35+
'--image ' + _image + ' ' +
36+
_network + ' ' +
37+
'--wait ' +
38+
self.server_name
39+
))
40+
if not cmd_output:
41+
self.fail('Server has not been created!')
42+
self.addCleanup(self.openstack, 'server delete ' + self.server_name)
43+
self.assertEqual(self.server_name, cmd_output['name'])
44+
self.server_id = cmd_output.get('id')
45+
46+
def test_server_event_list_and_show(self):
47+
"""Test list, show server event"""
48+
# Test 'server event list' for creating
49+
cmd_output = json.loads(self.openstack(
50+
'server event list -f json ' + self.server_name
51+
))
52+
request_id = None
53+
for each_event in cmd_output:
54+
self.assertNotIn('Message', each_event)
55+
self.assertNotIn('Project ID', each_event)
56+
self.assertNotIn('User ID', each_event)
57+
if each_event.get('Action') == 'create':
58+
self.assertEqual(self.server_id, each_event.get('Server ID'))
59+
request_id = each_event.get('Request ID')
60+
break
61+
self.assertIsNotNone(request_id)
62+
# Test 'server event show' for creating
63+
cmd_output = json.loads(self.openstack(
64+
'server event show -f json ' + self.server_name + ' ' + request_id
65+
))
66+
self.assertEqual(self.server_id, cmd_output.get('instance_uuid'))
67+
self.assertEqual(request_id, cmd_output.get('request_id'))
68+
self.assertEqual('create', cmd_output.get('action'))
69+
self.assertIsNotNone(cmd_output.get('events'))
70+
self.assertIsInstance(cmd_output.get('events'), list)
71+
72+
# Reboot server, trigger reboot event
73+
self.openstack('server reboot --wait ' + self.server_name)
74+
# Test 'server event list --long' for rebooting
75+
cmd_output = json.loads(self.openstack(
76+
'server event list --long -f json ' + self.server_name
77+
))
78+
request_id = None
79+
for each_event in cmd_output:
80+
self.assertIn('Message', each_event)
81+
self.assertIn('Project ID', each_event)
82+
self.assertIn('User ID', each_event)
83+
if each_event.get('Action') == 'reboot':
84+
request_id = each_event.get('Request ID')
85+
self.assertEqual(self.server_id, each_event.get('Server ID'))
86+
break
87+
self.assertIsNotNone(request_id)
88+
# Test 'server event show' for rebooting
89+
cmd_output = json.loads(self.openstack(
90+
'server event show -f json ' + self.server_name + ' ' + request_id
91+
))
92+
93+
self.assertEqual(self.server_id, cmd_output.get('instance_uuid'))
94+
self.assertEqual(request_id, cmd_output.get('request_id'))
95+
self.assertEqual('reboot', cmd_output.get('action'))
96+
self.assertIsNotNone(cmd_output.get('events'))
97+
self.assertIsInstance(cmd_output.get('events'), list)

openstackclient/tests/unit/compute/v2/fakes.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ def __init__(self, **kwargs):
204204
self.server_groups = mock.Mock()
205205
self.server_groups.resource_class = fakes.FakeResource(None, {})
206206

207+
self.instance_action = mock.Mock()
208+
self.instance_action.resource_class = fakes.FakeResource(None, {})
209+
207210
self.auth_token = kwargs['token']
208211

209212
self.management_url = kwargs['endpoint']
@@ -656,6 +659,47 @@ def get_servers(servers=None, count=2):
656659
return mock.Mock(side_effect=servers)
657660

658661

662+
class FakeServerEvent(object):
663+
"""Fake one or more server event."""
664+
665+
@staticmethod
666+
def create_one_server_event(attrs=None):
667+
"""Create a fake server event.
668+
669+
:param attrs:
670+
A dictionary with all attributes
671+
:return:
672+
A FakeResource object, with id and other attributes
673+
"""
674+
attrs = attrs or {}
675+
676+
# Set default attributes
677+
server_event_info = {
678+
"instance_uuid": "server-event-" + uuid.uuid4().hex,
679+
"user_id": "user-id-" + uuid.uuid4().hex,
680+
"start_time": "2017-02-27T07:47:13.000000",
681+
"request_id": "req-" + uuid.uuid4().hex,
682+
"action": "create",
683+
"message": None,
684+
"project_id": "project-id-" + uuid.uuid4().hex,
685+
"events": [{
686+
"finish_time": "2017-02-27T07:47:25.000000",
687+
"start_time": "2017-02-27T07:47:15.000000",
688+
"traceback": None,
689+
"event": "compute__do_build_and_run_instance",
690+
"result": "Success"
691+
}]
692+
}
693+
# Overwrite default attributes
694+
server_event_info.update(attrs)
695+
696+
server_event = fakes.FakeResource(
697+
info=copy.deepcopy(server_event_info),
698+
loaded=True,
699+
)
700+
return server_event
701+
702+
659703
class FakeService(object):
660704
"""Fake one or more services."""
661705

0 commit comments

Comments
 (0)