diff --git a/discover_test.py b/discover_test.py index 8a72e2b..822085f 100644 --- a/discover_test.py +++ b/discover_test.py @@ -4,7 +4,7 @@ from itertools import groupby from ostack_validator.common import Issue, MarkedIssue, Inspection from ostack_validator.model import OpenstackComponent from ostack_validator.discovery import OpenstackDiscovery -from ostack_validator.inspections import KeystoneAuthtokenSettingsInspection, KeystoneEndpointsInspection +from ostack_validator.inspections import KeystoneAuthtokenSettingsInspection, KeystoneEndpointsInspection, LettuceRunnerInspection def indent_prefix(indent=0): s = '' @@ -62,7 +62,7 @@ def main(): openstack = discovery.discover(['172.18.65.179'], 'root', private_key=private_key) - all_inspections = [KeystoneAuthtokenSettingsInspection, KeystoneEndpointsInspection] + all_inspections = [KeystoneAuthtokenSettingsInspection, KeystoneEndpointsInspection, LettuceRunnerInspection] for inspection in all_inspections: x = inspection() x.inspect(openstack) diff --git a/doc/architecture_model.rst b/doc/architecture_model.rst index 1115d08..079e968 100644 --- a/doc/architecture_model.rst +++ b/doc/architecture_model.rst @@ -2,16 +2,20 @@ ARCHITECTURE DATA MODEL ======================= --------- Overview -------- -Architecture data model produced by Joker could be consumed by configuration -validator tool (Dark Knight), by architecture graph (Stencil) and others. +We want to introduce unified data structure which contains all information +necessary to inspect, analyze, describe and visualize OpenStack architecture. -At some point it should be made convertible into format accepted by deployment -systems (e.g. Fuel or TripleO) which will allow to effectively 'clone' OpenStack -clouds using different deployment applications. +This Architecture data model could be consumed and processed by configuration +analysis and diagnostics tool (**Dark Knight**) and by architecture visualizer +(**Stencil**). + +Arhictecture data model must include all information necessary to deployment +systems (e.g. **Fuel** or **TripleO**). We will implement simple conversion tools which +will allow to configure these deployment systems and effectively support +'portable' clouds. This model could be reused by Rally project to compare benchmarking results for different architectures. @@ -24,11 +28,10 @@ support contract pricing purposes. The model could be used to perform automated/guided hardening of OpenStack architecture and configuration. ------------ Data Format ----------- -This section proposes data model format which allows to describe any OpenStack +This section proposes data model format which allows to describe an OpenStack installation. The model includes data regarding physical infrastructure, logical topology of services and mapping between the two. diff --git a/doc/configuration_validator.rst b/doc/configuration_validator.rst new file mode 100644 index 0000000..f7a81ed --- /dev/null +++ b/doc/configuration_validator.rst @@ -0,0 +1,68 @@ +OPENSTACK DIAGNOSTICS PROPOSAL +============================== + +Project Name +------------ + +**Official:** OpenStack Diagnostics +**Codename:** Dark Knight + +Overview +-------- + +OpenStack cloud operators usually rely on deploymnet tools to configure all the +platform components correctly and efficiently upfront. However, after initial +deployment platform configurations and operational conditions start to change. +We propose a project that allows to analyze OpenStack architecture and diagnose +existing and potential problems using flexible set of rules. + +Mission +--------- + +Diagnostics project mission is to **provide OpenStack cloud operators with +flexible way to inspect, analyze and diagnose architecture of the cloud and +configuration of components of the platform**. + +User Stories +------------ + +As a **cloud operator**, I want to be able to automatically extract +configuration parameters from all OpenStack components to verify their +correctness, consistency and integrity. +As a **cloud architect**, I want to make sure that my OpenStack architecture and +configuration are compliant to 'best practices'. +As a **cloud operator**, I want automatic diagnostics tool which can tell me +what problems does my OpenStack architecture and/or configuration have or might +potentially have (e.g. at scale or if some component or node failed). +As a **cloud operator**, I want to be able to define rules used to inspect and +verify configuration of OpenStack components and store them to use for +verification of future configuration changes. + +Requirements +------------ + +TBD + +Scope +----- + +As an MVP1, we create a service that includes: + +1. Rules engine with grammatic analysis capabilities +1. Extensible implementation of rules +1. REST API for running inspections +1. Storage back-end implementation for OpenStack platform architecture and + configuration data + +Assumptions +----------- + +We assume that we must reuse as much as possible from OpenStack Deployment +program in terms of platform configuration and architecture definitions (i.e. +TripleO Heat and configuration files templates). + +Dependencies +------------ + +Design +------ diff --git a/doc/classes_No_Name.dot b/doc/images/src/classes_No_Name.dot similarity index 100% rename from doc/classes_No_Name.dot rename to doc/images/src/classes_No_Name.dot diff --git a/doc/packages_No_Name.dot b/doc/images/src/packages_No_Name.dot similarity index 100% rename from doc/packages_No_Name.dot rename to doc/images/src/packages_No_Name.dot diff --git a/ostack_validator/discovery.py b/ostack_validator/discovery.py index 5b8a89d..a25d409 100644 --- a/ostack_validator/discovery.py +++ b/ostack_validator/discovery.py @@ -408,5 +408,8 @@ class OpenstackDiscovery(object): if ' '.join(process).find('rabbit') == -1: return None - return None + rabbitmq = RabbitMqComponent() + rabbitmq.version = 'unknown' + + return rabbitmq diff --git a/ostack_validator/inspections/__init__.py b/ostack_validator/inspections/__init__.py index b9e71c5..3126d17 100644 --- a/ostack_validator/inspections/__init__.py +++ b/ostack_validator/inspections/__init__.py @@ -1,3 +1,4 @@ from ostack_validator.inspections.keystone_authtoken import KeystoneAuthtokenSettingsInspection from ostack_validator.inspections.keystone_endpoints import KeystoneEndpointsInspection +from ostack_validator.inspections.lettuce_runner import LettuceRunnerInspection diff --git a/ostack_validator/inspections/lettuce/sample.feature b/ostack_validator/inspections/lettuce/sample.feature new file mode 100644 index 0000000..3525747 --- /dev/null +++ b/ostack_validator/inspections/lettuce/sample.feature @@ -0,0 +1,8 @@ +Feature: Configuration consistency + + Scenario: Nova has proper Keystone host + Given I use OpenStack 2013.1 + And Nova has "auth_strategy" equal to "keystone" + And Keystone addresses are @X + Then Nova should have "keystone_authtoken.auth_host" in "$X" + diff --git a/ostack_validator/inspections/lettuce/steps.py b/ostack_validator/inspections/lettuce/steps.py new file mode 100644 index 0000000..acee845 --- /dev/null +++ b/ostack_validator/inspections/lettuce/steps.py @@ -0,0 +1,67 @@ +import string +from lettuce import * + +from ostack_validator.common import Issue, Version, find +from ostack_validator.model import * + +def get_variable(name): + if not hasattr(world, 'variables'): + return None + + return world.variables.get(name) + +def set_variable(name, value): + if not hasattr(world, 'variables'): + world.variables = {} + + world.variables[name] = value + +def subst(template): + if not hasattr(world, 'variables'): + return template + + tmpl = string.Template(template) + return tmpl.safe_substitute(world.variables) + +def stop(): + assert False, "stop" + +@step(r'I use OpenStack (\w+)') +def use_openstack_version(step, version): + version = Version(version) + for component in [c for c in world.openstack.components if isinstance(c, OpenstackComponent)]: + if not Version(component.version) >= version: stop() + +@step(r'Nova has "(.+)" equal to "(.*)"') +def nova_has_property(step, name, value): + name = subst(name) + value = subst(value) + + for nova in [c for c in world.openstack.components if c.name.startswith('nova')]: + if not nova.config[name] == value: stop() + +@step(r'Keystone addresses are @(\w+)') +def keystone_addresses(self, variable): + keystone = find(world.openstack.components, lambda c: c.name == 'keystone') + + if keystone.config['bind_host'] == '0.0.0.0': + addresses = filter(lambda ip: not ip.startswith('127.'), keystone.host.network_addresses) + else: + addresses = [keystone.config['bind_host']] + + set_variable(variable, addresses) + +@step(r'Nova should have "(.+)" in "(.*)"') +def nova_property_assertion(self, name, values): + name = subst(name) + values = subst(values) + + if not values: + return + + for nova in [c for c in world.openstack.components if c.name.startswith('nova')]: + nova_value = nova.config[name] + + if not (nova_value and nova_value in values): + nova.report_issue(Issue(Issue.ERROR, 'Nova should have "%s" in %s' % (name, values))) + diff --git a/ostack_validator/inspections/lettuce_runner.py b/ostack_validator/inspections/lettuce_runner.py new file mode 100644 index 0000000..cec92f1 --- /dev/null +++ b/ostack_validator/inspections/lettuce_runner.py @@ -0,0 +1,20 @@ +import os.path +import lettuce + +from ostack_validator.common import Inspection, Issue + +class LettuceRunnerInspection(Inspection): + def inspect(self, openstack): + runner = lettuce.Runner( + base_path=os.path.join(os.path.dirname(__file__), 'lettuce') + ) + + lettuce.world.openstack = openstack + result = runner.run() + del lettuce.world.openstack + + for feature_result in result.feature_results: + for scenario_result in [s for s in feature_result.scenario_results if not s.passed]: + for step in scenario_result.steps_undefined: + openstack.report_issue(Issue(Issue.ERROR, 'Undefined step "%s"' % step.sentence)) + diff --git a/ostack_validator/model.py b/ostack_validator/model.py index 01cd574..4dc66e3 100644 --- a/ostack_validator/model.py +++ b/ostack_validator/model.py @@ -270,7 +270,7 @@ class MysqlComponent(Service): name = 'mysql' class RabbitMqComponent(Service): - pass + name = 'rabbitmq' class FileResource(IssueReporter): def __init__(self, path, contents, owner, group, permissions):