Compare commits
6 Commits
8fd063d21d
...
9e0c0505b3
Author | SHA1 | Date | |
---|---|---|---|
9e0c0505b3 | |||
7ea4d070ee | |||
8f2ffd6619 | |||
622481e231 | |||
0bbc4ec1cd | |||
ec4fba16d6 |
@ -10,18 +10,84 @@ all:
|
||||
en1:
|
||||
vars:
|
||||
skylab_location: Newton MA
|
||||
# gross hack for now, will be refactored later
|
||||
_skylab_adguard_nat_rule: 8
|
||||
|
||||
hosts:
|
||||
core:
|
||||
ansible_host: 10.42.101.1
|
||||
ansible_port: 4242
|
||||
ansible_network_os: edgeos
|
||||
skylab_description: EN1 Core Router
|
||||
|
||||
iridium:
|
||||
ansible_host: 10.42.101.200
|
||||
skylab_description: Local Monitor Node
|
||||
skylab_targets: [network]
|
||||
|
||||
children:
|
||||
network:
|
||||
|
||||
cluster:
|
||||
hosts:
|
||||
core:
|
||||
ansible_host: core.net.enp.one
|
||||
ansible_port: 4242
|
||||
skylab_description: Core Router
|
||||
skylab_targets: [network]
|
||||
pegasus: # jupiter
|
||||
ansible_host: 10.42.101.100
|
||||
skylab_description: Arbiter Node
|
||||
skylab_targets: [cluster, datastore]
|
||||
skylab_cluster:
|
||||
address: 10.42.101.10/24
|
||||
interface: bond0
|
||||
|
||||
saturn: # remus
|
||||
ansible_host: 10.42.101.110
|
||||
skylab_description: Operation Node
|
||||
skylab_cluster:
|
||||
address: 10.42.101.110/24
|
||||
interface: bond0
|
||||
skylab_networking:
|
||||
hostname: saturn.skylab.enp.one
|
||||
dns: [10.42.101.1]
|
||||
gateway: 10.42.101.1/24
|
||||
interfaces:
|
||||
bond0:
|
||||
type: bond
|
||||
members: [eno1, eno2]
|
||||
addresses:
|
||||
- 10.42.101.11/24
|
||||
- 10.42.101.110/24
|
||||
dhcp: false
|
||||
bond0.99:
|
||||
type: vlan
|
||||
address: 192.168.42.20/24
|
||||
dhcp: false
|
||||
|
||||
orion: # romulus
|
||||
ansible_host: 10.42.101.120
|
||||
skylab_description: Operation Node
|
||||
skylab_targets: [cluster, datastore]
|
||||
skylab_cluster:
|
||||
address: 10.42.101.12/24
|
||||
interface: bond0
|
||||
skylab_networking:
|
||||
hostname: orion.skylab.enp.one
|
||||
dns: [10.42.101.1]
|
||||
gateway: 10.42.101.1/24
|
||||
interfaces:
|
||||
bond0:
|
||||
type: bond
|
||||
members: [eno1, eno2]
|
||||
addresses:
|
||||
- 10.42.101.12/24
|
||||
- 10.42.101.120/24
|
||||
dhcp: false
|
||||
bond0.99:
|
||||
type: vlan
|
||||
address: 192.168.42.30/24
|
||||
dhcp: false
|
||||
|
||||
en2:
|
||||
vars:
|
||||
skylab_location: DigitalOcean NYC1
|
||||
skylab_location: DigitalOcean TOR1
|
||||
|
||||
hosts:
|
||||
hubble:
|
||||
ansible_host: en2a.enp.one
|
||||
|
129
playbooks/node-down.yaml
Normal file
129
playbooks/node-down.yaml
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
- name: Check cluster state
|
||||
hosts: cluster
|
||||
any_errors_fatal: true
|
||||
tasks:
|
||||
- name: Validate user input
|
||||
run_once: true
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- node is defined
|
||||
- node in groups.cluster
|
||||
fail_msg: >-
|
||||
ERROR: Please set the 'node' variable to the cluster host to offline
|
||||
(one of: {{ groups.cluster | join(', ') }})
|
||||
|
||||
- name: Fetch node swarm ID
|
||||
ansible.builtin.command:
|
||||
cmd: !unsafe docker info --format '{{ .Swarm.NodeID}}'
|
||||
changed_when: false
|
||||
register: _docker_node_id_raw
|
||||
|
||||
- name: Fetch swarm node availability
|
||||
ansible.builtin.command:
|
||||
cmd: docker node inspect {{ _docker_node_id_raw.stdout.strip() }} --format '{{ '{{ .Spec.Availability}}' }}'
|
||||
changed_when: false
|
||||
register: _docker_node_availability_raw
|
||||
|
||||
- name: Set common facts
|
||||
ansible.builtin.set_fact:
|
||||
_target_node: "{{ node }}"
|
||||
_docker_node_id: "{{ _docker_node_id_raw.stdout.strip() }}"
|
||||
_docker_node_availability: "{{ _docker_node_availability_raw.stdout.strip() }}"
|
||||
# Use the next host in the group, unless that would exceed the length of the group,
|
||||
# in which case use the first host in the group
|
||||
_target_alt: >-
|
||||
{{ groups.cluster[
|
||||
lookup('ansible.utils.index_of', groups.cluster, 'eq', node) + 1
|
||||
if (lookup('ansible.utils.index_of', groups.cluster, 'eq', node) + 1) < (groups.cluster | length)
|
||||
else 0]
|
||||
}}
|
||||
|
||||
# I'm not sure how to do this without invoking a loop, so here we are
|
||||
- name: Set common fact for node addresses
|
||||
vars:
|
||||
_node_addresses:
|
||||
- "{{ lookup('vars', 'ansible_' + skylab_cluster.interface).ipv4.address }}"
|
||||
ansible.builtin.set_fact:
|
||||
_node_addresses: "{{ _node_addresses + [item.address] }}"
|
||||
loop: "{{ lookup('vars', 'ansible_' + skylab_cluster.interface).ipv4_secondaries }}"
|
||||
loop_control:
|
||||
label: "{{ item.address }}"
|
||||
|
||||
- name: Set facts for target node
|
||||
when: inventory_hostname == _target_node
|
||||
ansible.builtin.set_fact:
|
||||
_needs_docker_migration: "{{ (_docker_node_availability | lower != 'drain') | bool }}"
|
||||
|
||||
- name: Check cluster settings
|
||||
when: inventory_hostname != _target_node
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- skylab_cluster.address | ansible.netcommon.ipaddr('address') in _node_addresses
|
||||
- _docker_node_availability | lower == 'active'
|
||||
fail_msg: >-
|
||||
ERROR: Node '{{ inventory_hostname }}' is already marked as unavailable. All cluster
|
||||
nodes must be available before a new node can be moved to unavailable status.
|
||||
|
||||
- name: Offline node
|
||||
hosts: "{{ node }}"
|
||||
tasks:
|
||||
- name: Migrate services off target node
|
||||
when: _needs_docker_migration
|
||||
block:
|
||||
- name: Fetch current cluster service state
|
||||
ansible.builtin.command:
|
||||
cmd: !unsafe docker service ls --format '{{json .}}'
|
||||
changed_when: false
|
||||
register: _cluster_service_prestate
|
||||
|
||||
- name: Disable NAT rule {{ _skylab_adguard_nat_rule }}
|
||||
delegate_to: core
|
||||
connection: ansible.netcommon.network_cli
|
||||
community.network.edgeos_config:
|
||||
lines:
|
||||
- set service nat rule {{ _skylab_adguard_nat_rule }} disable
|
||||
|
||||
- name: Update node availability
|
||||
vars:
|
||||
ansible_python_interpreter: "{{ skylab_state_dir }}/ansible-runtime/bin/python"
|
||||
community.docker.docker_node:
|
||||
availability: drain
|
||||
hostname: "{{ _docker_node_id }}"
|
||||
register: _node_availability_status
|
||||
|
||||
- name: Wait for services to shutdown
|
||||
ansible.builtin.pause:
|
||||
seconds: 10
|
||||
|
||||
- name: Wait for services to migrate
|
||||
ansible.builtin.command:
|
||||
cmd: !unsafe docker service ls --format '{{json .}}'
|
||||
changed_when: false
|
||||
register: _cluster_service_poststate
|
||||
until: _cluster_service_poststate.stdout == _cluster_service_prestate.stdout
|
||||
retries: 120
|
||||
delay: 5
|
||||
|
||||
- name: Enable NAT rule {{ _skylab_adguard_nat_rule }}
|
||||
delegate_to: core
|
||||
connection: ansible.netcommon.network_cli
|
||||
community.network.edgeos_config:
|
||||
lines:
|
||||
- delete service nat rule {{ _skylab_adguard_nat_rule }} disable
|
||||
save: true
|
||||
|
||||
- name: Delete address from node
|
||||
become: true
|
||||
when: skylab_cluster.address | ansible.netcommon.ipaddr('address') in _node_addresses
|
||||
ansible.builtin.command:
|
||||
cmd: ip address delete {{ skylab_cluster.address | ansible.netcommon.ipaddr('host/prefix') }} dev {{ skylab_cluster.interface }}
|
||||
changed_when: true
|
||||
|
||||
- name: Assign address to alt node
|
||||
delegate_to: "{{ _target_alt }}"
|
||||
become: true
|
||||
when: skylab_cluster.address | ansible.netcommon.ipaddr('address') not in hostvars[_target_alt]._node_addresses
|
||||
ansible.builtin.command:
|
||||
cmd: ip address add {{ skylab_cluster.address | ansible.netcommon.ipaddr('host/prefix') }} dev {{ hostvars[_target_alt].skylab_cluster.interface }}
|
||||
changed_when: true
|
9
playbooks/node-poweroff.yaml
Normal file
9
playbooks/node-poweroff.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
- ansible.builtin.import_playbook: node-down.yaml
|
||||
|
||||
- name: Shutdown node
|
||||
hosts: "{{ node }}"
|
||||
tasks:
|
||||
- name: Shutdown node
|
||||
become: true
|
||||
community.general.shutdown:
|
217
poetry.lock
generated
217
poetry.lock
generated
@ -37,6 +37,14 @@ pyyaml = "*"
|
||||
rich = "*"
|
||||
"ruamel.yaml" = {version = ">=0.15.37,<1", markers = "python_version >= \"3.7\""}
|
||||
|
||||
[[package]]
|
||||
name = "appnope"
|
||||
version = "0.1.2"
|
||||
description = "Disable App Nap on macOS >= 10.9"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "21.2.0"
|
||||
@ -51,6 +59,14 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "backcall"
|
||||
version = "0.2.0"
|
||||
description = "Specifications for callback functions passed in to an API"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "backports.entry-points-selectable"
|
||||
version = "1.1.0"
|
||||
@ -166,6 +182,14 @@ sdist = ["setuptools-rust (>=0.11.4)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.1.0"
|
||||
description = "Decorators for Humans"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.2"
|
||||
@ -304,6 +328,53 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "7.28.0"
|
||||
description = "IPython: Productive Interactive Computing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
appnope = {version = "*", markers = "sys_platform == \"darwin\""}
|
||||
backcall = "*"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
decorator = "*"
|
||||
jedi = ">=0.16"
|
||||
matplotlib-inline = "*"
|
||||
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
|
||||
pickleshare = "*"
|
||||
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
|
||||
pygments = "*"
|
||||
traitlets = ">=4.2"
|
||||
|
||||
[package.extras]
|
||||
all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
|
||||
doc = ["Sphinx (>=1.3)"]
|
||||
kernel = ["ipykernel"]
|
||||
nbconvert = ["nbconvert"]
|
||||
nbformat = ["nbformat"]
|
||||
notebook = ["notebook", "ipywidgets"]
|
||||
parallel = ["ipyparallel"]
|
||||
qtconsole = ["qtconsole"]
|
||||
test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
|
||||
|
||||
[[package]]
|
||||
name = "jedi"
|
||||
version = "0.18.0"
|
||||
description = "An autocompletion tool for Python that can be used for text editors."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
parso = ">=0.8.0,<0.9.0"
|
||||
|
||||
[package.extras]
|
||||
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
|
||||
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.0.1"
|
||||
@ -388,6 +459,17 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "matplotlib-inline"
|
||||
version = "0.1.3"
|
||||
description = "Inline Matplotlib backend for Jupyter"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
traitlets = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mdformat"
|
||||
version = "0.7.9"
|
||||
@ -444,6 +526,14 @@ code_style = ["pre-commit (==2.6)"]
|
||||
rtd = ["myst-parser (==0.14.0a3)", "sphinx-book-theme (>=0.1.0,<0.2.0)"]
|
||||
testing = ["coverage", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions"]
|
||||
|
||||
[[package]]
|
||||
name = "netaddr"
|
||||
version = "0.8.0"
|
||||
description = "A network address manipulation library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.6.0"
|
||||
@ -482,6 +572,18 @@ ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"]
|
||||
gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"]
|
||||
invoke = ["invoke (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.2"
|
||||
description = "A Python Parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
|
||||
testing = ["docopt", "pytest (<6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.9.0"
|
||||
@ -490,6 +592,25 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
|
||||
[[package]]
|
||||
name = "pexpect"
|
||||
version = "4.8.0"
|
||||
description = "Pexpect allows easy control of interactive console applications."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
ptyprocess = ">=0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pickleshare"
|
||||
version = "0.7.5"
|
||||
description = "Tiny 'shelve'-like database with concurrency support"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.3.0"
|
||||
@ -550,6 +671,25 @@ python-versions = ">=3.6.1"
|
||||
"ruamel.yaml" = ">=0.15"
|
||||
toml = "*"
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.21"
|
||||
description = "Library for building powerful interactive command lines in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
version = "0.7.0"
|
||||
description = "Run a subprocess in a pseudo terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
@ -765,6 +905,17 @@ tox = ">=3.8.0,<4.0.0"
|
||||
[package.extras]
|
||||
poetry = ["poetry (>=1.0.0,<2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "traitlets"
|
||||
version = "5.1.1"
|
||||
description = "Traitlets Python configuration system"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "uc-micro-py"
|
||||
version = "1.0.1"
|
||||
@ -808,6 +959,14 @@ six = ">=1.9.0,<2"
|
||||
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
|
||||
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.5"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
version = "0.59.0"
|
||||
@ -846,7 +1005,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "efb98f8ed668c7ae299859efbb9bc422f583e9d8676022b5e4bd1c8bf410fd71"
|
||||
content-hash = "e041af953c811fd08dae466115df5dca3001e49d62c4aef4473537ff00de85d9"
|
||||
|
||||
[metadata.files]
|
||||
ansible = [
|
||||
@ -859,10 +1018,18 @@ ansible-lint = [
|
||||
{file = "ansible-lint-4.3.7.tar.gz", hash = "sha256:1012fc3f5c4c0c58eece515860f19c34c5088faa5be412eec6fae5b45bda9c4f"},
|
||||
{file = "ansible_lint-4.3.7-py2.py3-none-any.whl", hash = "sha256:300e841f690b556a08d44902d6414283dc101079b27909e3a892f1cf1d10d7ff"},
|
||||
]
|
||||
appnope = [
|
||||
{file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
|
||||
{file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
|
||||
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
|
||||
]
|
||||
backcall = [
|
||||
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
|
||||
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
|
||||
]
|
||||
"backports.entry-points-selectable" = [
|
||||
{file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"},
|
||||
{file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"},
|
||||
@ -966,6 +1133,10 @@ cryptography = [
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:cd65b60cfe004790c795cc35f272e41a3df4631e2fb6b35aa7ac6ef2859d554e"},
|
||||
{file = "cryptography-3.4.8.tar.gz", hash = "sha256:94cc5ed4ceaefcbe5bf38c8fba6a21fc1d365bb8fb826ea1688e3370b2e24a1c"},
|
||||
]
|
||||
decorator = [
|
||||
{file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"},
|
||||
{file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"},
|
||||
]
|
||||
distlib = [
|
||||
{file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"},
|
||||
{file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"},
|
||||
@ -1008,6 +1179,14 @@ importlib-metadata = [
|
||||
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
|
||||
{file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
|
||||
]
|
||||
ipython = [
|
||||
{file = "ipython-7.28.0-py3-none-any.whl", hash = "sha256:f16148f9163e1e526f1008d7c8d966d9c15600ca20d1a754287cf96d00ba6f1d"},
|
||||
{file = "ipython-7.28.0.tar.gz", hash = "sha256:2097be5c814d1b974aea57673176a924c4c8c9583890e7a5f082f547b9975b11"},
|
||||
]
|
||||
jedi = [
|
||||
{file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
|
||||
{file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
|
||||
{file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
|
||||
@ -1063,6 +1242,10 @@ markupsafe = [
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
|
||||
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
||||
]
|
||||
matplotlib-inline = [
|
||||
{file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
|
||||
{file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
|
||||
]
|
||||
mdformat = [
|
||||
{file = "mdformat-0.7.9-py3-none-any.whl", hash = "sha256:59b79575f3a2c7d6c442475c96c349ba063450911bbc95685c1d1d855ae22c5f"},
|
||||
{file = "mdformat-0.7.9.tar.gz", hash = "sha256:ff506ac4cec7f5b805692c326484c36e8297aa7214e556603a79ee8c143b76e5"},
|
||||
@ -1079,6 +1262,10 @@ mdit-py-plugins = [
|
||||
{file = "mdit-py-plugins-0.2.8.tar.gz", hash = "sha256:5991cef645502e80a5388ec4fc20885d2313d4871e8b8e320ca2de14ac0c015f"},
|
||||
{file = "mdit_py_plugins-0.2.8-py3-none-any.whl", hash = "sha256:1833bf738e038e35d89cb3a07eb0d227ed647ce7dd357579b65343740c6d249c"},
|
||||
]
|
||||
netaddr = [
|
||||
{file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"},
|
||||
{file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"},
|
||||
]
|
||||
nodeenv = [
|
||||
{file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
|
||||
{file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
|
||||
@ -1091,10 +1278,22 @@ paramiko = [
|
||||
{file = "paramiko-2.7.2-py2.py3-none-any.whl", hash = "sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898"},
|
||||
{file = "paramiko-2.7.2.tar.gz", hash = "sha256:7f36f4ba2c0d81d219f4595e35f70d56cc94f9ac40a6acdf51d6ca210ce65035"},
|
||||
]
|
||||
parso = [
|
||||
{file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"},
|
||||
{file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"},
|
||||
]
|
||||
pathspec = [
|
||||
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
|
||||
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
|
||||
]
|
||||
pexpect = [
|
||||
{file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
|
||||
{file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
|
||||
]
|
||||
pickleshare = [
|
||||
{file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
|
||||
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
|
||||
]
|
||||
platformdirs = [
|
||||
{file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"},
|
||||
{file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"},
|
||||
@ -1115,6 +1314,14 @@ pre-commit-hooks = [
|
||||
{file = "pre_commit_hooks-3.4.0-py2.py3-none-any.whl", hash = "sha256:b1d329fc712f53f56af7c4a0ac08c414a7fcfd634dbd829c3a03f39cfb9c3574"},
|
||||
{file = "pre_commit_hooks-3.4.0.tar.gz", hash = "sha256:57e377b931aceead550e4a7bdbe8065e79e371e80f593b5b6d1129e63a77154f"},
|
||||
]
|
||||
prompt-toolkit = [
|
||||
{file = "prompt_toolkit-3.0.21-py3-none-any.whl", hash = "sha256:62b3d3ea5a3ccee94dc1aac018279cf64866a76837156ebe159b981c42dd20a8"},
|
||||
{file = "prompt_toolkit-3.0.21.tar.gz", hash = "sha256:27f13ff4e4850fe8f860b77414c7880f67c6158076a7b099062cc8570f1562e5"},
|
||||
]
|
||||
ptyprocess = [
|
||||
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
||||
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
||||
@ -1274,6 +1481,10 @@ tox-poetry-installer = [
|
||||
{file = "tox-poetry-installer-0.8.1.tar.gz", hash = "sha256:e3ee6578baa7f0ff9129182cc68af9eb0bbde520f9b0460a3f6e451579832c21"},
|
||||
{file = "tox_poetry_installer-0.8.1-py3-none-any.whl", hash = "sha256:7acb70fa996d36168fd4de6f86d8e045e972844b8ad955d6768b77518af82e7f"},
|
||||
]
|
||||
traitlets = [
|
||||
{file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
|
||||
{file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
|
||||
]
|
||||
uc-micro-py = [
|
||||
{file = "uc-micro-py-1.0.1.tar.gz", hash = "sha256:b7cdf4ea79433043ddfe2c82210208f26f7962c0cfbe3bacb05ee879a7fdb596"},
|
||||
{file = "uc_micro_py-1.0.1-py3-none-any.whl", hash = "sha256:316cfb8b6862a0f1d03540f0ae6e7b033ff1fa0ddbe60c12cbe0d4cec846a69f"},
|
||||
@ -1286,6 +1497,10 @@ virtualenv = [
|
||||
{file = "virtualenv-20.7.2-py2.py3-none-any.whl", hash = "sha256:e4670891b3a03eb071748c569a87cceaefbf643c5bac46d996c5a45c34aa0f06"},
|
||||
{file = "virtualenv-20.7.2.tar.gz", hash = "sha256:9ef4e8ee4710826e98ff3075c9a4739e2cb1040de6a2a8d35db0055840dc96a0"},
|
||||
]
|
||||
wcwidth = [
|
||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||
]
|
||||
websocket-client = [
|
||||
{file = "websocket-client-0.59.0.tar.gz", hash = "sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"},
|
||||
{file = "websocket_client-0.59.0-py2.py3-none-any.whl", hash = "sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32"},
|
||||
|
@ -12,6 +12,7 @@ docker = "^4.2.0"
|
||||
docker-compose = "^1.25.4"
|
||||
paramiko = "^2.7.1"
|
||||
jsondiff = "^1.2.0"
|
||||
netaddr = "^0.8.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
ansible-lint = "^4.2.0"
|
||||
@ -23,3 +24,4 @@ tox-poetry-installer = "^0.8.1"
|
||||
yamllint = "^1.20.0"
|
||||
mdformat = "^0.7.9"
|
||||
mdformat-gfm = "^0.3.3"
|
||||
ipython = "^7.28.0"
|
||||
|
Reference in New Issue
Block a user