[tcpcloud] Additional configuration parameters

* Added get_ips_cmd parameter that specifies salt command
  that returns ip address of minion.
* Added slave_direct_ssh parameter that enables/disables
  using of salt master node as ssh proxy.

Change-Id: Ida4a2e0e7e549d7d9313a1c11188d38cdc49e475
Signed-off-by: Anton Studenov <astudenov@mirantis.com>
This commit is contained in:
Anton Studenov 2017-01-25 16:09:57 +03:00
parent 3954065549
commit abf60b09ca
3 changed files with 99 additions and 17 deletions

View File

@ -24,6 +24,10 @@ class PowerManagementError(OSFError):
"""Base Error class for Power Management API"""
class CloudManagementError(OSFError):
"""Base Error class for Cloud Management API"""
class ServiceError(OSFError):
"""Base Error class for Service API"""

View File

@ -20,6 +20,7 @@ from os_faults.api import cloud_management
from os_faults.api import node_collection
from os_faults.api import node_discover
from os_faults.common import service
from os_faults import error
from os_faults import utils
LOG = logging.getLogger(__name__)
@ -246,8 +247,9 @@ class TCPCloudManagement(cloud_management.CloudManagement,
'slave_username': {'type': 'string'},
'master_sudo': {'type': 'boolean'},
'slave_sudo': {'type': 'boolean'},
'slave_iface': {'type': 'string'},
'slave_name_regexp': {'type': 'string'},
'slave_direct_ssh': {'type': 'boolean'},
'get_ips_cmd': {'type': 'string'},
},
'required': ['address', 'username'],
'additionalProperties': False,
@ -262,6 +264,11 @@ class TCPCloudManagement(cloud_management.CloudManagement,
self.slave_username = cloud_management_params.get(
'slave_username', self.username)
self.private_key_file = cloud_management_params.get('private_key_file')
self.slave_direct_ssh = cloud_management_params.get(
'slave_direct_ssh', False)
use_jump = not self.slave_direct_ssh
self.get_ips_cmd = cloud_management_params.get(
'get_ips_cmd', 'pillar.get _param:single_address')
self.master_node_executor = executor.AnsibleRunner(
remote_user=self.username,
@ -271,12 +278,10 @@ class TCPCloudManagement(cloud_management.CloudManagement,
self.cloud_executor = executor.AnsibleRunner(
remote_user=self.slave_username,
private_key_file=self.private_key_file,
jump_host=self.master_node_address,
jump_user=self.username,
jump_host=self.master_node_address if use_jump else None,
jump_user=self.username if use_jump else None,
become=cloud_management_params.get('slave_sudo'))
self.slave_iface = cloud_management_params.get('slave_iface', 'eth0')
# get all nodes except salt master (that has cfg* hostname) by default
self.slave_name_regexp = cloud_management_params.get(
'slave_name_regexp', '^(?!cfg|mon)')
@ -295,17 +300,33 @@ class TCPCloudManagement(cloud_management.CloudManagement,
LOG.info('Connected to cloud successfully!')
def _run_salt(self, command):
cmd = "salt -E '{}' {} --out=yaml".format(
self.slave_name_regexp, command)
result = self._execute_on_master_node({'command': cmd})
return yaml.load(result[0].payload['stdout'])
def discover_hosts(self):
if not self.cached_cloud_hosts:
cmd = "salt -E '{}' network.interfaces --out=yaml".format(
self.slave_name_regexp)
result = self._execute_on_master_node({'command': cmd})
stdout = result[0].payload['stdout']
for fqdn, net_data in yaml.load(stdout).items():
host = node_collection.Host(
ip=net_data[self.slave_iface]['inet'][0]['address'],
mac=net_data[self.slave_iface]['hwaddr'],
fqdn=fqdn)
interfaces = self._run_salt("network.interfaces")
ips = self._run_salt(self.get_ips_cmd)
for fqdn, ip in ips.items():
node_ifaces = interfaces[fqdn]
mac = None
for iface_name, net_data in node_ifaces.items():
iface_ips = [data['address']
for data in net_data.get('inet', [])]
if ip in iface_ips:
mac = net_data['hwaddr']
break
else:
raise error.CloudManagementError(
"Can't find ip {} on node {} with node_ifaces:\n{}"
"".format(ip, fqdn, yaml.dump(node_ifaces)))
host = node_collection.Host(ip=ip, mac=mac, fqdn=fqdn)
self.cached_cloud_hosts.append(host)
self.cached_cloud_hosts = sorted(self.cached_cloud_hosts)

View File

@ -33,19 +33,36 @@ class TCPCloudManagementTestCase(test.TestCase):
' hwaddr: 09:7b:74:90:63:c2\n'
' inet:\n'
' - address: 10.0.0.2\n'
' eth2:\n'
' hwaddr: 00:00:00:00:00:02\n'
' inet:\n'
' - address: 192.168.1.2\n'
'cmp02.mk20.local:\n'
' eth1:\n'
' hwaddr: 00:00:00:00:00:03\n'
' inet:\n'
' - address: 192.168.1.3\n'
' eth2:\n'
' hwaddr: 09:7b:74:90:63:c3\n'
' inet:\n'
' - address: 10.0.0.3\n'
})
self.fake_node_ip_result = fakes.FakeAnsibleResult(
payload={
'stdout': 'cmp01.mk20.local:\n'
' 10.0.0.2\n'
'cmp02.mk20.local:\n'
' 10.0.0.3\n'
})
self.tcp_conf = {
'address': 'tcp.local',
'username': 'root',
'slave_iface': 'eth1',
}
self.get_nodes_cmd = (
"salt -E '^(?!cfg|mon)' network.interfaces --out=yaml")
self.get_ips_cmd = ("salt -E '^(?!cfg|mon)' "
"pillar.get _param:single_address --out=yaml")
@mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True)
@ddt.data((
@ -69,6 +86,15 @@ class TCPCloudManagementTestCase(test.TestCase):
remote_user='ubuntu'),
mock.call(become=True, jump_host='tcp.local', jump_user='ubuntu',
private_key_file='/path/id_rsa', remote_user='root'))
), (
dict(address='tcp.local', username='ubuntu',
slave_username='root', slave_sudo=True,
private_key_file='/path/id_rsa',
slave_direct_ssh=True),
(mock.call(become=None, private_key_file='/path/id_rsa',
remote_user='ubuntu'),
mock.call(become=True, jump_host=None, jump_user=None,
private_key_file='/path/id_rsa', remote_user='root'))
))
@ddt.unpack
def test_init(self, config, expected_runner_calls, mock_ansible_runner):
@ -85,6 +111,7 @@ class TCPCloudManagementTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''}),
fakes.FakeAnsibleResult(payload={'stdout': ''})],
]
@ -93,20 +120,27 @@ class TCPCloudManagementTestCase(test.TestCase):
tcp_managment.verify()
get_nodes_cmd = "salt -E '(ctl*|cmp*)' network.interfaces --out=yaml"
get_ips_cmd = ("salt -E '(ctl*|cmp*)' "
"pillar.get _param:single_address --out=yaml")
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': get_nodes_cmd}),
mock.call(['tcp.local'], {'command': get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'], {'command': 'hostname'}),
])
@mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True)
def test_get_nodes(self, mock_ansible_runner):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [[self.fake_ansible_result]]
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
]
tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf)
nodes = tcp_managment.get_nodes()
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
])
hosts = [
@ -140,6 +174,7 @@ class TCPCloudManagementTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''}),
fakes.FakeAnsibleResult(payload={'stdout': ''})]
]
@ -150,6 +185,7 @@ class TCPCloudManagementTestCase(test.TestCase):
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'], {'command': 'mycmd'}, []),
])
@ -160,7 +196,10 @@ class TCPCloudManagementTestCase(test.TestCase):
@mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True)
def test_get_nodes_fqdns(self, mock_ansible_runner):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [[self.fake_ansible_result]]
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
]
tcp_managment = tcpcloud.TCPCloudManagement(self.tcp_conf)
nodes = tcp_managment.get_nodes(fqdns=['cmp02.mk20.local'])
@ -178,6 +217,7 @@ class TCPCloudManagementTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''},
status=executor.STATUS_FAILED,
host='10.0.0.2'),
@ -194,6 +234,7 @@ class TCPCloudManagementTestCase(test.TestCase):
cmd = 'bash -c "ps ax | grep \'{}\'"'.format(service_cls.GREP)
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'],
{'command': cmd}, []),
])
@ -223,12 +264,22 @@ class TcpServiceTestCase(test.TestCase):
' inet:\n'
' - address: 10.0.0.3\n'
})
self.fake_node_ip_result = fakes.FakeAnsibleResult(
payload={
'stdout': 'cmp01.mk20.local:\n'
' 10.0.0.2\n'
'cmp02.mk20.local:\n'
' 10.0.0.3\n'
})
self.tcp_conf = {
'address': 'tcp.local',
'username': 'root',
}
self.get_nodes_cmd = (
"salt -E '^(?!cfg|mon)' network.interfaces --out=yaml")
self.get_ips_cmd = ("salt -E '^(?!cfg|mon)' "
"pillar.get _param:single_address --out=yaml")
@mock.patch('os_faults.ansible.executor.AnsibleRunner', autospec=True)
@ddt.data(*tcpcloud.TCPCloudManagement.SERVICE_NAME_TO_CLASS.items())
@ -237,6 +288,7 @@ class TcpServiceTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''},
status=executor.STATUS_FAILED,
host='10.0.0.2'),
@ -258,6 +310,7 @@ class TcpServiceTestCase(test.TestCase):
cmd = 'bash -c "ps ax | grep \'{}\'"'.format(service_cls.GREP)
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'],
{'command': cmd}, []),
mock.call(['10.0.0.3'], {'shell': service.RESTART_CMD}),
@ -270,6 +323,7 @@ class TcpServiceTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''},
status=executor.STATUS_FAILED,
host='10.0.0.2'),
@ -291,6 +345,7 @@ class TcpServiceTestCase(test.TestCase):
cmd = 'bash -c "ps ax | grep \'{}\'"'.format(service_cls.GREP)
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'],
{'command': cmd}, []),
mock.call(['10.0.0.3'], {'shell': service.TERMINATE_CMD}),
@ -303,6 +358,7 @@ class TcpServiceTestCase(test.TestCase):
ansible_runner_inst = mock_ansible_runner.return_value
ansible_runner_inst.execute.side_effect = [
[self.fake_ansible_result],
[self.fake_node_ip_result],
[fakes.FakeAnsibleResult(payload={'stdout': ''},
status=executor.STATUS_FAILED,
host='10.0.0.2'),
@ -324,6 +380,7 @@ class TcpServiceTestCase(test.TestCase):
cmd = 'bash -c "ps ax | grep \'{}\'"'.format(service_cls.GREP)
ansible_runner_inst.execute.assert_has_calls([
mock.call(['tcp.local'], {'command': self.get_nodes_cmd}),
mock.call(['tcp.local'], {'command': self.get_ips_cmd}),
mock.call(['10.0.0.2', '10.0.0.3'],
{'command': cmd}, []),
mock.call(['10.0.0.3'], {'shell': service.START_CMD}),