Implement New Discovery Schema
Change-Id: Ibd34c44211920197cf19df535ef038279fff2714
This commit is contained in:
parent
ae4bc4ebb7
commit
c382a3e860
|
@ -28,6 +28,7 @@ sys.path.insert(0, ROOT)
|
||||||
sys.path.insert(0, BASE_DIR)
|
sys.path.insert(0, BASE_DIR)
|
||||||
sys.path = PLUGIN_DIRS + sys.path
|
sys.path = PLUGIN_DIRS + sys.path
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Automatically write module docs
|
# Automatically write module docs
|
||||||
#
|
#
|
||||||
|
|
|
@ -10,6 +10,7 @@ Satori is a configuration discovery tool for OpenStack and OpenStack tenant host
|
||||||
contributing
|
contributing
|
||||||
releases
|
releases
|
||||||
terminology
|
terminology
|
||||||
|
schema
|
||||||
satori
|
satori
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,8 +52,7 @@ Use Satori
|
||||||
Host:
|
Host:
|
||||||
4.4.4.4 (www.foo.com) is hosted on a Nova Instance
|
4.4.4.4 (www.foo.com) is hosted on a Nova Instance
|
||||||
Instance Information:
|
Instance Information:
|
||||||
URI: https://nova.api.somecloud.com/v2/111222/servers/d9119040-f767-414
|
URI: https://nova.api.somecloud.com/v2/111222/servers/d9119040
|
||||||
1-95a4-d4dbf452363a
|
|
||||||
Name: sampleserver01.foo.com
|
Name: sampleserver01.foo.com
|
||||||
ID: d9119040-f767-4141-95a4-d4dbf452363a
|
ID: d9119040-f767-4141-95a4-d4dbf452363a
|
||||||
ip-addresses:
|
ip-addresses:
|
||||||
|
@ -61,6 +61,12 @@ Use Satori
|
||||||
4.4.4.4
|
4.4.4.4
|
||||||
private:
|
private:
|
||||||
10.1.1.156
|
10.1.1.156
|
||||||
|
Listening Services:
|
||||||
|
0.0.0.0:6082 varnishd
|
||||||
|
127.0.0.1:8080 apache2
|
||||||
|
127.0.0.1:3306 mysqld
|
||||||
|
Talking to:
|
||||||
|
10.1.1.71 on 27017
|
||||||
|
|
||||||
|
|
||||||
Links
|
Links
|
||||||
|
|
|
@ -67,6 +67,12 @@ control plane discovery was possible using the OpenStack credentials.
|
||||||
192.0.2.10
|
192.0.2.10
|
||||||
private:
|
private:
|
||||||
10.1.1.156
|
10.1.1.156
|
||||||
|
Listening Services:
|
||||||
|
0.0.0.0:6082 varnishd
|
||||||
|
127.0.0.1:8080 apache2
|
||||||
|
127.0.0.1:3306 mysqld
|
||||||
|
Talking to:
|
||||||
|
10.1.1.71 on 27017
|
||||||
|
|
||||||
.. _nova: https://github.com/openstack/python-novaclient
|
.. _nova: https://github.com/openstack/python-novaclient
|
||||||
.. _OpenStack Nova conventions: https://github.com/openstack/python-novaclient/blob/master/README.rst#id1
|
.. _OpenStack Nova conventions: https://github.com/openstack/python-novaclient/blob/master/README.rst#id1
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
======
|
||||||
|
Schema
|
||||||
|
======
|
||||||
|
|
||||||
|
The following list of fields describes the data returned from Satori.
|
||||||
|
|
||||||
|
|
||||||
|
Target
|
||||||
|
======
|
||||||
|
|
||||||
|
Target contains the address suplplied to run the discovery.
|
||||||
|
|
||||||
|
|
||||||
|
Found
|
||||||
|
=====
|
||||||
|
|
||||||
|
All data items discovered are returned under the found key. Keys to resources
|
||||||
|
discovered are also added under found, but the actual resources are stored
|
||||||
|
under the resources key.
|
||||||
|
|
||||||
|
|
||||||
|
Resources
|
||||||
|
=========
|
||||||
|
|
||||||
|
All resources (servers, load balancers, DNS domains, etc...) are stored under
|
||||||
|
the resources key.
|
||||||
|
|
||||||
|
Each resource contains the following keys:
|
||||||
|
|
||||||
|
* **key**: a globally unique identifier for the resource (could be a URI)
|
||||||
|
* **id**: the id in the system that hosts the resource
|
||||||
|
* **type**: the resource type using Heat or Heat-like resource types
|
||||||
|
* **data**: any additional fields for that resource
|
|
@ -76,12 +76,12 @@ def preserve_linefeeds(value):
|
||||||
return value.replace("\n", "\\n").replace("\r", "")
|
return value.replace("\n", "\\n").replace("\r", "")
|
||||||
|
|
||||||
|
|
||||||
def get_jinja_environment(template, extra_globals=None):
|
def get_jinja_environment(template, extra_globals=None, **env_vars):
|
||||||
"""Return a sandboxed jinja environment."""
|
"""Return a sandboxed jinja environment."""
|
||||||
template_map = {'template': template}
|
template_map = {'template': template}
|
||||||
env = sandbox.ImmutableSandboxedEnvironment(
|
env = sandbox.ImmutableSandboxedEnvironment(
|
||||||
loader=jinja2.DictLoader(template_map),
|
loader=jinja2.DictLoader(template_map),
|
||||||
bytecode_cache=CompilerCache())
|
bytecode_cache=CompilerCache(), **env_vars)
|
||||||
env.filters['prepend'] = do_prepend
|
env.filters['prepend'] = do_prepend
|
||||||
env.filters['preserve'] = preserve_linefeeds
|
env.filters['preserve'] = preserve_linefeeds
|
||||||
env.globals['json'] = json
|
env.globals['json'] = json
|
||||||
|
@ -90,14 +90,17 @@ def get_jinja_environment(template, extra_globals=None):
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def parse(template, extra_globals=None, **kwargs):
|
def parse(template, extra_globals=None, env_vars=None, **kwargs):
|
||||||
"""Parse template.
|
"""Parse template.
|
||||||
|
|
||||||
:param template: the template contents as a string
|
:param template: the template contents as a string
|
||||||
:param extra_globals: additional globals to include
|
:param extra_globals: additional globals to include
|
||||||
:param kwargs: extra arguments are passed to the renderer
|
:param kwargs: extra arguments are passed to the renderer
|
||||||
"""
|
"""
|
||||||
env = get_jinja_environment(template, extra_globals=extra_globals)
|
if env_vars is None:
|
||||||
|
env_vars = {}
|
||||||
|
env = get_jinja_environment(template, extra_globals=extra_globals,
|
||||||
|
**env_vars)
|
||||||
|
|
||||||
minimum_kwargs = {
|
minimum_kwargs = {
|
||||||
'data': {},
|
'data': {},
|
||||||
|
|
|
@ -23,7 +23,10 @@ Example usage:
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import ipaddress as ipaddress_module
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
from novaclient.v1_1 import client
|
from novaclient.v1_1 import client
|
||||||
from pythonwhois import shared
|
from pythonwhois import shared
|
||||||
import six
|
import six
|
||||||
|
@ -32,48 +35,95 @@ from satori import dns
|
||||||
from satori import utils
|
from satori import utils
|
||||||
|
|
||||||
|
|
||||||
def run(address, config=None, interactive=False):
|
def run(target, config=None, interactive=False):
|
||||||
"""Run discovery and return results."""
|
"""Run discovery and return results."""
|
||||||
if config is None:
|
if config is None:
|
||||||
config = {}
|
config = {}
|
||||||
|
|
||||||
results = {}
|
found = {}
|
||||||
if utils.is_valid_ip_address(address):
|
resources = {}
|
||||||
ipaddress = address
|
errors = {}
|
||||||
|
results = {
|
||||||
|
'target': target,
|
||||||
|
'created': utils.get_time_string(),
|
||||||
|
'found': found,
|
||||||
|
'resources': resources,
|
||||||
|
}
|
||||||
|
if utils.is_valid_ip_address(target):
|
||||||
|
ip_address = target
|
||||||
else:
|
else:
|
||||||
ipaddress = dns.resolve_hostname(address)
|
hostname = dns.parse_target_hostname(target)
|
||||||
|
found['hostname'] = hostname
|
||||||
|
ip_address = six.text_type(dns.resolve_hostname(hostname))
|
||||||
#TODO(sam): Use ipaddress.ip_address.is_global
|
#TODO(sam): Use ipaddress.ip_address.is_global
|
||||||
# " .is_private
|
# " .is_private
|
||||||
# " .is_unspecified
|
# " .is_unspecified
|
||||||
# " .is_multicast
|
# " .is_multicast
|
||||||
# To determine address "type"
|
# To determine address "type"
|
||||||
if not ipaddress_module.ip_address(unicode(ipaddress)).is_loopback:
|
if not ipaddress.ip_address(ip_address).is_loopback:
|
||||||
try:
|
try:
|
||||||
results['domain'] = dns.domain_info(address)
|
domain_info = dns.domain_info(hostname)
|
||||||
|
resource_type = 'OS::DNS::Domain'
|
||||||
|
identifier = '%s:%s' % (resource_type, hostname)
|
||||||
|
resources[identifier] = {
|
||||||
|
'type': resource_type,
|
||||||
|
'key': identifier,
|
||||||
|
}
|
||||||
|
found['domain-key'] = identifier
|
||||||
|
resources[identifier]['data'] = domain_info
|
||||||
|
if 'registered' in domain_info:
|
||||||
|
found['registered-domain'] = domain_info['registered']
|
||||||
except shared.WhoisException as exc:
|
except shared.WhoisException as exc:
|
||||||
results['domain'] = str(exc)
|
results['domain'] = str(exc)
|
||||||
|
found['ip-address'] = ip_address
|
||||||
|
|
||||||
results['address'] = ipaddress
|
host, host_errors = discover_host(ip_address, config,
|
||||||
|
interactive=interactive)
|
||||||
|
if host_errors:
|
||||||
|
errors.update(host_errors)
|
||||||
|
key = host.get('key') or ip_address
|
||||||
|
resources[key] = host
|
||||||
|
found['host-key'] = key
|
||||||
|
results['updated'] = utils.get_time_string()
|
||||||
|
return results, errors
|
||||||
|
|
||||||
results['host'] = host = {'type': 'Undetermined'}
|
|
||||||
|
def discover_host(address, config, interactive=False):
|
||||||
|
"""Discover host by IP address."""
|
||||||
|
host = {}
|
||||||
|
errors = {}
|
||||||
if config.get('username'):
|
if config.get('username'):
|
||||||
server = find_nova_host(ipaddress, config)
|
server = find_nova_host(address, config)
|
||||||
if server:
|
if server:
|
||||||
host['type'] = 'Nova instance'
|
host['type'] = 'OS::Nova::Instance'
|
||||||
host['uri'] = [l['href'] for l in server.links
|
data = {}
|
||||||
|
host['data'] = data
|
||||||
|
data['uri'] = [l['href'] for l in server.links
|
||||||
if l['rel'] == 'self'][0]
|
if l['rel'] == 'self'][0]
|
||||||
host['name'] = server.name
|
data['name'] = server.name
|
||||||
host['id'] = server.id
|
data['id'] = server.id
|
||||||
host['addresses'] = server.addresses
|
data['addresses'] = server.addresses
|
||||||
|
host['key'] = data['uri']
|
||||||
|
|
||||||
if config.get('system_info'):
|
if config.get('system_info'):
|
||||||
module_name = config['system_info'].replace("-", "_")
|
module_name = config['system_info'].replace("-", "_")
|
||||||
if '.' not in module_name:
|
if '.' not in module_name:
|
||||||
module_name = 'satori.sysinfo.%s' % module_name
|
module_name = 'satori.sysinfo.%s' % module_name
|
||||||
system_info_module = utils.import_object(module_name)
|
system_info_module = utils.import_object(module_name)
|
||||||
result = system_info_module.get_systeminfo(ipaddress, config,
|
try:
|
||||||
interactive=interactive)
|
result = system_info_module.get_systeminfo(
|
||||||
host['system_info'] = result
|
address, config, interactive=interactive)
|
||||||
return results
|
host.setdefault('data', {})
|
||||||
|
host['data']['system_info'] = result
|
||||||
|
except Exception as exc:
|
||||||
|
exc_traceback = sys.exc_info()[2]
|
||||||
|
errors['system_info'] = {
|
||||||
|
'type': "ERROR",
|
||||||
|
'message': str(exc),
|
||||||
|
'exception': exc,
|
||||||
|
'traceback': traceback.format_tb(exc_traceback),
|
||||||
|
}
|
||||||
|
return host, errors
|
||||||
|
|
||||||
|
|
||||||
def find_nova_host(address, config):
|
def find_nova_host(address, config):
|
||||||
|
@ -86,6 +136,6 @@ def find_nova_host(address, config):
|
||||||
service_type="compute")
|
service_type="compute")
|
||||||
for server in nova.servers.list():
|
for server in nova.servers.list():
|
||||||
for network_addresses in six.itervalues(server.addresses):
|
for network_addresses in six.itervalues(server.addresses):
|
||||||
for ipaddress in network_addresses:
|
for ip_address in network_addresses:
|
||||||
if ipaddress['addr'] == address:
|
if ip_address['addr'] == address:
|
||||||
return server
|
return server
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
"""Satori DNS Discovery."""
|
"""Satori DNS Discovery."""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -27,21 +27,24 @@ from satori import utils
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def resolve_hostname(host):
|
def parse_target_hostname(target):
|
||||||
"""Get IP address of hostname or URL."""
|
"""Get IP address or FQDN of a target which could be a URL or address."""
|
||||||
|
if not target:
|
||||||
|
raise errors.SatoriInvalidNetloc("Target must be supplied.")
|
||||||
try:
|
try:
|
||||||
if not host:
|
parsed = urlparse.urlparse(target)
|
||||||
raise AttributeError("Host must be supplied.")
|
|
||||||
parsed = urlparse.urlparse(host)
|
|
||||||
except AttributeError as err:
|
except AttributeError as err:
|
||||||
error = "Hostname `%s` is unparseable. Error: %s" % (host, err)
|
error = "Target `%s` is unparseable. Error: %s" % (target, err)
|
||||||
LOG.exception(error)
|
LOG.exception(error)
|
||||||
raise errors.SatoriInvalidNetloc(error)
|
raise errors.SatoriInvalidNetloc(error)
|
||||||
|
|
||||||
# Domain names and IP are in netloc when parsed with a protocol
|
# Domain names and IP are in netloc when parsed with a protocol
|
||||||
# they will be in path if parsed without a protocol
|
# they will be in path if parsed without a protocol
|
||||||
hostname = parsed.netloc or parsed.path
|
return parsed.netloc or parsed.path
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_hostname(hostname):
|
||||||
|
"""Get IP address of hostname."""
|
||||||
try:
|
try:
|
||||||
address = socket.gethostbyname(hostname)
|
address = socket.gethostbyname(hostname)
|
||||||
except socket.gaierror:
|
except socket.gaierror:
|
||||||
|
@ -55,13 +58,13 @@ def get_registered_domain(hostname):
|
||||||
return tldextract.extract(hostname).registered_domain
|
return tldextract.extract(hostname).registered_domain
|
||||||
|
|
||||||
|
|
||||||
def ip_info(ip):
|
def ip_info(ip_address):
|
||||||
"""Get as much information as possible for a given ip address."""
|
"""Get as much information as possible for a given ip address."""
|
||||||
if not utils.is_valid_ip_address(ip):
|
if not utils.is_valid_ip_address(ip_address):
|
||||||
error = "`%s` is an invalid IP address." % ip
|
error = "`%s` is an invalid IP address." % ip_address
|
||||||
raise errors.SatoriInvalidIP(error)
|
raise errors.SatoriInvalidIP(error)
|
||||||
|
|
||||||
result = pythonwhois.get_whois(ip)
|
result = pythonwhois.get_whois(ip_address)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'whois': result['raw']
|
'whois': result['raw']
|
||||||
|
|
|
@ -1,20 +1,54 @@
|
||||||
{% if data['address'] != target %}Address:
|
{% set found = data['found'] | default({}) %}
|
||||||
{{ target }} resolves to IPv4 address {{ data.address }}
|
{% set resources = data['resources'] | default({'n/a': {}}) %}
|
||||||
{% endif %}{% if data['domain'] %}Domain: {{ data.domain.name }}
|
{% set address = found['ip-address'] %}
|
||||||
Registrar: {{ data.domain.registrar }}{% if data.domain.nameservers %}
|
{% set hostkey = found['host-key'] | default('n/a') %}
|
||||||
Nameservers: {% for nameserver in data.domain.nameservers %}{{nameserver}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}{% endif %}{% if data['domain'] and data.domain.days_until_expires %} Expires: {{ data.domain.days_until_expires }} days{% endif %}{% if data['host'] and data.host.type == 'Nova instance' %}Host:
|
{% set domainkey = found['domain-key'] | default('n/a') %}
|
||||||
{{ data.address }} ({{ target }}) is hosted on a {{ data.host.type }}
|
{% set server = resources[hostkey] | default(False) %}
|
||||||
Instance Information:
|
{% set domain = resources[domainkey] | default(False) %}
|
||||||
URI: {{ data.host.uri }}
|
|
||||||
Name: {{ data.host.name }}
|
{% if found['ip-address'] != target %}Address:
|
||||||
ID: {{ data.host.id }}
|
{{ target }} resolves to IPv4 address {{ found['ip-address'] }}
|
||||||
ip-addresses:{% for name, addresses in data.host.addresses.items() %}
|
{%- endif %}
|
||||||
{{ name }}:{% for address in addresses %}
|
|
||||||
{{ address.addr }}{% endfor %}{% endfor %}
|
{% if domain %}Domain: {{ domain['data'].name }}
|
||||||
{% elif data['address'] %}Host:
|
Registrar: {{ domain['data'].registrar }}
|
||||||
ip-address: {{ data.address }}
|
{% if domain['data'].nameservers %}
|
||||||
{% else %}Host not found
|
Nameservers: {% for nameserver in domain['data'].nameservers %}{{nameserver}}{% if not loop.last %}, {% endif %}{% endfor %}
|
||||||
{% endif %}{% if data['host'] and data.host.system_info %}Packages:
|
|
||||||
{% for name, package in data.host.system_info.packages.items() %} {{ name }}: {{ package.version }}
|
{% endif %}
|
||||||
{% endfor %}
|
{% if domain['data'].days_until_expires %}
|
||||||
|
Expires: {{ domain['data'].days_until_expires }} days
|
||||||
|
{% endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{% if server and server.type == 'OS::Nova::Instance' %}
|
||||||
|
Host:
|
||||||
|
{{ found['ip-address'] }} ({{ target }}) is hosted on a Nova instance
|
||||||
|
{% if 'data' in server %} Instance Information:
|
||||||
|
URI: {{ server['data'].uri | default('n/a') }}
|
||||||
|
Name: {{ server['data'].name | default('n/a') }}
|
||||||
|
ID: {{ server['data'].id | default('n/a') }}
|
||||||
|
{% if 'addresses' in server['data'] %} ip-addresses:
|
||||||
|
{% for name, addresses in server['data'].addresses.items() %}
|
||||||
|
{{ name }}:
|
||||||
|
{% for address in addresses %}
|
||||||
|
{{ address.addr }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}{% endif %}{% endif %}
|
||||||
|
{% elif found['ip-address'] %}
|
||||||
|
Host:
|
||||||
|
ip-address: {{ found['ip-address'] }}
|
||||||
|
{% else %}Host not found
|
||||||
|
{% endif %}
|
||||||
|
{% if server and 'data' in server and server['data'].system_info %}
|
||||||
|
{% if 'remote_services' in server['data'].system_info %}
|
||||||
|
Listening Services:
|
||||||
|
{% for remote in server['data'].system_info.remote_services | sort %}
|
||||||
|
{{ remote.ip }}:{{ remote.port }} {{ remote.process }}
|
||||||
|
{% endfor %}{% endif %}
|
||||||
|
{% if 'connections' in server['data'].system_info %}
|
||||||
|
Talking to:
|
||||||
|
{% for connection in server['data'].system_info.connections | dictsort %}
|
||||||
|
{{ connection[0] }}{% if connection[1] %} on {% for port in connection[1] %}{{ port }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}
|
||||||
|
|
||||||
|
{% endfor %}{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
Accept a network location, run through the discovery process and report the
|
Accept a network location, run through the discovery process and report the
|
||||||
findings back to the user.
|
findings back to the user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
@ -225,9 +224,12 @@ def main(argv=None):
|
||||||
get_template_path(config['format']))
|
get_template_path(config['format']))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
results = discovery.run(config['netloc'], config, interactive=True)
|
results, errors = discovery.run(config['netloc'], config,
|
||||||
|
interactive=True)
|
||||||
print(format_output(config['netloc'], results,
|
print(format_output(config['netloc'], results,
|
||||||
template_name=config['format']))
|
template_name=config['format']))
|
||||||
|
if errors:
|
||||||
|
sys.stderr.write(format_errors(errors, config))
|
||||||
except Exception as exc: # pylint: disable=W0703
|
except Exception as exc: # pylint: disable=W0703
|
||||||
if config['debug']:
|
if config['debug']:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
|
@ -263,8 +265,20 @@ def format_output(discovered_target, results, template_name="text"):
|
||||||
return(json.dumps(results, indent=2))
|
return(json.dumps(results, indent=2))
|
||||||
else:
|
else:
|
||||||
template = get_template(template_name)
|
template = get_template(template_name)
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
return templating.parse(template, target=discovered_target,
|
return templating.parse(template, target=discovered_target,
|
||||||
data=results).strip('\n')
|
data=results, env_vars=env_vars).strip('\n')
|
||||||
|
|
||||||
|
|
||||||
|
def format_errors(errors, config):
|
||||||
|
"""Format errors for output to console."""
|
||||||
|
if config['debug']:
|
||||||
|
return str(errors)
|
||||||
|
else:
|
||||||
|
formatted = {}
|
||||||
|
for key, error in errors.items():
|
||||||
|
formatted[key] = error['message']
|
||||||
|
return str(formatted)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"""."""
|
"""Modules for Data Plane Discovery."""
|
||||||
|
|
|
@ -136,13 +136,13 @@ class TestDNS(utils.TestCase):
|
||||||
def test_resolve_int_raises_invalid_netloc_error(self):
|
def test_resolve_int_raises_invalid_netloc_error(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
errors.SatoriInvalidNetloc,
|
errors.SatoriInvalidNetloc,
|
||||||
dns.resolve_hostname,
|
dns.parse_target_hostname,
|
||||||
100)
|
100)
|
||||||
|
|
||||||
def test_resolve_none_raises_invalid_netloc_error(self):
|
def test_resolve_none_raises_invalid_netloc_error(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
errors.SatoriInvalidNetloc,
|
errors.SatoriInvalidNetloc,
|
||||||
dns.resolve_hostname,
|
dns.parse_target_hostname,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
def test_registered_domain_subdomain_removed(self):
|
def test_registered_domain_subdomain_removed(self):
|
||||||
|
|
|
@ -29,20 +29,25 @@ class TestTextTemplate(unittest.TestCase):
|
||||||
|
|
||||||
def test_no_data(self):
|
def test_no_data(self):
|
||||||
"""Handles response with no host."""
|
"""Handles response with no host."""
|
||||||
result = templating.parse(self.template, data={})
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
|
result = templating.parse(self.template, data={}, env_vars=env_vars)
|
||||||
self.assertEqual(result.strip('\n'), 'Host not found')
|
self.assertEqual(result.strip('\n'), 'Host not found')
|
||||||
|
|
||||||
def test_target_is_ip(self):
|
def test_target_is_ip(self):
|
||||||
"""Handles response when host is just the supplied address."""
|
"""Handles response when host is just the supplied address."""
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
result = templating.parse(self.template, target='127.0.0.1',
|
result = templating.parse(self.template, target='127.0.0.1',
|
||||||
data={'address': '127.0.0.1'})
|
data={'found': {'ip-address': '127.0.0.1'}},
|
||||||
|
env_vars=env_vars)
|
||||||
self.assertEqual(result.strip('\n'),
|
self.assertEqual(result.strip('\n'),
|
||||||
'Host:\n ip-address: 127.0.0.1')
|
'Host:\n ip-address: 127.0.0.1')
|
||||||
|
|
||||||
def test_host_not_server(self):
|
def test_host_not_server(self):
|
||||||
"""Handles response when host is not a nova instance."""
|
"""Handles response when host is not a nova instance."""
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
result = templating.parse(self.template, target='localhost',
|
result = templating.parse(self.template, target='localhost',
|
||||||
data={'address': '127.0.0.1'})
|
data={'found': {'ip-address': '127.0.0.1'}},
|
||||||
|
env_vars=env_vars)
|
||||||
self.assertEqual(result.strip('\n'),
|
self.assertEqual(result.strip('\n'),
|
||||||
'Address:\n localhost resolves to IPv4 address '
|
'Address:\n localhost resolves to IPv4 address '
|
||||||
'127.0.0.1\nHost:\n ip-address: 127.0.0.1')
|
'127.0.0.1\nHost:\n ip-address: 127.0.0.1')
|
||||||
|
@ -50,20 +55,44 @@ class TestTextTemplate(unittest.TestCase):
|
||||||
def test_host_is_nova_instance(self):
|
def test_host_is_nova_instance(self):
|
||||||
"""Handles response when host is a nova instance."""
|
"""Handles response when host is a nova instance."""
|
||||||
data = {
|
data = {
|
||||||
'address': '10.1.1.45',
|
'found': {
|
||||||
'host': {
|
'ip-address': '10.1.1.45',
|
||||||
'type': 'Nova instance',
|
'hostname': 'x',
|
||||||
|
'host-key': 'https://servers/path'
|
||||||
|
},
|
||||||
|
'target': 'instance.nova.local',
|
||||||
|
'resources': {
|
||||||
|
'https://servers/path': {
|
||||||
|
'type': 'OS::Nova::Instance',
|
||||||
|
'data': {
|
||||||
'uri': 'https://servers/path',
|
'uri': 'https://servers/path',
|
||||||
'id': '1000B',
|
'id': '1000B',
|
||||||
'name': 'x',
|
'name': 'x',
|
||||||
'addresses': {
|
'addresses': {
|
||||||
'public': [{'type': 'ipv4', 'addr': '10.1.1.45'}]
|
'public': [{'type': 'ipv4', 'addr': '10.1.1.45'}]
|
||||||
|
},
|
||||||
|
'system_info': {
|
||||||
|
'connections': {
|
||||||
|
'192.168.2.100': [],
|
||||||
|
'192.168.2.101': [433],
|
||||||
|
'192.168.2.102': [8080, 8081]
|
||||||
|
},
|
||||||
|
'remote_services': [
|
||||||
|
{
|
||||||
|
'ip': '0.0.0.0',
|
||||||
|
'process': 'nginx',
|
||||||
|
'port': 80
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
result = templating.parse(self.template,
|
result = templating.parse(self.template,
|
||||||
target='instance.nova.local',
|
target='instance.nova.local',
|
||||||
data=data)
|
data=data, env_vars=env_vars)
|
||||||
expected = """\
|
expected = """\
|
||||||
Address:
|
Address:
|
||||||
instance.nova.local resolves to IPv4 address 10.1.1.45
|
instance.nova.local resolves to IPv4 address 10.1.1.45
|
||||||
|
@ -75,7 +104,83 @@ Host:
|
||||||
ID: 1000B
|
ID: 1000B
|
||||||
ip-addresses:
|
ip-addresses:
|
||||||
public:
|
public:
|
||||||
10.1.1.45"""
|
10.1.1.45
|
||||||
|
Listening Services:
|
||||||
|
0.0.0.0:80 nginx
|
||||||
|
Talking to:
|
||||||
|
192.168.2.100
|
||||||
|
192.168.2.101 on 433
|
||||||
|
192.168.2.102 on 8080, 8081"""
|
||||||
|
self.assertEqual(result.strip('\n'), expected)
|
||||||
|
|
||||||
|
def test_host_has_no_data(self):
|
||||||
|
"""Handles response when host is a nova instance."""
|
||||||
|
data = {
|
||||||
|
'found': {
|
||||||
|
'ip-address': '10.1.1.45',
|
||||||
|
'hostname': 'x',
|
||||||
|
'host-key': 'https://servers/path'
|
||||||
|
},
|
||||||
|
'target': 'instance.nova.local',
|
||||||
|
'resources': {
|
||||||
|
'https://servers/path': {
|
||||||
|
'type': 'OS::Nova::Instance'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
|
result = templating.parse(self.template,
|
||||||
|
target='instance.nova.local',
|
||||||
|
data=data, env_vars=env_vars)
|
||||||
|
expected = """\
|
||||||
|
Address:
|
||||||
|
instance.nova.local resolves to IPv4 address 10.1.1.45
|
||||||
|
Host:
|
||||||
|
10.1.1.45 (instance.nova.local) is hosted on a Nova instance"""
|
||||||
|
self.assertEqual(result.strip('\n'), expected)
|
||||||
|
|
||||||
|
def test_host_data_missing_items(self):
|
||||||
|
"""Handles response when host is a nova instance."""
|
||||||
|
data = {
|
||||||
|
'found': {
|
||||||
|
'ip-address': '10.1.1.45',
|
||||||
|
'hostname': 'x',
|
||||||
|
'host-key': 'https://servers/path'
|
||||||
|
},
|
||||||
|
'target': 'instance.nova.local',
|
||||||
|
'resources': {
|
||||||
|
'https://servers/path': {
|
||||||
|
'type': 'OS::Nova::Instance',
|
||||||
|
'data': {
|
||||||
|
'id': '1000B',
|
||||||
|
'system_info': {
|
||||||
|
'remote_services': [
|
||||||
|
{
|
||||||
|
'ip': '0.0.0.0',
|
||||||
|
'process': 'nginx',
|
||||||
|
'port': 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env_vars = dict(lstrip_blocks=True, trim_blocks=True)
|
||||||
|
result = templating.parse(self.template,
|
||||||
|
target='instance.nova.local',
|
||||||
|
data=data, env_vars=env_vars)
|
||||||
|
expected = """\
|
||||||
|
Address:
|
||||||
|
instance.nova.local resolves to IPv4 address 10.1.1.45
|
||||||
|
Host:
|
||||||
|
10.1.1.45 (instance.nova.local) is hosted on a Nova instance
|
||||||
|
Instance Information:
|
||||||
|
URI: n/a
|
||||||
|
Name: n/a
|
||||||
|
ID: 1000B
|
||||||
|
Listening Services:
|
||||||
|
0.0.0.0:80 nginx"""
|
||||||
self.assertEqual(result.strip('\n'), expected)
|
self.assertEqual(result.strip('\n'), expected)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue