General tidy for module ready for release.
Refresh charm to drop release usage in ops-sunbeam. Drop surplus template fragments. Refresh unit tests. Tidy requirements.txt. Switch to black + other linters. Tidy docstrings across operator. Change-Id: I75df01d000d74878e7313fa556b16eef2ca4c9ff
This commit is contained in:
parent
0cf34f7d16
commit
fcf143bd16
@ -7,4 +7,4 @@
|
|||||||
build_type: charmcraft
|
build_type: charmcraft
|
||||||
publish_charm: true
|
publish_charm: true
|
||||||
charmcraft_channel: 2.0/stable
|
charmcraft_channel: 2.0/stable
|
||||||
publish_channel: latest/edge
|
publish_channel: 21.09/edge
|
||||||
|
39
charms/ovn-central-k8s/pyproject.toml
Normal file
39
charms/ovn-central-k8s/pyproject.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Copyright 2022 Canonical Ltd.
|
||||||
|
# See LICENSE file for licensing details.
|
||||||
|
|
||||||
|
# Testing tools configuration
|
||||||
|
[tool.coverage.run]
|
||||||
|
branch = true
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
show_missing = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
minversion = "6.0"
|
||||||
|
log_cli_level = "INFO"
|
||||||
|
|
||||||
|
# Formatting tools configuration
|
||||||
|
[tool.black]
|
||||||
|
line-length = 79
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
multi_line_output = 3
|
||||||
|
force_grid_wrap = true
|
||||||
|
|
||||||
|
# Linting tools configuration
|
||||||
|
[tool.flake8]
|
||||||
|
max-line-length = 79
|
||||||
|
max-doc-length = 99
|
||||||
|
max-complexity = 10
|
||||||
|
exclude = [".git", "__pycache__", ".tox", "build", "dist", "*.egg_info", "venv"]
|
||||||
|
select = ["E", "W", "F", "C", "N", "R", "D", "H"]
|
||||||
|
# Ignore W503, E501 because using black creates errors with this
|
||||||
|
# Ignore D107 Missing docstring in __init__
|
||||||
|
ignore = ["W503", "E501", "D107", "E402"]
|
||||||
|
per-file-ignores = []
|
||||||
|
docstring-convention = "google"
|
||||||
|
# Check for properly formatted copyright header in each file
|
||||||
|
copyright-check = "True"
|
||||||
|
copyright-author = "Canonical Ltd."
|
||||||
|
copyright-regexp = "Copyright\\s\\d{4}([-,]\\d{4})*\\s+%(author)s"
|
@ -1,30 +1,50 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright 2022 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
"""OVN Central Operator Charm.
|
"""OVN Central Operator Charm.
|
||||||
|
|
||||||
This charm provide Glance services as part of an OpenStack deployment
|
This charm provide Glance services as part of an OpenStack deployment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ovn
|
|
||||||
import ovsdb as ch_ovsdb
|
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Mapping
|
from typing import (
|
||||||
|
List,
|
||||||
import ops.charm
|
Mapping,
|
||||||
from ops.framework import StoredState
|
)
|
||||||
from ops.main import main
|
|
||||||
|
|
||||||
import ops_sunbeam.charm as sunbeam_charm
|
|
||||||
import ops_sunbeam.core as sunbeam_core
|
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
|
||||||
import ops_sunbeam.config_contexts as sunbeam_ctxts
|
|
||||||
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
|
||||||
import ops_sunbeam.ovn.config_contexts as ovn_ctxts
|
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
|
||||||
|
|
||||||
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
||||||
|
import ops.charm
|
||||||
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
|
import ops_sunbeam.config_contexts as sunbeam_ctxts
|
||||||
|
import ops_sunbeam.core as sunbeam_core
|
||||||
|
import ops_sunbeam.ovn.config_contexts as ovn_ctxts
|
||||||
|
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
||||||
|
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
||||||
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
from charms.observability_libs.v0.kubernetes_service_patch import (
|
||||||
|
KubernetesServicePatch,
|
||||||
|
)
|
||||||
|
from ops.framework import (
|
||||||
|
StoredState,
|
||||||
|
)
|
||||||
|
from ops.main import (
|
||||||
|
main,
|
||||||
|
)
|
||||||
|
|
||||||
from charms.observability_libs.v0.kubernetes_service_patch \
|
import ovn
|
||||||
import KubernetesServicePatch
|
import ovsdb as ch_ovsdb
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -35,52 +55,62 @@ OVN_DB_CONTAINERS = [OVN_SB_DB_CONTAINER, OVN_NB_DB_CONTAINER]
|
|||||||
|
|
||||||
|
|
||||||
class OVNNorthBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNNorthBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
|
"""Handler for North OVN DB."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wrapper_script(self):
|
def wrapper_script(self):
|
||||||
return '/root/ovn-northd-wrapper.sh'
|
"""Wrapper script for managing OVN service."""
|
||||||
|
return "/root/ovn-northd-wrapper.sh"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_command(self):
|
def status_command(self):
|
||||||
return '/usr/share/ovn/scripts/ovn-ctl status_northd'
|
"""Status command for container."""
|
||||||
|
return "/usr/share/ovn/scripts/ovn-ctl status_northd"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def service_description(self):
|
def service_description(self):
|
||||||
return 'OVN Northd'
|
"""Description of service."""
|
||||||
|
return "OVN Northd"
|
||||||
|
|
||||||
def default_container_configs(self):
|
def default_container_configs(self):
|
||||||
|
"""Config files for container."""
|
||||||
_cc = super().default_container_configs()
|
_cc = super().default_container_configs()
|
||||||
_cc.append(
|
_cc.append(
|
||||||
sunbeam_core.ContainerConfigFile(
|
sunbeam_core.ContainerConfigFile(
|
||||||
'/etc/ovn/ovn-northd-db-params.conf',
|
"/etc/ovn/ovn-northd-db-params.conf", "root", "root"
|
||||||
'root',
|
)
|
||||||
'root'))
|
)
|
||||||
return _cc
|
return _cc
|
||||||
|
|
||||||
|
|
||||||
class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
|
"""Handler for North-bound OVN DB."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wrapper_script(self):
|
def wrapper_script(self):
|
||||||
return '/root/ovn-nb-db-server-wrapper.sh'
|
"""Wrapper script for managing OVN service."""
|
||||||
|
return "/root/ovn-nb-db-server-wrapper.sh"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_command(self):
|
def status_command(self):
|
||||||
|
"""Status command for container."""
|
||||||
# This command always return 0 even if the DB service
|
# This command always return 0 even if the DB service
|
||||||
# is not running, so adding healthcheck with tcp check
|
# is not running, so adding healthcheck with tcp check
|
||||||
return '/usr/share/ovn/scripts/ovn-ctl status_ovsdb'
|
return "/usr/share/ovn/scripts/ovn-ctl status_ovsdb"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def service_description(self):
|
def service_description(self):
|
||||||
return 'OVN North Bound DB'
|
"""Description of service."""
|
||||||
|
return "OVN North Bound DB"
|
||||||
|
|
||||||
def default_container_configs(self):
|
def default_container_configs(self):
|
||||||
|
"""Config files for container."""
|
||||||
_cc = super().default_container_configs()
|
_cc = super().default_container_configs()
|
||||||
_cc.append(
|
_cc.append(
|
||||||
sunbeam_core.ContainerConfigFile(
|
sunbeam_core.ContainerConfigFile(
|
||||||
'/root/ovn-nb-cluster-join.sh',
|
"/root/ovn-nb-cluster-join.sh", "root", "root"
|
||||||
'root',
|
)
|
||||||
'root'))
|
)
|
||||||
return _cc
|
return _cc
|
||||||
|
|
||||||
def get_healthcheck_layer(self) -> dict:
|
def get_healthcheck_layer(self) -> dict:
|
||||||
@ -94,37 +124,40 @@ class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
"online": {
|
"online": {
|
||||||
"override": "replace",
|
"override": "replace",
|
||||||
"level": "ready",
|
"level": "ready",
|
||||||
"tcp": {
|
"tcp": {"port": 6641},
|
||||||
"port": 6641
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
|
"""Handler for South-bound OVN DB."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wrapper_script(self):
|
def wrapper_script(self):
|
||||||
return '/root/ovn-sb-db-server-wrapper.sh'
|
"""Wrapper script for managing OVN service."""
|
||||||
|
return "/root/ovn-sb-db-server-wrapper.sh"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_command(self):
|
def status_command(self):
|
||||||
|
"""Status command for container."""
|
||||||
# This command always return 0 even if the DB service
|
# This command always return 0 even if the DB service
|
||||||
# is not running, so adding healthcheck with tcp check
|
# is not running, so adding healthcheck with tcp check
|
||||||
return '/usr/share/ovn/scripts/ovn-ctl status_ovsdb'
|
return "/usr/share/ovn/scripts/ovn-ctl status_ovsdb"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def service_description(self):
|
def service_description(self):
|
||||||
return 'OVN South Bound DB'
|
"""Description of service."""
|
||||||
|
return "OVN South Bound DB"
|
||||||
|
|
||||||
def default_container_configs(self):
|
def default_container_configs(self):
|
||||||
|
"""Config files for container."""
|
||||||
_cc = super().default_container_configs()
|
_cc = super().default_container_configs()
|
||||||
_cc.append(
|
_cc.append(
|
||||||
sunbeam_core.ContainerConfigFile(
|
sunbeam_core.ContainerConfigFile(
|
||||||
'/root/ovn-sb-cluster-join.sh',
|
"/root/ovn-sb-cluster-join.sh", "root", "root"
|
||||||
'root',
|
)
|
||||||
'root'))
|
)
|
||||||
return _cc
|
return _cc
|
||||||
|
|
||||||
def get_healthcheck_layer(self) -> dict:
|
def get_healthcheck_layer(self) -> dict:
|
||||||
@ -138,9 +171,7 @@ class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
"online": {
|
"online": {
|
||||||
"override": "replace",
|
"override": "replace",
|
||||||
"level": "ready",
|
"level": "ready",
|
||||||
"tcp": {
|
"tcp": {"port": 6642},
|
||||||
"port": 6642
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,66 +181,69 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
_state = StoredState()
|
_state = StoredState()
|
||||||
mandatory_relations = {
|
mandatory_relations = {"certificates", "peers"}
|
||||||
'certificates',
|
|
||||||
'peers'
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, framework):
|
def __init__(self, framework):
|
||||||
|
"""Setup OVN central charm class."""
|
||||||
super().__init__(framework)
|
super().__init__(framework)
|
||||||
self.service_patcher = KubernetesServicePatch(
|
self.service_patcher = KubernetesServicePatch(
|
||||||
self,
|
self,
|
||||||
[
|
[
|
||||||
('northbound', 6641),
|
("northbound", 6641),
|
||||||
('southbound', 6642),
|
("southbound", 6642),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_pebble_handlers(self):
|
def get_pebble_handlers(self):
|
||||||
|
"""Pebble handlers for all OVN containers."""
|
||||||
pebble_handlers = [
|
pebble_handlers = [
|
||||||
OVNNorthBPebbleHandler(
|
OVNNorthBPebbleHandler(
|
||||||
self,
|
self,
|
||||||
OVN_NORTHD_CONTAINER,
|
OVN_NORTHD_CONTAINER,
|
||||||
'ovn-northd',
|
"ovn-northd",
|
||||||
self.container_configs,
|
self.container_configs,
|
||||||
self.template_dir,
|
self.template_dir,
|
||||||
self.openstack_release,
|
self.configure_charm,
|
||||||
self.configure_charm),
|
),
|
||||||
OVNSouthBDBPebbleHandler(
|
OVNSouthBDBPebbleHandler(
|
||||||
self,
|
self,
|
||||||
OVN_SB_DB_CONTAINER,
|
OVN_SB_DB_CONTAINER,
|
||||||
'ovn-sb-db-server',
|
"ovn-sb-db-server",
|
||||||
self.container_configs,
|
self.container_configs,
|
||||||
self.template_dir,
|
self.template_dir,
|
||||||
self.openstack_release,
|
self.configure_charm,
|
||||||
self.configure_charm),
|
),
|
||||||
OVNNorthBDBPebbleHandler(
|
OVNNorthBDBPebbleHandler(
|
||||||
self,
|
self,
|
||||||
OVN_NB_DB_CONTAINER,
|
OVN_NB_DB_CONTAINER,
|
||||||
'ovn-nb-db-server',
|
"ovn-nb-db-server",
|
||||||
self.container_configs,
|
self.container_configs,
|
||||||
self.template_dir,
|
self.template_dir,
|
||||||
self.openstack_release,
|
self.configure_charm,
|
||||||
self.configure_charm)]
|
),
|
||||||
|
]
|
||||||
return pebble_handlers
|
return pebble_handlers
|
||||||
|
|
||||||
def get_relation_handlers(self, handlers=None) -> List[
|
def get_relation_handlers(
|
||||||
sunbeam_rhandlers.RelationHandler]:
|
self, handlers=None
|
||||||
|
) -> List[sunbeam_rhandlers.RelationHandler]:
|
||||||
"""Relation handlers for the service."""
|
"""Relation handlers for the service."""
|
||||||
handlers = handlers or []
|
handlers = handlers or []
|
||||||
if self.can_add_handler('peers', handlers):
|
if self.can_add_handler("peers", handlers):
|
||||||
self.peers = ovn_rhandlers.OVNDBClusterPeerHandler(
|
self.peers = ovn_rhandlers.OVNDBClusterPeerHandler(
|
||||||
self,
|
self,
|
||||||
'peers',
|
"peers",
|
||||||
self.configure_charm,
|
self.configure_charm,
|
||||||
'peers' in self.mandatory_relations)
|
"peers" in self.mandatory_relations,
|
||||||
|
)
|
||||||
handlers.append(self.peers)
|
handlers.append(self.peers)
|
||||||
if self.can_add_handler('ovsdb-cms', handlers):
|
if self.can_add_handler("ovsdb-cms", handlers):
|
||||||
self.ovsdb_cms = ovn_rhandlers.OVSDBCMSProvidesHandler(
|
self.ovsdb_cms = ovn_rhandlers.OVSDBCMSProvidesHandler(
|
||||||
self,
|
self,
|
||||||
'ovsdb-cms',
|
"ovsdb-cms",
|
||||||
self.configure_charm,
|
self.configure_charm,
|
||||||
'ovsdb-cms' in self.mandatory_relations)
|
"ovsdb-cms" in self.mandatory_relations,
|
||||||
|
)
|
||||||
handlers.append(self.ovsdb_cms)
|
handlers.append(self.ovsdb_cms)
|
||||||
handlers = super().get_relation_handlers(handlers)
|
handlers = super().get_relation_handlers(handlers)
|
||||||
return handlers
|
return handlers
|
||||||
@ -218,8 +252,7 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
def config_contexts(self) -> List[sunbeam_ctxts.ConfigContext]:
|
def config_contexts(self) -> List[sunbeam_ctxts.ConfigContext]:
|
||||||
"""Configuration contexts for the operator."""
|
"""Configuration contexts for the operator."""
|
||||||
contexts = super().config_contexts
|
contexts = super().config_contexts
|
||||||
contexts.append(
|
contexts.append(ovn_ctxts.OVNDBConfigContext(self, "ovs_db"))
|
||||||
ovn_ctxts.OVNDBConfigContext(self, "ovs_db"))
|
|
||||||
return contexts
|
return contexts
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -232,18 +265,19 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
def ovn_rundir(self):
|
def ovn_rundir(self):
|
||||||
return '/var/run/ovn'
|
"""OVN run dir."""
|
||||||
|
return "/var/run/ovn"
|
||||||
|
|
||||||
def get_pebble_executor(self, container_name):
|
def get_pebble_executor(self, container_name):
|
||||||
container = self.unit.get_container(
|
"""Execute command in pebble."""
|
||||||
container_name)
|
container = self.unit.get_container(container_name)
|
||||||
|
|
||||||
def _run_via_pebble(*args):
|
def _run_via_pebble(*args):
|
||||||
process = container.exec(list(args), timeout=5*60)
|
process = container.exec(list(args), timeout=5 * 60)
|
||||||
out, warnings = process.wait_output()
|
out, warnings = process.wait_output()
|
||||||
if warnings:
|
if warnings:
|
||||||
for line in warnings.splitlines():
|
for line in warnings.splitlines():
|
||||||
logger.warning('CMD Out: %s', line.strip())
|
logger.warning("CMD Out: %s", line.strip())
|
||||||
return out
|
return out
|
||||||
|
|
||||||
return _run_via_pebble
|
return _run_via_pebble
|
||||||
@ -261,12 +295,13 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
# is clustered and while units are paused, so we need to handle
|
# is clustered and while units are paused, so we need to handle
|
||||||
# errors from this call gracefully.
|
# errors from this call gracefully.
|
||||||
return ovn.cluster_status(
|
return ovn.cluster_status(
|
||||||
db,
|
db, rundir=self.ovn_rundir(), cmd_executor=cmd_executor
|
||||||
rundir=self.ovn_rundir(),
|
)
|
||||||
cmd_executor=cmd_executor)
|
|
||||||
except (ValueError) as e:
|
except (ValueError) as e:
|
||||||
logging.error('Unable to get cluster status, ovsdb-server '
|
logging.error(
|
||||||
'not ready yet?: {}'.format(e))
|
"Unable to get cluster status, ovsdb-server "
|
||||||
|
"not ready yet?: {}".format(e)
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def configure_ovn_listener(self, db, port_map):
|
def configure_ovn_listener(self, db, port_map):
|
||||||
@ -278,65 +313,79 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
:type port_map: Dict[int,Dict[str,str]]
|
:type port_map: Dict[int,Dict[str,str]]
|
||||||
:raises: ValueError
|
:raises: ValueError
|
||||||
"""
|
"""
|
||||||
if db == 'nb':
|
if db == "nb":
|
||||||
executor = self.get_pebble_executor(OVN_NB_DB_CONTAINER)
|
executor = self.get_pebble_executor(OVN_NB_DB_CONTAINER)
|
||||||
elif db == 'sb':
|
elif db == "sb":
|
||||||
executor = self.get_pebble_executor(OVN_SB_DB_CONTAINER)
|
executor = self.get_pebble_executor(OVN_SB_DB_CONTAINER)
|
||||||
status = self.cluster_status(
|
status = self.cluster_status(
|
||||||
'ovn{}_db'.format(db),
|
"ovn{}_db".format(db), cmd_executor=executor
|
||||||
cmd_executor=executor)
|
)
|
||||||
if status and status.is_cluster_leader:
|
if status and status.is_cluster_leader:
|
||||||
logging.debug(
|
logging.debug(
|
||||||
'configure_ovn_listener is_cluster_leader {}'.format(db))
|
"configure_ovn_listener is_cluster_leader {}".format(db)
|
||||||
|
)
|
||||||
connections = ch_ovsdb.SimpleOVSDB(
|
connections = ch_ovsdb.SimpleOVSDB(
|
||||||
'ovn-{}ctl'.format(db),
|
"ovn-{}ctl".format(db), cmd_executor=executor
|
||||||
cmd_executor=executor).connection
|
).connection
|
||||||
for port, settings in port_map.items():
|
for port, settings in port_map.items():
|
||||||
logging.debug('port {} {}'.format(port, settings))
|
logging.debug("port {} {}".format(port, settings))
|
||||||
# discover and create any non-existing listeners first
|
# discover and create any non-existing listeners first
|
||||||
for connection in connections.find(
|
for connection in connections.find(
|
||||||
'target="pssl:{}"'.format(port)):
|
'target="pssl:{}"'.format(port)
|
||||||
logging.debug('Found port {}'.format(port))
|
):
|
||||||
|
logging.debug("Found port {}".format(port))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logging.debug('Create port {}'.format(port))
|
logging.debug("Create port {}".format(port))
|
||||||
executor(
|
executor(
|
||||||
'ovn-{}ctl'.format(db),
|
"ovn-{}ctl".format(db),
|
||||||
'--',
|
"--",
|
||||||
'--id=@connection',
|
"--id=@connection",
|
||||||
'create', 'connection',
|
"create",
|
||||||
|
"connection",
|
||||||
'target="pssl:{}"'.format(port),
|
'target="pssl:{}"'.format(port),
|
||||||
'--',
|
"--",
|
||||||
'add', '{}_Global'.format(db.upper()),
|
"add",
|
||||||
'.', 'connections', '@connection')
|
"{}_Global".format(db.upper()),
|
||||||
|
".",
|
||||||
|
"connections",
|
||||||
|
"@connection",
|
||||||
|
)
|
||||||
# set/update connection settings
|
# set/update connection settings
|
||||||
for connection in connections.find(
|
for connection in connections.find(
|
||||||
'target="pssl:{}"'.format(port)):
|
'target="pssl:{}"'.format(port)
|
||||||
|
):
|
||||||
for k, v in settings.items():
|
for k, v in settings.items():
|
||||||
logging.debug(
|
logging.debug(
|
||||||
'set {} {} {}'
|
"set {} {} {}".format(
|
||||||
.format(str(connection['_uuid']), k, v))
|
str(connection["_uuid"]), k, v
|
||||||
connections.set(str(connection['_uuid']), k, v)
|
)
|
||||||
|
)
|
||||||
|
connections.set(str(connection["_uuid"]), k, v)
|
||||||
|
|
||||||
def configure_charm(self, event: ops.framework.EventBase) -> None:
|
# Refactor this method to simplify it.
|
||||||
"""Catchall handler to configure charm services.
|
def configure_charm( # noqa: C901
|
||||||
|
self, event: ops.framework.EventBase
|
||||||
"""
|
) -> None:
|
||||||
|
"""Catchall handler to configure charm services."""
|
||||||
if not self.unit.is_leader():
|
if not self.unit.is_leader():
|
||||||
if not self.is_leader_ready():
|
if not self.is_leader_ready():
|
||||||
self.unit.status = ops.model.WaitingStatus(
|
self.unit.status = ops.model.WaitingStatus(
|
||||||
"Waiting for leader to be ready")
|
"Waiting for leader to be ready"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
missing_leader_data = [
|
missing_leader_data = [
|
||||||
k for k in ['nb_cid', 'sb_cid']
|
k for k in ["nb_cid", "sb_cid"] if not self.leader_get(k)
|
||||||
if not self.leader_get(k)]
|
]
|
||||||
if missing_leader_data:
|
if missing_leader_data:
|
||||||
logging.debug(f"missing {missing_leader_data} from leader")
|
logging.debug(f"missing {missing_leader_data} from leader")
|
||||||
self.unit.status = ops.model.WaitingStatus(
|
self.unit.status = ops.model.WaitingStatus(
|
||||||
"Waiting for data from leader")
|
"Waiting for data from leader"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"Remote leader is ready and has supplied all data needed")
|
"Remote leader is ready and has supplied all data needed"
|
||||||
|
)
|
||||||
|
|
||||||
if not self.relation_handlers_ready():
|
if not self.relation_handlers_ready():
|
||||||
logging.debug("Aborting charm relations not ready")
|
logging.debug("Aborting charm relations not ready")
|
||||||
@ -344,7 +393,8 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
|
|
||||||
if not all([ph.pebble_ready for ph in self.pebble_handlers]):
|
if not all([ph.pebble_ready for ph in self.pebble_handlers]):
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"Aborting configuration, not all pebble handlers are ready")
|
"Aborting configuration, not all pebble handlers are ready"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Render Config in all containers but init should *NOT* start
|
# Render Config in all containers but init should *NOT* start
|
||||||
@ -356,7 +406,8 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
else:
|
else:
|
||||||
logging.debug(
|
logging.debug(
|
||||||
f"Not running init for {ph.service_name},"
|
f"Not running init for {ph.service_name},"
|
||||||
" container not ready")
|
" container not ready"
|
||||||
|
)
|
||||||
|
|
||||||
if self.unit.is_leader():
|
if self.unit.is_leader():
|
||||||
# Start services in North/South containers on lead unit
|
# Start services in North/South containers on lead unit
|
||||||
@ -366,35 +417,39 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
# Attempt to setup listers etc
|
# Attempt to setup listers etc
|
||||||
self.configure_ovn()
|
self.configure_ovn()
|
||||||
nb_status = self.cluster_status(
|
nb_status = self.cluster_status(
|
||||||
'ovnnb_db',
|
"ovnnb_db", self.get_pebble_executor(OVN_NB_DB_CONTAINER)
|
||||||
self.get_pebble_executor(OVN_NB_DB_CONTAINER))
|
)
|
||||||
sb_status = self.cluster_status(
|
sb_status = self.cluster_status(
|
||||||
'ovnsb_db',
|
"ovnsb_db", self.get_pebble_executor(OVN_SB_DB_CONTAINER)
|
||||||
self.get_pebble_executor(OVN_SB_DB_CONTAINER))
|
)
|
||||||
logging.debug("Telling peers leader is ready and cluster ids")
|
logging.debug("Telling peers leader is ready and cluster ids")
|
||||||
self.set_leader_ready()
|
self.set_leader_ready()
|
||||||
self.leader_set({
|
self.leader_set(
|
||||||
'nb_cid': str(nb_status.cluster_id),
|
{
|
||||||
'sb_cid': str(sb_status.cluster_id),
|
"nb_cid": str(nb_status.cluster_id),
|
||||||
})
|
"sb_cid": str(sb_status.cluster_id),
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.debug("Attempting to join OVN_Northbound cluster")
|
logging.debug("Attempting to join OVN_Northbound cluster")
|
||||||
container = self.unit.get_container(OVN_NB_DB_CONTAINER)
|
container = self.unit.get_container(OVN_NB_DB_CONTAINER)
|
||||||
process = container.exec(
|
process = container.exec(
|
||||||
['bash', '/root/ovn-nb-cluster-join.sh'], timeout=5*60)
|
["bash", "/root/ovn-nb-cluster-join.sh"], timeout=5 * 60
|
||||||
|
)
|
||||||
out, warnings = process.wait_output()
|
out, warnings = process.wait_output()
|
||||||
if warnings:
|
if warnings:
|
||||||
for line in warnings.splitlines():
|
for line in warnings.splitlines():
|
||||||
logger.warning('CMD Out: %s', line.strip())
|
logger.warning("CMD Out: %s", line.strip())
|
||||||
|
|
||||||
logging.debug("Attempting to join OVN_Southbound cluster")
|
logging.debug("Attempting to join OVN_Southbound cluster")
|
||||||
container = self.unit.get_container(OVN_SB_DB_CONTAINER)
|
container = self.unit.get_container(OVN_SB_DB_CONTAINER)
|
||||||
process = container.exec(
|
process = container.exec(
|
||||||
['bash', '/root/ovn-sb-cluster-join.sh'], timeout=5*60)
|
["bash", "/root/ovn-sb-cluster-join.sh"], timeout=5 * 60
|
||||||
|
)
|
||||||
out, warnings = process.wait_output()
|
out, warnings = process.wait_output()
|
||||||
if warnings:
|
if warnings:
|
||||||
for line in warnings.splitlines():
|
for line in warnings.splitlines():
|
||||||
logger.warning('CMD Out: %s', line.strip())
|
logger.warning("CMD Out: %s", line.strip())
|
||||||
logging.debug("Starting services in DB containers")
|
logging.debug("Starting services in DB containers")
|
||||||
for ph in self.get_named_pebble_handlers(OVN_DB_CONTAINERS):
|
for ph in self.get_named_pebble_handlers(OVN_DB_CONTAINERS):
|
||||||
ph.start_service()
|
ph.start_service()
|
||||||
@ -414,34 +469,37 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
self.unit.status = ops.model.ActiveStatus()
|
self.unit.status = ops.model.ActiveStatus()
|
||||||
|
|
||||||
def configure_ovn(self):
|
def configure_ovn(self):
|
||||||
inactivity_probe = int(
|
"""Configure ovn listener."""
|
||||||
self.config['ovsdb-server-inactivity-probe']) * 1000
|
inactivity_probe = (
|
||||||
|
int(self.config["ovsdb-server-inactivity-probe"]) * 1000
|
||||||
|
)
|
||||||
self.configure_ovn_listener(
|
self.configure_ovn_listener(
|
||||||
'nb', {
|
"nb",
|
||||||
|
{
|
||||||
self.ovsdb_cms.db_nb_port: {
|
self.ovsdb_cms.db_nb_port: {
|
||||||
'inactivity_probe': inactivity_probe,
|
"inactivity_probe": inactivity_probe,
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
self.configure_ovn_listener(
|
self.configure_ovn_listener(
|
||||||
'sb', {
|
"sb",
|
||||||
|
{
|
||||||
self.ovsdb_cms.db_sb_port: {
|
self.ovsdb_cms.db_sb_port: {
|
||||||
'inactivity_probe': inactivity_probe,
|
"inactivity_probe": inactivity_probe,
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
self.configure_ovn_listener(
|
self.configure_ovn_listener(
|
||||||
'sb', {
|
"sb",
|
||||||
|
{
|
||||||
self.ovsdb_cms.db_sb_admin_port: {
|
self.ovsdb_cms.db_sb_admin_port: {
|
||||||
'inactivity_probe': inactivity_probe,
|
"inactivity_probe": inactivity_probe,
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
class OVNCentralXenaOperatorCharm(OVNCentralOperatorCharm):
|
|
||||||
|
|
||||||
openstack_release = 'xena'
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Note: use_juju_for_storage=True required per
|
# Note: use_juju_for_storage=True required per
|
||||||
# https://github.com/canonical/operator/issues/506
|
# https://github.com/canonical/operator/issues/506
|
||||||
main(OVNCentralXenaOperatorCharm, use_juju_for_storage=True)
|
main(OVNCentralOperatorCharm, use_juju_for_storage=True)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2019 Canonical Ltd
|
# Copyright 2022 Canonical Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -11,19 +11,22 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""OVN utilities."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
OVN_RUNDIR = "/var/run/ovn"
|
||||||
OVN_RUNDIR = '/var/run/ovn'
|
OVN_SYSCONFDIR = "/etc/ovn"
|
||||||
OVN_SYSCONFDIR = '/etc/ovn'
|
|
||||||
|
|
||||||
|
|
||||||
def ovn_appctl(target, args, rundir=None, use_ovs_appctl=False,
|
def ovn_appctl(
|
||||||
cmd_executor=None):
|
target, args, rundir=None, use_ovs_appctl=False, cmd_executor=None
|
||||||
|
):
|
||||||
"""Run ovn/ovs-appctl for target with args and return output.
|
"""Run ovn/ovs-appctl for target with args and return output.
|
||||||
|
|
||||||
:param target: Name of daemon to contact. Unless target begins with '/',
|
:param target: Name of daemon to contact. Unless target begins with '/',
|
||||||
@ -44,25 +47,43 @@ def ovn_appctl(target, args, rundir=None, use_ovs_appctl=False,
|
|||||||
# NOTE(fnordahl): The ovsdb-server processes for the OVN databases use a
|
# NOTE(fnordahl): The ovsdb-server processes for the OVN databases use a
|
||||||
# non-standard naming scheme for their daemon control socket and we need
|
# non-standard naming scheme for their daemon control socket and we need
|
||||||
# to pass the full path to the socket.
|
# to pass the full path to the socket.
|
||||||
if target in ('ovnnb_db', 'ovnsb_db',):
|
if target in (
|
||||||
target = os.path.join(rundir or OVN_RUNDIR, target + '.ctl')
|
"ovnnb_db",
|
||||||
|
"ovnsb_db",
|
||||||
|
):
|
||||||
|
target = os.path.join(rundir or OVN_RUNDIR, target + ".ctl")
|
||||||
|
|
||||||
if use_ovs_appctl:
|
if use_ovs_appctl:
|
||||||
tool = 'ovs-appctl'
|
tool = "ovs-appctl"
|
||||||
else:
|
else:
|
||||||
tool = 'ovn-appctl'
|
tool = "ovn-appctl"
|
||||||
|
|
||||||
if not cmd_executor:
|
if not cmd_executor:
|
||||||
cmd_executor = utils._run
|
cmd_executor = utils._run
|
||||||
return cmd_executor(tool, '-t', target, *args)
|
return cmd_executor(tool, "-t", target, *args)
|
||||||
|
|
||||||
|
|
||||||
class OVNClusterStatus(object):
|
class OVNClusterStatus(object):
|
||||||
|
"""Class for examining cluster status."""
|
||||||
|
|
||||||
def __init__(self, name, cluster_id, server_id, address, status, role,
|
def __init__(
|
||||||
term, leader, vote, election_timer, log,
|
self,
|
||||||
entries_not_yet_committed, entries_not_yet_applied,
|
name,
|
||||||
connections, servers):
|
cluster_id,
|
||||||
|
server_id,
|
||||||
|
address,
|
||||||
|
status,
|
||||||
|
role,
|
||||||
|
term,
|
||||||
|
leader,
|
||||||
|
vote,
|
||||||
|
election_timer,
|
||||||
|
log,
|
||||||
|
entries_not_yet_committed,
|
||||||
|
entries_not_yet_applied,
|
||||||
|
connections,
|
||||||
|
servers,
|
||||||
|
):
|
||||||
"""Initialize and populate OVNClusterStatus object.
|
"""Initialize and populate OVNClusterStatus object.
|
||||||
|
|
||||||
Use class initializer so we can define types in a compatible manner.
|
Use class initializer so we can define types in a compatible manner.
|
||||||
@ -116,23 +137,25 @@ class OVNClusterStatus(object):
|
|||||||
self.servers = servers
|
self.servers = servers
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
"""Whether statuses are equal."""
|
||||||
return (
|
return (
|
||||||
self.name == other.name and
|
self.name == other.name
|
||||||
self.cluster_id == other.cluster_id and
|
and self.cluster_id == other.cluster_id
|
||||||
self.server_id == other.server_id and
|
and self.server_id == other.server_id
|
||||||
self.address == other.address and
|
and self.address == other.address
|
||||||
self.status == other.status and
|
and self.status == other.status
|
||||||
self.role == other.role and
|
and self.role == other.role
|
||||||
self.term == other.term and
|
and self.term == other.term
|
||||||
self.leader == other.leader and
|
and self.leader == other.leader
|
||||||
self.vote == other.vote and
|
and self.vote == other.vote
|
||||||
self.election_timer == other.election_timer and
|
and self.election_timer == other.election_timer
|
||||||
self.log == other.log and
|
and self.log == other.log
|
||||||
self.entries_not_yet_committed ==
|
and self.entries_not_yet_committed
|
||||||
other.entries_not_yet_committed and
|
== other.entries_not_yet_committed
|
||||||
self.entries_not_yet_applied == other.entries_not_yet_applied and
|
and self.entries_not_yet_applied == other.entries_not_yet_applied
|
||||||
self.connections == other.connections and
|
and self.connections == other.connections
|
||||||
self.servers == other.servers)
|
and self.servers == other.servers
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_cluster_leader(self):
|
def is_cluster_leader(self):
|
||||||
@ -141,11 +164,12 @@ class OVNClusterStatus(object):
|
|||||||
:returns: Whether target is cluster leader
|
:returns: Whether target is cluster leader
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
return self.leader == 'self'
|
return self.leader == "self"
|
||||||
|
|
||||||
|
|
||||||
def cluster_status(target, schema=None, use_ovs_appctl=False, rundir=None,
|
def cluster_status(
|
||||||
cmd_executor=None):
|
target, schema=None, use_ovs_appctl=False, rundir=None, cmd_executor=None
|
||||||
|
):
|
||||||
"""Retrieve status information from clustered OVSDB.
|
"""Retrieve status information from clustered OVSDB.
|
||||||
|
|
||||||
:param target: Usually one of 'ovsdb-server', 'ovnnb_db', 'ovnsb_db', can
|
:param target: Usually one of 'ovsdb-server', 'ovnnb_db', 'ovnsb_db', can
|
||||||
@ -163,38 +187,44 @@ def cluster_status(target, schema=None, use_ovs_appctl=False, rundir=None,
|
|||||||
:raises: subprocess.CalledProcessError, KeyError, RuntimeError
|
:raises: subprocess.CalledProcessError, KeyError, RuntimeError
|
||||||
"""
|
"""
|
||||||
schema_map = {
|
schema_map = {
|
||||||
'ovnnb_db': 'OVN_Northbound',
|
"ovnnb_db": "OVN_Northbound",
|
||||||
'ovnsb_db': 'OVN_Southbound',
|
"ovnsb_db": "OVN_Southbound",
|
||||||
}
|
}
|
||||||
if schema and schema not in schema_map.keys():
|
if schema and schema not in schema_map.keys():
|
||||||
raise RuntimeError('Unknown schema provided: "{}"'.format(schema))
|
raise RuntimeError('Unknown schema provided: "{}"'.format(schema))
|
||||||
|
|
||||||
status = {}
|
status = {}
|
||||||
k = ''
|
k = ""
|
||||||
for line in ovn_appctl(target,
|
for line in ovn_appctl(
|
||||||
('cluster/status', schema or schema_map[target]),
|
target,
|
||||||
rundir=rundir,
|
("cluster/status", schema or schema_map[target]),
|
||||||
use_ovs_appctl=use_ovs_appctl,
|
rundir=rundir,
|
||||||
cmd_executor=cmd_executor).splitlines():
|
use_ovs_appctl=use_ovs_appctl,
|
||||||
if k and line.startswith(' '):
|
cmd_executor=cmd_executor,
|
||||||
|
).splitlines():
|
||||||
|
if k and line.startswith(" "):
|
||||||
# there is no key which means this is a instance of a multi-line/
|
# there is no key which means this is a instance of a multi-line/
|
||||||
# multi-value item, populate the List which is already stored under
|
# multi-value item, populate the List which is already stored under
|
||||||
# the key.
|
# the key.
|
||||||
if k == 'servers':
|
if k == "servers":
|
||||||
status[k].append(
|
status[k].append(
|
||||||
tuple(line.replace(')', '').lstrip().split()[0:4:3]))
|
tuple(line.replace(")", "").lstrip().split()[0:4:3])
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
status[k].append(line.lstrip())
|
status[k].append(line.lstrip())
|
||||||
elif ':' in line:
|
elif ":" in line:
|
||||||
# this is a line with a key
|
# this is a line with a key
|
||||||
k, v = line.split(':', 1)
|
k, v = line.split(":", 1)
|
||||||
k = k.lower()
|
k = k.lower()
|
||||||
k = k.replace(' ', '_')
|
k = k.replace(" ", "_")
|
||||||
if v:
|
if v:
|
||||||
# this is a line with both key and value
|
# this is a line with both key and value
|
||||||
if k in ('cluster_id', 'server_id',):
|
if k in (
|
||||||
v = v.replace('(', '')
|
"cluster_id",
|
||||||
v = v.replace(')', '')
|
"server_id",
|
||||||
|
):
|
||||||
|
v = v.replace("(", "")
|
||||||
|
v = v.replace(")", "")
|
||||||
status[k] = tuple(v.split())
|
status[k] = tuple(v.split())
|
||||||
else:
|
else:
|
||||||
status[k] = v.lstrip()
|
status[k] = v.lstrip()
|
||||||
@ -204,21 +234,22 @@ def cluster_status(target, schema=None, use_ovs_appctl=False, rundir=None,
|
|||||||
# populated on subsequent iterations.
|
# populated on subsequent iterations.
|
||||||
status[k] = []
|
status[k] = []
|
||||||
return OVNClusterStatus(
|
return OVNClusterStatus(
|
||||||
status['name'],
|
status["name"],
|
||||||
uuid.UUID(status['cluster_id'][1]),
|
uuid.UUID(status["cluster_id"][1]),
|
||||||
uuid.UUID(status['server_id'][1]),
|
uuid.UUID(status["server_id"][1]),
|
||||||
status['address'],
|
status["address"],
|
||||||
status['status'],
|
status["status"],
|
||||||
status['role'],
|
status["role"],
|
||||||
int(status['term']),
|
int(status["term"]),
|
||||||
status['leader'],
|
status["leader"],
|
||||||
status['vote'],
|
status["vote"],
|
||||||
int(status['election_timer']),
|
int(status["election_timer"]),
|
||||||
status['log'],
|
status["log"],
|
||||||
int(status['entries_not_yet_committed']),
|
int(status["entries_not_yet_committed"]),
|
||||||
int(status['entries_not_yet_applied']),
|
int(status["entries_not_yet_applied"]),
|
||||||
status['connections'],
|
status["connections"],
|
||||||
status['servers'])
|
status["servers"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_northd_active(cmd_executor=None):
|
def is_northd_active(cmd_executor=None):
|
||||||
@ -231,9 +262,10 @@ def is_northd_active(cmd_executor=None):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
for line in ovn_appctl('ovn-northd', ('status',),
|
for line in ovn_appctl(
|
||||||
cmd_executor=cmd_executor).splitlines():
|
"ovn-northd", ("status",), cmd_executor=cmd_executor
|
||||||
if line.startswith('Status:') and 'active' in line:
|
).splitlines():
|
||||||
|
if line.startswith("Status:") and "active" in line:
|
||||||
return True
|
return True
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass
|
pass
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2019 Canonical Ltd
|
# Copyright 2022 Canonical Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -11,6 +11,9 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Interface for interacting with OVSDB."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -49,85 +52,85 @@ class SimpleOVSDB(object):
|
|||||||
# that will most likely be lower then the cost of finding the needle in
|
# that will most likely be lower then the cost of finding the needle in
|
||||||
# the haystack whenever downstream code misspells something.
|
# the haystack whenever downstream code misspells something.
|
||||||
_tool_table_map = {
|
_tool_table_map = {
|
||||||
'ovs-vsctl': (
|
"ovs-vsctl": (
|
||||||
'autoattach',
|
"autoattach",
|
||||||
'bridge',
|
"bridge",
|
||||||
'ct_timeout_policy',
|
"ct_timeout_policy",
|
||||||
'ct_zone',
|
"ct_zone",
|
||||||
'controller',
|
"controller",
|
||||||
'datapath',
|
"datapath",
|
||||||
'flow_sample_collector_set',
|
"flow_sample_collector_set",
|
||||||
'flow_table',
|
"flow_table",
|
||||||
'ipfix',
|
"ipfix",
|
||||||
'interface',
|
"interface",
|
||||||
'manager',
|
"manager",
|
||||||
'mirror',
|
"mirror",
|
||||||
'netflow',
|
"netflow",
|
||||||
'open_vswitch',
|
"open_vswitch",
|
||||||
'port',
|
"port",
|
||||||
'qos',
|
"qos",
|
||||||
'queue',
|
"queue",
|
||||||
'ssl',
|
"ssl",
|
||||||
'sflow',
|
"sflow",
|
||||||
),
|
),
|
||||||
'ovn-nbctl': (
|
"ovn-nbctl": (
|
||||||
'acl',
|
"acl",
|
||||||
'address_set',
|
"address_set",
|
||||||
'connection',
|
"connection",
|
||||||
'dhcp_options',
|
"dhcp_options",
|
||||||
'dns',
|
"dns",
|
||||||
'forwarding_group',
|
"forwarding_group",
|
||||||
'gateway_chassis',
|
"gateway_chassis",
|
||||||
'ha_chassis',
|
"ha_chassis",
|
||||||
'ha_chassis_group',
|
"ha_chassis_group",
|
||||||
'load_balancer',
|
"load_balancer",
|
||||||
'load_balancer_health_check',
|
"load_balancer_health_check",
|
||||||
'logical_router',
|
"logical_router",
|
||||||
'logical_router_policy',
|
"logical_router_policy",
|
||||||
'logical_router_port',
|
"logical_router_port",
|
||||||
'logical_router_static_route',
|
"logical_router_static_route",
|
||||||
'logical_switch',
|
"logical_switch",
|
||||||
'logical_switch_port',
|
"logical_switch_port",
|
||||||
'meter',
|
"meter",
|
||||||
'meter_band',
|
"meter_band",
|
||||||
'nat',
|
"nat",
|
||||||
'nb_global',
|
"nb_global",
|
||||||
'port_group',
|
"port_group",
|
||||||
'qos',
|
"qos",
|
||||||
'ssl',
|
"ssl",
|
||||||
),
|
),
|
||||||
'ovn-sbctl': (
|
"ovn-sbctl": (
|
||||||
'address_set',
|
"address_set",
|
||||||
'chassis',
|
"chassis",
|
||||||
'connection',
|
"connection",
|
||||||
'controller_event',
|
"controller_event",
|
||||||
'dhcp_options',
|
"dhcp_options",
|
||||||
'dhcpv6_options',
|
"dhcpv6_options",
|
||||||
'dns',
|
"dns",
|
||||||
'datapath_binding',
|
"datapath_binding",
|
||||||
'encap',
|
"encap",
|
||||||
'gateway_chassis',
|
"gateway_chassis",
|
||||||
'ha_chassis',
|
"ha_chassis",
|
||||||
'ha_chassis_group',
|
"ha_chassis_group",
|
||||||
'igmp_group',
|
"igmp_group",
|
||||||
'ip_multicast',
|
"ip_multicast",
|
||||||
'logical_flow',
|
"logical_flow",
|
||||||
'mac_binding',
|
"mac_binding",
|
||||||
'meter',
|
"meter",
|
||||||
'meter_band',
|
"meter_band",
|
||||||
'multicast_group',
|
"multicast_group",
|
||||||
'port_binding',
|
"port_binding",
|
||||||
'port_group',
|
"port_group",
|
||||||
'rbac_permission',
|
"rbac_permission",
|
||||||
'rbac_role',
|
"rbac_role",
|
||||||
'sb_global',
|
"sb_global",
|
||||||
'ssl',
|
"ssl",
|
||||||
'service_monitor',
|
"service_monitor",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, tool, args=None, cmd_executor=None):
|
def __init__(self, tool, args=None, cmd_executor=None):
|
||||||
"""SimpleOVSDB constructor.
|
"""The SimpleOVSDB constructor.
|
||||||
|
|
||||||
:param tool: Which tool with database commands to operate on.
|
:param tool: Which tool with database commands to operate on.
|
||||||
Usually one of `ovs-vsctl`, `ovn-nbctl`, `ovn-sbctl`
|
Usually one of `ovs-vsctl`, `ovn-nbctl`, `ovn-sbctl`
|
||||||
@ -137,18 +140,23 @@ class SimpleOVSDB(object):
|
|||||||
"""
|
"""
|
||||||
if tool not in self._tool_table_map:
|
if tool not in self._tool_table_map:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'tool must be one of "{}"'.format(self._tool_table_map.keys()))
|
'tool must be one of "{}"'.format(self._tool_table_map.keys())
|
||||||
|
)
|
||||||
self._tool = tool
|
self._tool = tool
|
||||||
self._args = args
|
self._args = args
|
||||||
self.cmd_executor = cmd_executor or utils._run
|
self.cmd_executor = cmd_executor or utils._run
|
||||||
|
|
||||||
def __getattr__(self, table):
|
def __getattr__(self, table):
|
||||||
|
"""Get table for tool."""
|
||||||
if table not in self._tool_table_map[self._tool]:
|
if table not in self._tool_table_map[self._tool]:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
'table "{}" not known for use with "{}"'
|
'table "{}" not known for use with "{}"'.format(
|
||||||
.format(table, self._tool))
|
table, self._tool
|
||||||
|
)
|
||||||
|
)
|
||||||
return self.Table(
|
return self.Table(
|
||||||
self._tool, table, args=self._args, cmd_executor=self.cmd_executor)
|
self._tool, table, args=self._args, cmd_executor=self.cmd_executor
|
||||||
|
)
|
||||||
|
|
||||||
class Table(object):
|
class Table(object):
|
||||||
"""Methods to interact with contents of OVSDB tables.
|
"""Methods to interact with contents of OVSDB tables.
|
||||||
@ -159,7 +167,7 @@ class SimpleOVSDB(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tool, table, args=None, cmd_executor=None):
|
def __init__(self, tool, table, args=None, cmd_executor=None):
|
||||||
"""SimpleOVSDBTable constructor.
|
"""Run SimpleOVSDBTable constructor.
|
||||||
|
|
||||||
:param table: Which table to operate on
|
:param table: Which table to operate on
|
||||||
:type table: str
|
:type table: str
|
||||||
@ -184,15 +192,17 @@ class SimpleOVSDB(object):
|
|||||||
# notation may occur that require further deserializing.
|
# notation may occur that require further deserializing.
|
||||||
# Reference: https://tools.ietf.org/html/rfc7047#section-5.1
|
# Reference: https://tools.ietf.org/html/rfc7047#section-5.1
|
||||||
ovs_type_cb_map = {
|
ovs_type_cb_map = {
|
||||||
'uuid': uuid.UUID,
|
"uuid": uuid.UUID,
|
||||||
# NOTE: OVSDB sets have overloaded type
|
# NOTE: OVSDB sets have overloaded type
|
||||||
# see special handling below
|
# see special handling below
|
||||||
'set': list,
|
"set": list,
|
||||||
'map': dict,
|
"map": dict,
|
||||||
}
|
}
|
||||||
assert len(data) > 1, ('Invalid data provided, expecting list '
|
assert len(data) > 1, (
|
||||||
'with at least two elements.')
|
"Invalid data provided, expecting list "
|
||||||
if data[0] == 'set':
|
"with at least two elements."
|
||||||
|
)
|
||||||
|
if data[0] == "set":
|
||||||
# special handling for set
|
# special handling for set
|
||||||
#
|
#
|
||||||
# it is either a list of strings or a list of typed lists.
|
# it is either a list of strings or a list of typed lists.
|
||||||
@ -203,8 +213,7 @@ class SimpleOVSDB(object):
|
|||||||
# We could potentially just handle this generally based on
|
# We could potentially just handle this generally based on
|
||||||
# the types listed in `ovs_type_cb_map` but let's open for
|
# the types listed in `ovs_type_cb_map` but let's open for
|
||||||
# that as soon as we have a concrete example to validate on
|
# that as soon as we have a concrete example to validate on
|
||||||
if isinstance(
|
if isinstance(el, list) and len(el) and el[0] == "uuid":
|
||||||
el, list) and len(el) and el[0] == 'uuid':
|
|
||||||
decoded_set = []
|
decoded_set = []
|
||||||
for el in data[1]:
|
for el in data[1]:
|
||||||
decoded_set.append(self._deserialize_ovsdb(el))
|
decoded_set.append(self._deserialize_ovsdb(el))
|
||||||
@ -227,33 +236,40 @@ class SimpleOVSDB(object):
|
|||||||
cmd = [self._tool]
|
cmd = [self._tool]
|
||||||
if self._args:
|
if self._args:
|
||||||
cmd.extend(self._args)
|
cmd.extend(self._args)
|
||||||
cmd.extend(['-f', 'json', 'find', self._table])
|
cmd.extend(["-f", "json", "find", self._table])
|
||||||
if condition:
|
if condition:
|
||||||
cmd.append(condition)
|
cmd.append(condition)
|
||||||
output = self.cmd_executor(*cmd)
|
output = self.cmd_executor(*cmd)
|
||||||
data = json.loads(output)
|
data = json.loads(output)
|
||||||
for row in data['data']:
|
for row in data["data"]:
|
||||||
values = []
|
values = []
|
||||||
for col in row:
|
for col in row:
|
||||||
if isinstance(col, list) and len(col) > 1:
|
if isinstance(col, list) and len(col) > 1:
|
||||||
values.append(self._deserialize_ovsdb(col))
|
values.append(self._deserialize_ovsdb(col))
|
||||||
else:
|
else:
|
||||||
values.append(col)
|
values.append(col)
|
||||||
yield dict(zip(data['headings'], values))
|
yield dict(zip(data["headings"], values))
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
"""Iterate over values in OVSDB table."""
|
||||||
return self._find_tbl()
|
return self._find_tbl()
|
||||||
|
|
||||||
def clear(self, rec, col):
|
def clear(self, rec, col):
|
||||||
self.cmd_executor(self._tool, 'clear', self._table, rec, col)
|
"""Clear value from OVSDB table."""
|
||||||
|
self.cmd_executor(self._tool, "clear", self._table, rec, col)
|
||||||
|
|
||||||
def find(self, condition):
|
def find(self, condition):
|
||||||
|
"""Find value in OVSDB table."""
|
||||||
return self._find_tbl(condition=condition)
|
return self._find_tbl(condition=condition)
|
||||||
|
|
||||||
def remove(self, rec, col, value):
|
def remove(self, rec, col, value):
|
||||||
|
"""Remove value from OVSDB table."""
|
||||||
self.cmd_executor(
|
self.cmd_executor(
|
||||||
self._tool, 'remove', self._table, rec, col, value)
|
self._tool, "remove", self._table, rec, col, value
|
||||||
|
)
|
||||||
|
|
||||||
def set(self, rec, col, value):
|
def set(self, rec, col, value):
|
||||||
self.cmd_executor(self._tool, 'set', self._table, rec,
|
"""Set value in OVSDB table."""
|
||||||
'{}={}'.format(col, value))
|
self.cmd_executor(
|
||||||
|
self._tool, "set", self._table, rec, "{}={}".format(col, value)
|
||||||
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2019 Canonical Ltd
|
# Copyright 2019 Canonical Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -11,6 +11,9 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Patch utilities."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2022 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Unit tests for charm."""
|
@ -14,14 +14,15 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import mock
|
"""Tests for OVN central charm."""
|
||||||
|
|
||||||
import charm
|
import mock
|
||||||
import ops_sunbeam.test_utils as test_utils
|
import ops_sunbeam.test_utils as test_utils
|
||||||
|
|
||||||
|
import charm
|
||||||
|
|
||||||
class _OVNCentralXenaOperatorCharm(charm.OVNCentralXenaOperatorCharm):
|
|
||||||
|
|
||||||
|
class _OVNCentralOperatorCharm(charm.OVNCentralOperatorCharm):
|
||||||
def __init__(self, framework):
|
def __init__(self, framework):
|
||||||
self.seen_events = []
|
self.seen_events = []
|
||||||
super().__init__(framework)
|
super().__init__(framework)
|
||||||
@ -37,64 +38,72 @@ class _OVNCentralXenaOperatorCharm(charm.OVNCentralXenaOperatorCharm):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def cluster_status(self, db, cmd_executor):
|
def cluster_status(self, db, cmd_executor):
|
||||||
if db == 'ovnnb_db':
|
if db == "ovnnb_db":
|
||||||
nb_mock = mock.MagicMock()
|
nb_mock = mock.MagicMock()
|
||||||
nb_mock.cluster_id = 'nb_id'
|
nb_mock.cluster_id = "nb_id"
|
||||||
return nb_mock
|
return nb_mock
|
||||||
if db == 'ovnsb_db':
|
if db == "ovnsb_db":
|
||||||
sb_mock = mock.MagicMock()
|
sb_mock = mock.MagicMock()
|
||||||
sb_mock.cluster_id = 'sb_id'
|
sb_mock.cluster_id = "sb_id"
|
||||||
return sb_mock
|
return sb_mock
|
||||||
|
|
||||||
|
|
||||||
class TestOVNCentralXenaOperatorCharm(test_utils.CharmTestCase):
|
class TestOVNCentralOperatorCharm(test_utils.CharmTestCase):
|
||||||
|
"""Class for testing OVN central charm."""
|
||||||
|
|
||||||
PATCHES = [
|
PATCHES = [
|
||||||
'KubernetesServicePatch',
|
"KubernetesServicePatch",
|
||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
"""Setup Glance tests."""
|
||||||
super().setUp(charm, self.PATCHES)
|
super().setUp(charm, self.PATCHES)
|
||||||
self.harness = test_utils.get_harness(
|
self.harness = test_utils.get_harness(
|
||||||
_OVNCentralXenaOperatorCharm,
|
_OVNCentralOperatorCharm, container_calls=self.container_calls
|
||||||
container_calls=self.container_calls)
|
)
|
||||||
self.addCleanup(self.harness.cleanup)
|
self.addCleanup(self.harness.cleanup)
|
||||||
self.harness.begin()
|
self.harness.begin()
|
||||||
|
|
||||||
def test_pebble_ready_handler(self):
|
def test_pebble_ready_handler(self):
|
||||||
|
"""Test Pebble ready event is captured."""
|
||||||
self.assertEqual(self.harness.charm.seen_events, [])
|
self.assertEqual(self.harness.charm.seen_events, [])
|
||||||
test_utils.set_all_pebbles_ready(self.harness)
|
test_utils.set_all_pebbles_ready(self.harness)
|
||||||
self.assertEqual(len(self.harness.charm.seen_events), 3)
|
self.assertEqual(len(self.harness.charm.seen_events), 3)
|
||||||
|
|
||||||
def check_rendered_files(self):
|
def check_rendered_files(self):
|
||||||
|
"""Check all files are rendered."""
|
||||||
sb_config_files = [
|
sb_config_files = [
|
||||||
'/etc/ovn/cert_host',
|
"/etc/ovn/cert_host",
|
||||||
'/etc/ovn/key_host',
|
"/etc/ovn/key_host",
|
||||||
'/etc/ovn/ovn-central.crt',
|
"/etc/ovn/ovn-central.crt",
|
||||||
'/root/ovn-sb-cluster-join.sh',
|
"/root/ovn-sb-cluster-join.sh",
|
||||||
'/root/ovn-sb-db-server-wrapper.sh']
|
"/root/ovn-sb-db-server-wrapper.sh",
|
||||||
|
]
|
||||||
for f in sb_config_files:
|
for f in sb_config_files:
|
||||||
self.check_file('ovn-sb-db-server', f)
|
self.check_file("ovn-sb-db-server", f)
|
||||||
|
|
||||||
nb_config_files = [
|
nb_config_files = [
|
||||||
'/etc/ovn/cert_host',
|
"/etc/ovn/cert_host",
|
||||||
'/etc/ovn/key_host',
|
"/etc/ovn/key_host",
|
||||||
'/etc/ovn/ovn-central.crt',
|
"/etc/ovn/ovn-central.crt",
|
||||||
'/root/ovn-nb-cluster-join.sh',
|
"/root/ovn-nb-cluster-join.sh",
|
||||||
'/root/ovn-nb-db-server-wrapper.sh']
|
"/root/ovn-nb-db-server-wrapper.sh",
|
||||||
|
]
|
||||||
for f in nb_config_files:
|
for f in nb_config_files:
|
||||||
self.check_file('ovn-nb-db-server', f)
|
self.check_file("ovn-nb-db-server", f)
|
||||||
|
|
||||||
northd_config_files = [
|
northd_config_files = [
|
||||||
'/etc/ovn/cert_host',
|
"/etc/ovn/cert_host",
|
||||||
'/etc/ovn/key_host',
|
"/etc/ovn/key_host",
|
||||||
'/etc/ovn/ovn-central.crt',
|
"/etc/ovn/ovn-central.crt",
|
||||||
'/etc/ovn/ovn-northd-db-params.conf',
|
"/etc/ovn/ovn-northd-db-params.conf",
|
||||||
'/root/ovn-northd-wrapper.sh']
|
"/root/ovn-northd-wrapper.sh",
|
||||||
|
]
|
||||||
for f in northd_config_files:
|
for f in northd_config_files:
|
||||||
self.check_file('ovn-northd', f)
|
self.check_file("ovn-northd", f)
|
||||||
|
|
||||||
def test_all_relations_leader(self):
|
def test_all_relations_leader(self):
|
||||||
|
"""Test all the charms relations."""
|
||||||
self.harness.set_leader()
|
self.harness.set_leader()
|
||||||
self.assertEqual(self.harness.charm.seen_events, [])
|
self.assertEqual(self.harness.charm.seen_events, [])
|
||||||
test_utils.set_all_pebbles_ready(self.harness)
|
test_utils.set_all_pebbles_ready(self.harness)
|
||||||
@ -102,28 +111,23 @@ class TestOVNCentralXenaOperatorCharm(test_utils.CharmTestCase):
|
|||||||
self.check_rendered_files()
|
self.check_rendered_files()
|
||||||
|
|
||||||
def test_all_relations_non_leader(self):
|
def test_all_relations_non_leader(self):
|
||||||
|
"""Test all the charms relations on non-leader."""
|
||||||
self.harness.set_leader(False)
|
self.harness.set_leader(False)
|
||||||
self.assertEqual(self.harness.charm.seen_events, [])
|
self.assertEqual(self.harness.charm.seen_events, [])
|
||||||
test_utils.set_all_pebbles_ready(self.harness)
|
test_utils.set_all_pebbles_ready(self.harness)
|
||||||
rel_ids = test_utils.add_all_relations(self.harness)
|
rel_ids = test_utils.add_all_relations(self.harness)
|
||||||
test_utils.set_remote_leader_ready(
|
test_utils.set_remote_leader_ready(self.harness, rel_ids["peers"])
|
||||||
self.harness,
|
|
||||||
rel_ids['peers'])
|
|
||||||
self.harness.update_relation_data(
|
self.harness.update_relation_data(
|
||||||
rel_ids['peers'],
|
rel_ids["peers"],
|
||||||
self.harness.charm.app.name,
|
self.harness.charm.app.name,
|
||||||
{
|
{"nb_cid": "nbcid", "sb_cid": "sbcid"},
|
||||||
'nb_cid': 'nbcid',
|
|
||||||
'sb_cid': 'sbcid'}
|
|
||||||
)
|
)
|
||||||
self.check_rendered_files()
|
self.check_rendered_files()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.container_calls.execute['ovn-sb-db-server'],
|
self.container_calls.execute["ovn-sb-db-server"],
|
||||||
[
|
[["bash", "/root/ovn-sb-cluster-join.sh"]],
|
||||||
['bash', '/root/ovn-sb-cluster-join.sh']
|
)
|
||||||
])
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.container_calls.execute['ovn-nb-db-server'],
|
self.container_calls.execute["ovn-nb-db-server"],
|
||||||
[
|
[["bash", "/root/ovn-nb-cluster-join.sh"]],
|
||||||
['bash', '/root/ovn-nb-cluster-join.sh']
|
)
|
||||||
])
|
|
||||||
|
@ -15,6 +15,8 @@ minversion = 3.18.0
|
|||||||
src_path = {toxinidir}/src/
|
src_path = {toxinidir}/src/
|
||||||
tst_path = {toxinidir}/tests/
|
tst_path = {toxinidir}/tests/
|
||||||
lib_path = {toxinidir}/lib/
|
lib_path = {toxinidir}/lib/
|
||||||
|
pyproject_toml = {toxinidir}/pyproject.toml
|
||||||
|
all_path = {[vars]src_path} {[vars]tst_path}
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
@ -33,6 +35,15 @@ allowlist_externals =
|
|||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
|
[testenv:fmt]
|
||||||
|
description = Apply coding style standards to code
|
||||||
|
deps =
|
||||||
|
black
|
||||||
|
isort
|
||||||
|
commands =
|
||||||
|
isort {[vars]all_path} --skip-glob {[vars]lib_path} --skip {toxinidir}/.tox
|
||||||
|
black --config {[vars]pyproject_toml} {[vars]all_path} --exclude {[vars]lib_path}
|
||||||
|
|
||||||
[testenv:build]
|
[testenv:build]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps =
|
deps =
|
||||||
@ -64,11 +75,6 @@ deps = {[testenv:py3]deps}
|
|||||||
basepython = python3.10
|
basepython = python3.10
|
||||||
deps = {[testenv:py3]deps}
|
deps = {[testenv:py3]deps}
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
basepython = python3
|
|
||||||
deps = {[testenv]deps}
|
|
||||||
commands = flake8 {posargs} {[vars]src_path} {[vars]tst_path}
|
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps = {[testenv:py3]deps}
|
deps = {[testenv:py3]deps}
|
||||||
@ -83,6 +89,31 @@ commands =
|
|||||||
coverage xml -o cover/coverage.xml
|
coverage xml -o cover/coverage.xml
|
||||||
coverage report
|
coverage report
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
description = Alias for lint
|
||||||
|
deps = {[testenv:lint]deps}
|
||||||
|
commands = {[testenv:lint]commands}
|
||||||
|
|
||||||
|
[testenv:lint]
|
||||||
|
description = Check code against coding style standards
|
||||||
|
deps =
|
||||||
|
black
|
||||||
|
# flake8==4.0.1 # Pin version until https://github.com/csachs/pyproject-flake8/pull/14 is merged
|
||||||
|
flake8
|
||||||
|
flake8-docstrings
|
||||||
|
flake8-copyright
|
||||||
|
flake8-builtins
|
||||||
|
pyproject-flake8
|
||||||
|
pep8-naming
|
||||||
|
isort
|
||||||
|
codespell
|
||||||
|
commands =
|
||||||
|
codespell {[vars]all_path}
|
||||||
|
# pflake8 wrapper supports config from pyproject.toml
|
||||||
|
pflake8 --exclude {[vars]lib_path} --config {toxinidir}/pyproject.toml {[vars]all_path}
|
||||||
|
isort --check-only --diff {[vars]all_path} --skip-glob {[vars]lib_path}
|
||||||
|
black --config {[vars]pyproject_toml} --check --diff {[vars]all_path} --exclude {[vars]lib_path}
|
||||||
|
|
||||||
[testenv:func-noop]
|
[testenv:func-noop]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user