Rename and fix network-management commands in NodesCollection
Both network-management methods are renamed: disable_network -> disconnect enable_network -> connect In Human API following commands are now available: * disconnect <name> network on [random|one|single|<fqdn>] node[s] [with <service> service] * connect <name> network on [random|one|single|<fqdn>] node[s] [with <service> service] Also keyword 'service' is made mandatory while referencing to service. Change-Id: I98eb842c39f7a1d08bcab185cd22e24523a1d7c6
This commit is contained in:
parent
8ffe4a9d6c
commit
1012048515
48
README.rst
48
README.rst
@ -12,7 +12,7 @@ IPMI driver).
|
||||
* Free software: Apache license
|
||||
* Documentation: http://os-faults.readthedocs.io
|
||||
* Source: https://github.com/openstack/os-faults
|
||||
* Bugs: http://bugs.launchpad.net/os_faults
|
||||
* Bugs: http://bugs.launchpad.net/os-faults
|
||||
|
||||
Usage
|
||||
-----
|
||||
@ -68,26 +68,36 @@ Simplified API
|
||||
|
||||
Simplified API is used to inject faults in a human-friendly form.
|
||||
|
||||
Service-based command performs specified `action` against `service` on
|
||||
**Service-oriented** command performs specified `action` against `service` on
|
||||
all, on one random node or on the node specified by FQDN::
|
||||
|
||||
<action> <service> service [on (random|one|single|<fqdn> node[s])]
|
||||
|
||||
Node-based command performs specified `action` on all or selected service's
|
||||
node::
|
||||
|
||||
<action> [random|one|single] <service> node[s]
|
||||
|
||||
Network-management command is a subset of node-based query::
|
||||
|
||||
disable|enable network <network name> on <service> node[s]
|
||||
<action> <service> service [on (random|one|single|<fqdn> node[s])]
|
||||
|
||||
Examples:
|
||||
* `Restart Keystone service` - restarts Keystone service on all nodes.
|
||||
* `kill nova-api service on one node` - restarts Nova API on one
|
||||
randomly-picked node.
|
||||
|
||||
* `Restart Keystone service` - restarts Keystone service on all nodes
|
||||
* `kill nova-api service on one node` - restarts Nova API on one of nodes
|
||||
* `Reboot one node with mysql` - reboots one random node with MySQL
|
||||
* `Reboot node-2.domain.tld node` - reboot node with specified name
|
||||
**Node-oriented** command performs specified `action` on node specified by FQDN
|
||||
or set of service's nodes::
|
||||
|
||||
<action> [random|one|single|<fqdn>] node[s] [with <service> service]
|
||||
|
||||
Examples:
|
||||
* `Reboot one node with mysql` - reboots one random node with MySQL.
|
||||
* `Reset node-2.domain.tld node` - reset node `node-2.domain.tld`.
|
||||
|
||||
**Network-oriented** command is a subset of node-oriented and performs network
|
||||
management operation on selected nodes::
|
||||
|
||||
<action> <network> network on [random|one|single|<fqdn>] node[s]
|
||||
[with <service> service]
|
||||
|
||||
Examples:
|
||||
* `Disconnect management network on nodes with rabbitmq service` - shuts
|
||||
down management network interface on all nodes where rabbitmq runs.
|
||||
* `Connect storage network on node-1.domain.tld node` - enables storage
|
||||
network interface on node-1.domain.tld.
|
||||
|
||||
|
||||
Extended API
|
||||
@ -127,8 +137,8 @@ Available actions:
|
||||
* `poweroff` - power off all nodes abruptly
|
||||
* `reset` - reset (cold restart) all nodes
|
||||
* `oom` - fill all node's RAM
|
||||
* `disable_network` - disable network with the specified name on all nodes
|
||||
* `enable_network` - enable network with the specified name on all nodes
|
||||
* `disconnect` - disable network with the specified name on all nodes
|
||||
* `connect` - enable network with the specified name on all nodes
|
||||
|
||||
3. Operate with nodes
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -147,7 +157,7 @@ Get nodes where l3-agent runs and disable the management network on them:
|
||||
|
||||
fqdns = neutron.l3_agent_list_hosting_router(router_id)
|
||||
nodes = destructor.get_nodes(fqdns=fqdns)
|
||||
nodes.disable_network(network_name='management')
|
||||
nodes.disconnect(network_name='management')
|
||||
|
||||
4. Operate with services
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -65,10 +65,10 @@ def main():
|
||||
logging.info('Node node-2.domain.tld: %s', nodes)
|
||||
|
||||
logging.info('# Disable public network on node-2.domain.tld')
|
||||
nodes.disable_network(network_name='public')
|
||||
nodes.disconnect(network_name='public')
|
||||
|
||||
logging.info('# Enable public network on node-2.domain.tld')
|
||||
nodes.enable_network(network_name='public')
|
||||
nodes.connect(network_name='public')
|
||||
|
||||
logging.info('# Kill Glance API service on a single node')
|
||||
service = destructor.get_service(name='glance-api')
|
||||
|
@ -28,9 +28,12 @@ Human API understands commands like these (examples):
|
||||
* freeze <service> service [on (random|one|single|<fqdn> node[s])]
|
||||
[for <T> seconds]
|
||||
* unfreeze <service> service [on (random|one|single|<fqdn> node[s])]
|
||||
* reboot [random|one|single|<fqdn>] node[s] [with <service>]
|
||||
* disable network <name> on <service> node[s]
|
||||
* enable network <name> on <service> node[s]
|
||||
* reboot [random|one|single|<fqdn>] node[s] [with <service> service]
|
||||
* reset [random|one|single|<fqdn>] node[s] [with <service> service]
|
||||
* disconnect <name> network on [random|one|single|<fqdn>] node[s]
|
||||
[with <service> service]
|
||||
* connect <name> network on [random|one|single|<fqdn>] node[s]
|
||||
[with <service> service]
|
||||
"""
|
||||
|
||||
|
||||
@ -54,10 +57,10 @@ PATTERNS = [
|
||||
'(\s+for\s+(?P<duration>\d+)\s+seconds)?' %
|
||||
SERVICE_ACTIONS_PATTERN),
|
||||
re.compile('(?P<action>%s)'
|
||||
'(\s+(?P<network>\w+)\s+on)?'
|
||||
'(\s+(?P<network>\w+)\s+network\s+on)?'
|
||||
'(\s+(?P<node>%s|\S+))?'
|
||||
'\s+node'
|
||||
'(\s+with\s+(?P<service>\S+))?' %
|
||||
'\s+nodes?'
|
||||
'(\s+with\s+(?P<service>\S+)\s+service)?' %
|
||||
(NODE_ACTIONS_PATTERN, RANDOMNESS_PATTERN)),
|
||||
]
|
||||
|
||||
@ -104,8 +107,12 @@ def execute(destructor, command):
|
||||
if node_name in RANDOMNESS:
|
||||
nodes = nodes.pick()
|
||||
|
||||
kwargs = {}
|
||||
if network_name:
|
||||
kwargs['network_name'] = network_name
|
||||
|
||||
fn = getattr(nodes, action)
|
||||
fn()
|
||||
fn(**kwargs)
|
||||
else: # nodes operation
|
||||
nodes = destructor.get_nodes(fqdns=[node_name])
|
||||
|
||||
|
@ -64,16 +64,16 @@ class NodeCollection(object):
|
||||
raise NotImplementedError
|
||||
|
||||
@public
|
||||
def disable_network(self, network_name):
|
||||
"""Disable network with name network_name on each of the nodes
|
||||
def disconnect(self, network_name):
|
||||
"""Disconnect nodes from <network_name> network
|
||||
|
||||
:param network_name: name of network
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@public
|
||||
def enable_network(self, network_name):
|
||||
"""Enable network with name network_name on each of the nodes
|
||||
def connect(self, network_name):
|
||||
"""Connect nodes to <network_name> network
|
||||
|
||||
:param network_name: name of network
|
||||
"""
|
||||
|
@ -60,10 +60,10 @@ class DevStackNode(node_collection.NodeCollection):
|
||||
logging.info('Reset nodes: %s', self)
|
||||
self.power_management.reset([self.host.mac])
|
||||
|
||||
def enable_network(self, network_name):
|
||||
def connect(self, network_name):
|
||||
raise NotImplementedError
|
||||
|
||||
def disable_network(self, network_name):
|
||||
def disconnect(self, network_name):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
@ -77,16 +77,17 @@ class FuelNodeCollection(node_collection.NodeCollection):
|
||||
logging.info('Reset nodes: %s', self)
|
||||
self.power_management.reset(self.get_macs())
|
||||
|
||||
def enable_network(self, network_name):
|
||||
logging.info("Enable '%s' network on nodes: %s", network_name, self)
|
||||
def connect(self, network_name):
|
||||
logging.info("Connect network '%s' on nodes: %s", network_name, self)
|
||||
task = {'fuel_network_mgmt': {
|
||||
'network_name': network_name,
|
||||
'operation': 'up',
|
||||
}}
|
||||
self.cloud_management.execute_on_cloud(self.get_ips(), task)
|
||||
|
||||
def disable_network(self, network_name):
|
||||
logging.info("Disable '%s' network on nodes: %s", network_name, self)
|
||||
def disconnect(self, network_name):
|
||||
logging.info("Disconnect network '%s' on nodes: %s",
|
||||
network_name, self)
|
||||
task = {'fuel_network_mgmt': {
|
||||
'network_name': network_name,
|
||||
'operation': 'down',
|
||||
|
@ -89,7 +89,7 @@ class TestHumanAPI(test.TestCase):
|
||||
|
||||
self.service.get_nodes = mock.MagicMock(return_value=nodes)
|
||||
|
||||
command = '%s node with %s' % (action, service_name)
|
||||
command = '%s node with %s service' % (action, service_name)
|
||||
human.execute(self.destructor, command)
|
||||
|
||||
self.destructor.get_service.assert_called_once_with(name=service_name)
|
||||
@ -105,7 +105,7 @@ class TestHumanAPI(test.TestCase):
|
||||
self.service.get_nodes = mock.MagicMock(return_value=nodes)
|
||||
nodes.pick = mock.MagicMock(return_value=nodes2)
|
||||
|
||||
command = '%s one node with %s' % (action, service_name)
|
||||
command = '%s one node with %s service' % (action, service_name)
|
||||
human.execute(self.destructor, command)
|
||||
|
||||
self.destructor.get_service.assert_called_once_with(name=service_name)
|
||||
@ -124,20 +124,59 @@ class TestHumanAPI(test.TestCase):
|
||||
destructor.get_nodes.assert_called_once_with(fqdns=['node-2.local'])
|
||||
getattr(nodes, action).assert_called_once()
|
||||
|
||||
@ddt.data(('Disable network', 'disable_network'),
|
||||
('Enable network', 'enable_network'))
|
||||
@ddt.data(('Disconnect', 'disconnect'),
|
||||
('Connect', 'connect'))
|
||||
@ddt.unpack
|
||||
def test_disable_network_node_by_fqdn(self, user_action, action):
|
||||
def test_network_on_nodes_by_fqdn(self, user_action, action):
|
||||
destructor = mock.MagicMock()
|
||||
nodes = mock.MagicMock(node_collection.NodeCollection)
|
||||
destructor.get_nodes = mock.MagicMock(return_value=nodes)
|
||||
|
||||
command = '%s storage on node-2.local node' % user_action
|
||||
command = '%s storage network on node-2.local node' % user_action
|
||||
human.execute(destructor, command)
|
||||
|
||||
destructor.get_nodes.assert_called_once_with(fqdns=['node-2.local'])
|
||||
getattr(nodes, action).assert_called_once_with(network_name='storage')
|
||||
|
||||
@ddt.data(('disconnect', 'storage', 'mysql'),
|
||||
('connect', 'management', 'rabbitmq'))
|
||||
@ddt.unpack
|
||||
def test_network_on_nodes_by_service(
|
||||
self, action, network_name, service_name):
|
||||
nodes = mock.MagicMock(node_collection.NodeCollection)
|
||||
|
||||
self.service.get_nodes = mock.MagicMock(return_value=nodes)
|
||||
|
||||
command = '%s %s network on node with %s service' % (
|
||||
action, network_name, service_name)
|
||||
human.execute(self.destructor, command)
|
||||
|
||||
self.destructor.get_service.assert_called_once_with(name=service_name)
|
||||
self.service.get_nodes.assert_called_once()
|
||||
getattr(nodes, action).assert_called_once_with(
|
||||
network_name=network_name)
|
||||
|
||||
@ddt.data(('disconnect', 'storage', 'one', 'mysql'),
|
||||
('connect', 'management', 'random', 'rabbitmq'))
|
||||
@ddt.unpack
|
||||
def test_network_on_nodes_by_service_picked_node(
|
||||
self, action, network_name, node, service_name):
|
||||
nodes = mock.MagicMock(node_collection.NodeCollection)
|
||||
nodes2 = mock.MagicMock(node_collection.NodeCollection)
|
||||
|
||||
self.service.get_nodes = mock.MagicMock(return_value=nodes)
|
||||
nodes.pick = mock.MagicMock(return_value=nodes2)
|
||||
|
||||
command = '%s %s network on %s node with %s service' % (
|
||||
action, network_name, node, service_name)
|
||||
human.execute(self.destructor, command)
|
||||
|
||||
self.destructor.get_service.assert_called_once_with(name=service_name)
|
||||
self.service.get_nodes.assert_called_once()
|
||||
nodes.pick.assert_called_once()
|
||||
getattr(nodes2, action).assert_called_once_with(
|
||||
network_name=network_name)
|
||||
|
||||
def test_malformed_query(self):
|
||||
destructor = mock.MagicMock()
|
||||
|
||||
|
@ -91,15 +91,15 @@ class FuelNodeCollectionTestCase(test.TestCase):
|
||||
['09:7b:74:90:63:c1', '09:7b:74:90:63:c2',
|
||||
'09:7b:74:90:63:c3', '09:7b:74:90:63:c4'])
|
||||
|
||||
def test_enable_network(self):
|
||||
self.node_collection.enable_network(network_name='storage')
|
||||
def test_connect(self):
|
||||
self.node_collection.connect(network_name='storage')
|
||||
self.mock_cloud_management.execute_on_cloud.assert_called_once_with(
|
||||
['10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5'],
|
||||
{'fuel_network_mgmt': {'operation': 'up',
|
||||
'network_name': 'storage'}})
|
||||
|
||||
def test_disable_network(self):
|
||||
self.node_collection.disable_network(network_name='storage')
|
||||
def test_disconnect(self):
|
||||
self.node_collection.disconnect(network_name='storage')
|
||||
self.mock_cloud_management.execute_on_cloud.assert_called_once_with(
|
||||
['10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5'],
|
||||
{'fuel_network_mgmt': {'operation': 'down',
|
||||
|
Loading…
x
Reference in New Issue
Block a user