111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
import os.path
|
|
import re
|
|
import sys
|
|
import tempfile
|
|
import logging
|
|
|
|
import spur
|
|
|
|
from ostack_validator.common import Issue, Mark, MarkedIssue, index
|
|
from ostack_validator.model import Openstack, Host, OpenstackComponent, KeystoneComponent, NovaComputeComponent, GlanceApiComponent
|
|
|
|
|
|
|
|
class NodeClient(object):
|
|
def __init__(self, node_address, username, private_key_file):
|
|
super(NodeClient, self).__init__()
|
|
self.shell = spur.SshShell(hostname=node_address, username=username, private_key_file=private_key_file, missing_host_key=spur.ssh.MissingHostKey.accept)
|
|
|
|
def run(self, command):
|
|
return self.shell.run(command)
|
|
|
|
def open(self, path, mode='r'):
|
|
return self.shell.open(path, mode)
|
|
|
|
python_re = re.compile('(/?([^/]*/)*)python[0-9.]*')
|
|
|
|
class OpenstackDiscovery(object):
|
|
def discover(self, initial_nodes, username, private_key):
|
|
"Takes a list of node addresses and returns discovered openstack installation info"
|
|
openstack = Openstack()
|
|
|
|
private_key_file = None
|
|
if private_key:
|
|
private_key_file = tempfile.NamedTemporaryFile(suffix='.key')
|
|
private_key_file.write(private_key)
|
|
private_key_file.flush()
|
|
|
|
for address in initial_nodes:
|
|
try:
|
|
client = NodeClient(address, username=username, private_key_file=private_key_file.name)
|
|
client.run(['echo', 'test'])
|
|
except:
|
|
openstack.report_issue(Issue(Issue.WARNING, "Can't connect to node %s" % address))
|
|
continue
|
|
|
|
host = self._discover_node(client)
|
|
|
|
if len(host.components) == 0:
|
|
continue
|
|
|
|
openstack.add_host(host)
|
|
|
|
if len(openstack.hosts) == 0:
|
|
openstack.report_issue(Issue(Issue.FATAL, "No OpenStack nodes were discovered"))
|
|
|
|
if private_key_file:
|
|
private_key_file.close()
|
|
|
|
return openstack
|
|
|
|
def _discover_node(self, client):
|
|
hostname = client.run(['hostname']).output.strip()
|
|
metadata = {}
|
|
|
|
host = Host(name=hostname, metadata=metadata, client=client)
|
|
|
|
processes = [line.split() for line in client.run(['ps', '-Ao', 'cmd', '--no-headers']).output.split("\n")]
|
|
|
|
keystone_process = self._find_python_process(processes, 'keystone-all')
|
|
if keystone_process:
|
|
p = index(keystone_process, lambda s: s == '--config-file')
|
|
if p != -1 and p+1 < len(keystone_process):
|
|
config_file = keystone_process[p+1]
|
|
else:
|
|
config_file = '/etc/keystone/keystone.conf'
|
|
|
|
host.add_component(KeystoneComponent(config_file))
|
|
|
|
glance_api_process = self._find_python_process(processes, 'glance-api')
|
|
if glance_api_process:
|
|
p = index(glance_api_process, lambda s: s == '--config-file')
|
|
if p != -1 and p+1 < len(glance_api_process):
|
|
config_file = glance_api_process[p+1]
|
|
else:
|
|
config_file = '/etc/glance/glance-api.conf'
|
|
|
|
host.add_component(GlanceApiComponent(config_file))
|
|
|
|
nova_compute_process = self._find_python_process(processes, 'nova-compute')
|
|
if nova_compute_process:
|
|
p = index(nova_compute_process, lambda s: s == '--config-file')
|
|
if p != -1 and p+1 < len(nova_compute_process):
|
|
config_file = nova_compute_process[p+1]
|
|
else:
|
|
config_file = '/etc/nova/nova.conf'
|
|
|
|
host.add_component(NovaComputeComponent(config_file))
|
|
|
|
return host
|
|
|
|
|
|
def _find_python_process(self, processes, name):
|
|
for line in processes:
|
|
if len(line) > 0 and (line[0] == name or line[0].endswith('/'+name)):
|
|
return line
|
|
if len(line) > 1 and python_re.match(line[0]) and (line[1] == name or line[1].endswith('/'+name)):
|
|
return line
|
|
|
|
return None
|
|
|