From 07cb34e82d30efd501e3fe513827acb527e41749 Mon Sep 17 00:00:00 2001 From: Scott Hussey Date: Wed, 22 Feb 2017 17:32:55 -0600 Subject: [PATCH] Initial Python skeleton and the model classes to match the YAML schema --- helm_drydock/__init__.py | 13 + helm_drydock/config.py | 35 +++ helm_drydock/drivers/__init__.py | 19 ++ helm_drydock/drivers/server/__init__.py | 18 ++ .../drivers/server/maasdriver/__init__.py | 17 ++ helm_drydock/ingester/ingester.py | 72 +++++ helm_drydock/ingester/plugins/aicyaml.py | 55 ++++ .../ingester/plugins/ingester_plugin.py | 30 ++ helm_drydock/model/__init__.py | 286 ++++++++++++++++++ helm_drydock/model/__init__.pyc | Bin 0 -> 8583 bytes helm_drydock/tox.ini | 11 + requirements.txt | 7 + setup.py | 56 ++++ testrequirements.txt | 2 + .../test_models.cpython-35-PYTEST.pyc | Bin 0 -> 2697 bytes tests/test_models.py | 69 +++++ tests/test_models.pyc | Bin 0 -> 1985 bytes tox.ini | 15 + 18 files changed, 705 insertions(+) create mode 100644 helm_drydock/__init__.py create mode 100644 helm_drydock/config.py create mode 100644 helm_drydock/drivers/__init__.py create mode 100644 helm_drydock/drivers/server/__init__.py create mode 100644 helm_drydock/drivers/server/maasdriver/__init__.py create mode 100644 helm_drydock/ingester/ingester.py create mode 100644 helm_drydock/ingester/plugins/aicyaml.py create mode 100644 helm_drydock/ingester/plugins/ingester_plugin.py create mode 100644 helm_drydock/model/__init__.py create mode 100644 helm_drydock/model/__init__.pyc create mode 100644 helm_drydock/tox.ini create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 testrequirements.txt create mode 100644 tests/__pycache__/test_models.cpython-35-PYTEST.pyc create mode 100644 tests/test_models.py create mode 100644 tests/test_models.pyc create mode 100644 tox.ini diff --git a/helm_drydock/__init__.py b/helm_drydock/__init__.py new file mode 100644 index 00000000..2a385a45 --- /dev/null +++ b/helm_drydock/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. \ No newline at end of file diff --git a/helm_drydock/config.py b/helm_drydock/config.py new file mode 100644 index 00000000..bf364d1f --- /dev/null +++ b/helm_drydock/config.py @@ -0,0 +1,35 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Read application configuration +# + +# configuration map with defaults + +class DrydockConfig(object): + + def __init__(self): + self.selected_server_driver = helm_drydock.drivers.server.maasdriver + self.selected_network_driver = helm_drydock.drivers.network.noopdriver + self.control_config = {} + self.ingester_config = { + plugins = [helm_drydock.ingester.plugins.aicyaml.AicYamlIngester] + } + self.introspection_config = {} + self.orchestrator_config = {} + self.statemgmt_config = { + backend_driver = 'helm_drydock.drivers.statemgmt.etcd', + } diff --git a/helm_drydock/drivers/__init__.py b/helm_drydock/drivers/__init__.py new file mode 100644 index 00000000..5c73a1fa --- /dev/null +++ b/helm_drydock/drivers/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ProviderDriver(object): + + __init__(self): + pass + diff --git a/helm_drydock/drivers/server/__init__.py b/helm_drydock/drivers/server/__init__.py new file mode 100644 index 00000000..febcdad1 --- /dev/null +++ b/helm_drydock/drivers/server/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import helm_drydock.drivers.ProviderDriver + +class ServerDriver(ProviderDriver): + \ No newline at end of file diff --git a/helm_drydock/drivers/server/maasdriver/__init__.py b/helm_drydock/drivers/server/maasdriver/__init__.py new file mode 100644 index 00000000..21cbcd26 --- /dev/null +++ b/helm_drydock/drivers/server/maasdriver/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import helm_drydock.drivers.server.ServerDriver + +class MaasServerDriver(object): + \ No newline at end of file diff --git a/helm_drydock/ingester/ingester.py b/helm_drydock/ingester/ingester.py new file mode 100644 index 00000000..ffdf2761 --- /dev/null +++ b/helm_drydock/ingester/ingester.py @@ -0,0 +1,72 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ingester - Ingest host topologies to define site design and +# persist design to helm-drydock's statemgmt service + +import logging +import yaml + +class Ingester(object): + + registered_plugins = {} + + def __init__(self): + logging.basicConfig(format="%(asctime)-15s [%(levelname)] %(module)s %(process)d %(message)s") + self.log = logging.Logger("ingester") + + """ + enable_plugins + + params: plugins - A list of class objects denoting the ingester plugins to be enabled + + Enable plugins that can be used for ingest_data calls. Each plugin should use + helm_drydock.ingester.plugins.IngesterPlugin as its base class. As long as one + enabled plugin successfully initializes, the call is considered successful. Otherwise + it will throw an exception + """ + def enable_plugins(self, plugins=[]): + if len(plugin) == 0: + self.log.error("Cannot have an empty plugin list.") + + for plugin in plugins: + try: + new_plugin = plugin() + plugin_name = new_plugin.get_name() + registered_plugins[plugin_name] = new_plugin + except: + self.log.error("Could not enable plugin %s" % (plugin.__name__)) + + if len(registered_plugins) == 0: + self.log.error("Could not enable at least one plugin") + raise Exception("Could not enable at least one plugin") + + """ + ingest_data + + params: plugin_name - Which plugin should be used for ingestion + params: params - A map of parameters that will be passed to the plugin's ingest_data method + + Execute a data ingestion using the named plugin (assuming it is enabled) + """ + def ingest_data(self, plugin_name, params={}): + if plugin_name in registered_plugins: + design_data = registered_plugins[plugin_name].ingest_data(params) + # Need to persist data here, but we don't yet have the statemgmt service working + yaml.dump(design_data) + else + self.log.error("Could not find plugin %s to ingest data." % (plugin_name)) + raise LookupError("Could not find plugin %s" % plugin_name) + + diff --git a/helm_drydock/ingester/plugins/aicyaml.py b/helm_drydock/ingester/plugins/aicyaml.py new file mode 100644 index 00000000..c54c6eed --- /dev/null +++ b/helm_drydock/ingester/plugins/aicyaml.py @@ -0,0 +1,55 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# AIC YAML Ingester - This data ingester will consume a AIC YAML design +# file +# +import yaml +import logging + +import helm_drydock.ingester.plugins.IngesterPlugin + +class AicYamlIngester(IngesterPlugin): + + def __init__(self): + super(AicYamlIngester, self).__init__() + + def get_name(self): + return "aic_yaml" + + """ + AIC YAML ingester params + + filename - Absolute path to the YAML file to ingest + """ + def ingest_data(self, **kwargs): + if 'filename' in params: + input_string = read_input_file(params['filename']) + parsed_data = parse_input_data(input_string) + processed_data = compute_effective_data(parsed_data) + else: + + raise Exception('Missing parameter') + + return processed_data + + def read_input_file(self, filename): + try: + file = open(filename,'rt') + except OSError as err: + self.log.error("Error opening input file %s for ingestion: %s" % (filename, err)) + return {} + + diff --git a/helm_drydock/ingester/plugins/ingester_plugin.py b/helm_drydock/ingester/plugins/ingester_plugin.py new file mode 100644 index 00000000..8d4dc955 --- /dev/null +++ b/helm_drydock/ingester/plugins/ingester_plugin.py @@ -0,0 +1,30 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Plugins to parse incoming topology and translate it to helm-drydock's +# intermediate representation + +import logging + +class IngesterPlugin(object): + + def __init__(self): + self.log = logging.Logger('ingester') + return + + def get_data(self): + return "ingester_skeleton" + + def ingest_data(self, **kwargs): + return {} diff --git a/helm_drydock/model/__init__.py b/helm_drydock/model/__init__.py new file mode 100644 index 00000000..1be9d722 --- /dev/null +++ b/helm_drydock/model/__init__.py @@ -0,0 +1,286 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Models for helm_drydock +# + + +class HardwareProfile(object): + + def __init__(self, **kwargs): + self.api_version = kwargs.get('apiVersion', '') + + if self.api_version == "1.0": + metadata = kwargs.get('metadata', {}) + spec = kwargs.get('spec', {}) + + # Need to add validation logic, we'll assume the input is + # valid for now + self.name = metadata.get('name', '') + self.region = metadata.get('region', '') + self.vendor = spec.get('vendor', '') + self.generation = spec.get('generation', '') + self.hw_version = spec.get('hw_version', '') + self.bios_version = spec.get('bios_version', '') + self.boot_mode = spec.get('boot_mode', '') + self.bootstrap_protocol = spec.get('bootstrap_protocol', '') + self.pxe_interface = spec.get('pxe_interface', '') + self.devices = [] + + device_aliases = spec.get('device_aliases', {}) + + pci_devices = device_aliases.get('pci', []) + scsi_devices = device_aliases.get('scsi', []) + + for d in pci_devices: + d['bus_type'] = 'pci' + self.devices.append( + HardwareDeviceAlias(self.api_version, **d)) + + for d in scsi_devices: + d['bus_type'] = 'scsi' + self.devices.append( + HardwareDeviceAlias(self.api_version, **d)) + else: + raise ValueError('Unknown API version of object') + + return + + +class HardwareDeviceAlias(object): + + def __init__(self, api_version, **kwargs): + self.api_version = api_version + + if self.api_version == "1.0": + self.bus_type = kwargs.get('bus_type', '') + self.address = kwargs.get('address', '') + self.alias = kwargs.get('alias', '') + self.type = kwargs.get('type', '') + else: + raise ValueError('Unknown API version of object') + + return + + +class Site(object): + + def __init__(self, **kwargs): + self.api_version = kwargs.get('apiVersion', '') + + if self.api_version == "1.0": + metadata = kwargs.get('metadata', {}) + + # Need to add validation logic, we'll assume the input is + # valid for now + self.name = metadata.get('name', '') + + self.networks = [] + self.network_links = [] + self.host_profiles = [] + self.hardware_profiles = [] + self.baremetal_nodes = [] + + else: + raise ValueError('Unknown API version of object') + + +class NetworkLink(object): + + def __init__(self, **kwargs): + self.api_version = kwargs.get('apiVersion', '') + + if self.api_version == "1.0": + metadata = kwargs.get('metadata', {}) + spec = kwargs.get('spec', {}) + + self.name = metadata.get('name', '') + self.region = metadata.get('region', '') + + bonding = spec.get('bonding', {}) + self.bonding_mode = bonding.get('mode', 'none') + + # TODO How should we define defaults for CIs not in the input? + if self.bonding_mode == '802.3ad': + self.bonding_xmit_hash = bonding.get('hash', 'layer3+4') + self.bonding_peer_rate = bonding.get('peer_rate', 'fast') + self.bonding_mon_rate = bonding.get('mon_rate', '') + self.bonding_up_delay = bonding.get('up_delay', '') + self.bonding_down_delay = bonding.get('down_delay', '') + + self.mtu = spec.get('mtu', 1500) + self.linkspeed = spec.get('linkspeed', 'auto') + + trunking = spec.get('trunking', {}) + self.trunk_mode = trunking.get('mode', 'none') + + self.native_network = spec.get('default_network', '') + else: + raise ValueError('Unknown API version of object') + + +class Network(object): + + def __init__(self, **kwargs): + self.api_version = kwargs.get('apiVersion', '') + + if self.api_version == "1.0": + metadata = kwargs.get('metadata', {}) + spec = kwargs.get('spec', {}) + + self.name = metadata.get('name', '') + self.region = metadata.get('region', '') + self.cidr = spec.get('cidr', '') + self.allocation_strategy = spec.get('allocation', 'static') + self.vlan_id = spec.get('vlan_id', 1) + self.mtu = spec.get('mtu', 0) + + dns = spec.get('dns', {}) + self.dns_domain = dns.get('domain', 'local') + self.dns_servers = dns.get('servers', '') + + ranges = spec.get('ranges', []) + self.ranges = [] + + for r in ranges: + self.ranges.append(NetworkAddressRange(self.api_version, **r)) + + routes = spec.get('routes', []) + self.routes = [] + + for r in routes: + self.routes.append(NetworkRoute(self.api_version, **r)) + else: + raise ValueError('Unknown API version of object') + + +class NetworkAddressRange(object): + + def __init__(self, api_version, **kwargs): + self.api_version = api_version + + if self.api_version == "1.0": + self.type = kwargs.get('type', 'static') + self.start = kwargs.get('start', '') + self.end = kwargs.get('end', '') + else: + raise ValueError('Unknown API version of object') + + +class NetworkRoute(object): + + def __init__(self, api_version, **kwargs): + self.api_version = api_version + + if self.api_version == "1.0": + self.type = kwargs.get('subnet', '') + self.start = kwargs.get('gateway', '') + self.end = kwargs.get('metric', 100) + else: + raise ValueError('Unknown API version of object') + + +class HostProfile(object): + + def __init__(self, **kwargs): + self.api_version = kwargs.get('apiVersion', '') + + if self.api_version == "1.0": + metadata = kwargs.get('metadata', {}) + spec = kwargs.get('spec', {}) + + self.name = metadata.get('name', '') + self.region = metadata.get('region', '') + + oob = spec.get('oob', {}) + self.oob_type = oob.get('type', 'ipmi') + self.oob_network = oob.get('network', 'oob') + self.oob_account = oob.get('account', '') + self.oob_credential = oob.get('credential', '') + + storage = spec.get('storage', {}) + self.storage_layout = storage.get('layout', 'lvm') + + bootdisk = storage.get('bootdisk', {}) + self.bootdisk_device = bootdisk.get('device', '') + self.bootdisk_root_size = bootdisk.get('root_size', '') + self.bootdisk_boot_size = bootdisk.get('boot_size', '') + + partitions = storage.get('partitions', []) + self.partitions = [] + + for p in partitions: + self.partitions.append(HostPartition(self.api_version, **p)) + + interfaces = spec.get('interfaces', []) + self.interfaces = [] + + for i in interfaces: + self.interfaces.append(HostInterface(self.api_version, **i)) + + else: + raise ValueError('Unknown API version of object') + + +class HostInterface(object): + + def __init__(self, api_version, **kwargs): + self.api_version = api_version + + if self.api_version == "1.0": + self.device_name = kwargs.get('device_name', '') + self.network_link = kwargs.get('device_link', '') + + self.hardware_slaves = [] + slaves = kwargs.get('slaves', []) + + for s in slaves: + self.hardware_slaves.append(s) + + self.networks = [] + networks = kwargs.get('networks', []) + + for n in networks: + self.networks.append(n) + + else: + raise ValueError('Unknown API version of object') + + +class HostPartition(object): + + def __init__(self, api_version, **kwargs): + self.api_version = api_version + + if self.api_version == "1.0": + self.name = kwargs.get('name', '') + self.device = kwargs.get('device', '') + self.part_uuid = kwargs.get('part_uuid', '') + self.size = kwargs.get('size', '') + self.mountpoint = kwargs.get('mountpoint', '') + self.fstype = kwargs.get('fstype', 'ext4') + self.mount_options = kwargs.get('mount_options', 'defaults') + self.fs_uuid = kwargs.get('fs_uuid', '') + self.fs_label = kwargs.get('fs_label', '') + else: + raise ValueError('Unknown API version of object') + + +# A BaremetalNode is really nothing more than a physical +# instantiation of a HostProfile, so they both represent +# the same set of CIs +class BaremetalNode(HostProfile): + + def __init__(self, **kwargs): + super(BaremetalNode, self).__init__() diff --git a/helm_drydock/model/__init__.pyc b/helm_drydock/model/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76853f3aee2eec228fcea1efaf93be5298859a61 GIT binary patch literal 8583 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 ReXf1JJ%|4b?X&Ine*pbW#A*Nl literal 0 HcmV?d00001 diff --git a/helm_drydock/tox.ini b/helm_drydock/tox.ini new file mode 100644 index 00000000..ae414018 --- /dev/null +++ b/helm_drydock/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py35 + +[testenv] +deps= + -rrequirements.txt +setenv= + PYTHONWARNING=all + +[flake8] +ignore=E302,H306 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..9c39a3da --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +PyYAML +oauth +requests-oauthlib +pyipmi +netaddr +pecan +python-libmaas \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..870b0bb0 --- /dev/null +++ b/setup.py @@ -0,0 +1,56 @@ +# Copyright 2017 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# helm_drydock - A tool to consume a host topology and orchestrate +# and monitor the provisioning of those hosts and execution of bootstrap +# scripts +# +# Modular services: +# smelter - A service to consume the host topology, will support multiple +# input formats. Initially supports a YAML schema as demonstrated +# in the examples folder +# tarot - A service for persisting the host topology and orchestration state +# and making the data available via API +# cockpit - The entrypoint API for users to control helm-drydock and query +# current state +# alchemist - The core orchestrator +# drivers - A tree with all of the plugins that alchemist uses to execute +# orchestrated tasks +# jabberwocky - An introspection API that newly provisioned nodes can use to +# ingest self-data and bootstrap their application deployment process + +from setuptools import setup + +setup(name='helm_drydock', + version='0.1a1', + description='Bootstrapper for Kubernetes infrastructure', + url='http://github.com/att-comdev/drydock', + author='Scott Hussey - AT&T', + author_email='sh8121@att.com', + license='Apache 2.0', + packages=['helm_drydock', + 'helm_drydock.model', + 'helm_drydock.ingester'], + install_requires=[ + 'PyYAML', + 'oauth', + 'requests-oauthlib', + 'pyipmi', + 'netaddr', + 'pecan' + ], + dependency_link=[ + 'git+https://github.com/maas/python-libmaas.git' + ] + ) \ No newline at end of file diff --git a/testrequirements.txt b/testrequirements.txt new file mode 100644 index 00000000..642edbdd --- /dev/null +++ b/testrequirements.txt @@ -0,0 +1,2 @@ +pytest +tox \ No newline at end of file diff --git a/tests/__pycache__/test_models.cpython-35-PYTEST.pyc b/tests/__pycache__/test_models.cpython-35-PYTEST.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c65a57e221401247636b14740eef8cc2781f4ff0 GIT binary patch literal 2697 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 TzUYV9H$3M3@l0i{hCBKf_g^yN literal 0 HcmV?d00001 diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..c92d6f00 --- /dev/null +++ b/tox.ini @@ -0,0 +1,15 @@ +[tox] +envlist = py35 + +[testenv] +deps= + -rrequirements.txt + -rtestrequirements.txt +setenv= + PYTHONWARNING=all +commands= + py.test \ + {posargs} + +[flake8] +ignore=E302,H306 \ No newline at end of file