This patch add experimental s3 support for Trilio
This patch allows an s3 backend to be used to store Trilio artefacts. Connection auth and endpoint are set via the s3 charm config options. To use an s3 backend the backup-target-type should be set to 'experimental-s3'. func-test-pr: https://github.com/openstack-charmers/zaza-openstack-tests/pull/516 Change-Id: I1dab03d25e6e583d88d69ea15e5b5119151c9676
This commit is contained in:
parent
cb2e78c437
commit
8e36f135e6
@ -11,6 +11,7 @@
|
||||
- focal-ussuri-41
|
||||
- bionic-queens-40
|
||||
- bionic-queens-41
|
||||
- bionic-train-41-s3
|
||||
vars:
|
||||
needs_charm_build: true
|
||||
charm_build_name: trilio-data-mover
|
||||
@ -68,3 +69,9 @@
|
||||
dependencies: *smoke-jobs
|
||||
vars:
|
||||
tox_extra_args: bionic-ussuri-41
|
||||
- job:
|
||||
name: bionic-train-41-s3
|
||||
parent: func-target
|
||||
dependencies: *smoke-jobs
|
||||
vars:
|
||||
tox_extra_args: bionic-train-41-s3
|
||||
|
@ -32,11 +32,11 @@ options:
|
||||
# UNSUPPORTED
|
||||
tv-s3-secret-key:
|
||||
type: string
|
||||
default: sample_s3_secret_key
|
||||
default:
|
||||
description: S3 secret access key
|
||||
tv-s3-access-key:
|
||||
type: string
|
||||
default: sample_s3_access_key
|
||||
default:
|
||||
description: S3 access key
|
||||
tv-s3-region-name:
|
||||
type: string
|
||||
@ -44,7 +44,7 @@ options:
|
||||
description: S3 region name
|
||||
tv-s3-bucket:
|
||||
type: string
|
||||
default: sample_s3_bucket_name
|
||||
default:
|
||||
description: S3 bucket name
|
||||
tv-s3-endpoint-url:
|
||||
type: string
|
||||
|
@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
import charmhelpers.core.hookenv as hookenv
|
||||
@ -45,6 +46,14 @@ class DataMoverDBAdapter(os_adapters.DatabaseRelationAdapter):
|
||||
return self.get_uri(prefix="dmapi")
|
||||
|
||||
|
||||
@charms_openstack.adapters.config_property
|
||||
def translated_backup_target_type(cls):
|
||||
_type = hookenv.config("backup-target-type").lower()
|
||||
if _type == "experimental-s3":
|
||||
return 's3'
|
||||
return _type
|
||||
|
||||
|
||||
@os_adapters.config_property
|
||||
def ceph_dir(ceph):
|
||||
return os.path.join("/var/lib/charm", hookenv.service_name())
|
||||
@ -94,13 +103,14 @@ class TrilioDataMoverBaseCharm(
|
||||
|
||||
data_mover_conf = "/etc/tvault-contego/tvault-contego.conf"
|
||||
logrotate_conf = "/etc/logrotate.d/tvault-contego"
|
||||
object_store_conf = "/etc/tvault-object-store/tvault-object-store.conf"
|
||||
|
||||
service_type = "data-mover"
|
||||
default_service = "tvault-contego"
|
||||
|
||||
required_relations = ["amqp", "shared-db"]
|
||||
|
||||
packages = ["tvault-contego", "nfs-common", "contego"]
|
||||
base_packages = ["tvault-contego", "nfs-common", "contego"]
|
||||
|
||||
# configuration file permissions
|
||||
user = "root"
|
||||
@ -114,6 +124,26 @@ class TrilioDataMoverBaseCharm(
|
||||
os_release_pkg = "nova-common"
|
||||
package_codenames = os_utils.PACKAGE_CODENAMES
|
||||
|
||||
@property
|
||||
def backup_target_type(self):
|
||||
# The main purpose of this property is to translate experimental-s3
|
||||
# to s3 and s3 to UNKNOWN. This forces the deployer to
|
||||
# use 'experimental-s3' for s3 support but the code can stay clean and
|
||||
# refer to s3.
|
||||
_type = hookenv.config("backup-target-type").lower()
|
||||
if _type == 'experimental-s3':
|
||||
return 's3'
|
||||
if _type == 'nfs':
|
||||
return 'nfs'
|
||||
return 'UNKNOWN'
|
||||
|
||||
@property
|
||||
def packages(self):
|
||||
_pkgs = copy.deepcopy(self.base_packages)
|
||||
if self.backup_target_type == 's3':
|
||||
_pkgs.append('python3-s3-fuse-plugin')
|
||||
return _pkgs
|
||||
|
||||
# Set ceph keyring prefix to charm specific location
|
||||
@property
|
||||
def ceph_keyring_path_prefix(self):
|
||||
@ -169,25 +199,42 @@ class TrilioDataMoverBaseCharm(
|
||||
|
||||
@property
|
||||
def services(self):
|
||||
if hookenv.config("backup-target-type") == "s3":
|
||||
if self.backup_target_type == "s3":
|
||||
return ["tvault-contego", "tvault-object-store"]
|
||||
return ["tvault-contego"]
|
||||
|
||||
@property
|
||||
def restart_map(self):
|
||||
_restart_map = {
|
||||
self.data_mover_conf: self.services,
|
||||
}
|
||||
if reactive.flags.is_flag_set("ceph.available"):
|
||||
return {
|
||||
self.data_mover_conf: self.services,
|
||||
self.ceph_conf: self.services,
|
||||
}
|
||||
return {
|
||||
self.data_mover_conf: self.services,
|
||||
}
|
||||
_restart_map[self.ceph_conf] = self.services
|
||||
if self.backup_target_type == 's3':
|
||||
_restart_map[self.object_store_conf] = ['tvault-object-store']
|
||||
return _restart_map
|
||||
|
||||
def custom_assess_status_check(self):
|
||||
"""Check required configuration options are set"""
|
||||
if not hookenv.config("nfs-shares"):
|
||||
return "blocked", "nfs-shares configuration not set"
|
||||
check_config_set = []
|
||||
if self.backup_target_type == "nfs":
|
||||
check_config_set = ['nfs-shares']
|
||||
elif self.backup_target_type == "s3":
|
||||
check_config_set = [
|
||||
"tv-s3-secret-key",
|
||||
"tv-s3-access-key",
|
||||
"tv-s3-region-name",
|
||||
"tv-s3-bucket",
|
||||
"tv-s3-endpoint-url"]
|
||||
unset_config = [c for c in check_config_set if not hookenv.config(c)]
|
||||
if unset_config:
|
||||
return "blocked", "{} configuration not set".format(
|
||||
', '.join(unset_config))
|
||||
# For s3 support backup-target-type should be set to 'experimental-s3'
|
||||
# as s3 support is pre-production. The self.backup_target_type
|
||||
# property will do any transaltion needed.
|
||||
if self.backup_target_type not in ["nfs", "s3"]:
|
||||
return "blocked", "Backup target type not supported"
|
||||
return None, None
|
||||
|
||||
def request_access_to_groups(self, ceph):
|
||||
@ -216,7 +263,7 @@ class TrilioDataMoverRockyCharm(TrilioDataMoverBaseCharm):
|
||||
# Python version used to execute installed workload
|
||||
python_version = 3
|
||||
|
||||
packages = ["python3-tvault-contego", "nfs-common", "contego"]
|
||||
base_packages = ["python3-tvault-contego", "nfs-common", "contego"]
|
||||
|
||||
@classmethod
|
||||
def trilio_version_package(cls):
|
||||
|
@ -13,13 +13,13 @@ dmapi_transport_url = {{ amqp.transport_url }}
|
||||
rabbit_virtual_host = {{ amqp.vhost }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.backup_target_type == 'nfs' -%}
|
||||
{% if options.translated_backup_target_type == 'nfs' -%}
|
||||
# NFS
|
||||
vault_storage_nfs_export = {{ options.nfs_shares }}
|
||||
vault_storage_nfs_options = {{ options.nfs_options }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.backup_target_type == 's3' -%}
|
||||
{% if options.translated_backup_target_type == 's3' -%}
|
||||
# S3
|
||||
vault_storage_nfs_export = TrilioVault
|
||||
vault_s3_auth_version = DEFAULT
|
||||
@ -31,7 +31,7 @@ vault_s3_endpoint_url = {{ options.tv_s3_endpoint_url }}
|
||||
{% endif -%}
|
||||
|
||||
# MISC
|
||||
vault_storage_type = {{ options.backup_target_type }}
|
||||
vault_storage_type = {{ options.translated_backup_target_type }}
|
||||
vault_data_directory_old = {{ options.tv_data_dir_old }}
|
||||
vault_data_directory = {{ options.tv_data_dir }}
|
||||
|
||||
|
19
src/templates/tvault-object-store.conf
Normal file
19
src/templates/tvault-object-store.conf
Normal file
@ -0,0 +1,19 @@
|
||||
[DEFAULT]
|
||||
verbose = {{ options.verbose }}
|
||||
debug = {{ options.debug }}
|
||||
log_file = /var/log/tvault-object-store/tvault-object-store.log
|
||||
|
||||
vault_storage_nfs_export = TrilioVault
|
||||
vault_data_directory = /var/triliovault-mounts
|
||||
vault_data_directory_old = /var/triliovault
|
||||
vault_storage_type = {{ options.translated_backup_target_type }}
|
||||
vault_storage_nfs_export = TrilioVault
|
||||
vault_s3_auth_version = DEFAULT
|
||||
vault_s3_access_key_id = {{ options.tv_s3_access_key }}
|
||||
vault_s3_secret_access_key = {{ options.tv_s3_secret_key }}
|
||||
vault_s3_region_name = {{ options.tv_s3_region_name }}
|
||||
vault_s3_bucket = {{ options.tv_s3_bucket }}
|
||||
vault_s3_endpoint_url = {{ options.tv_s3_endpoint_url }}
|
||||
|
||||
[s3fuse_sys_admin]
|
||||
helper_command = sudo /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf privsep-helper
|
249
src/tests/bundles/bionic-train-41-s3.yaml
Normal file
249
src/tests/bundles/bionic-train-41-s3.yaml
Normal file
@ -0,0 +1,249 @@
|
||||
local_overlay_enabled: False
|
||||
variables:
|
||||
openstack-origin: &openstack-origin 'cloud:bionic-train'
|
||||
triliovault-pkg-source: &triliovault-pkg-source 'deb [trusted=yes] https://apt.fury.io/triliodata-4-1/ /'
|
||||
|
||||
series: &series bionic
|
||||
|
||||
relations:
|
||||
- - 'nova-compute:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'neutron-gateway:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'neutron-gateway:amqp-nova'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'keystone:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'cinder:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'nova-cloud-controller:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'glance:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'neutron-api:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'neutron-openvswitch:neutron-plugin-api'
|
||||
- 'neutron-api:neutron-plugin-api'
|
||||
- - 'cinder:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'neutron-api:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'cinder:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'neutron-api:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'neutron-gateway:neutron-plugin-api'
|
||||
- 'neutron-api:neutron-plugin-api'
|
||||
- - 'glance:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'glance:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'nova-cloud-controller:image-service'
|
||||
- 'glance:image-service'
|
||||
- - 'nova-compute:image-service'
|
||||
- 'glance:image-service'
|
||||
- - 'nova-cloud-controller:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'nova-cloud-controller:quantum-network-service'
|
||||
- 'neutron-gateway:quantum-network-service'
|
||||
- - 'nova-compute:neutron-plugin'
|
||||
- 'neutron-openvswitch:neutron-plugin'
|
||||
- - 'neutron-openvswitch:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'nova-cloud-controller:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'nova-cloud-controller:neutron-api'
|
||||
- 'neutron-api:neutron-api'
|
||||
- - 'nova-cloud-controller:cloud-compute'
|
||||
- 'nova-compute:cloud-compute'
|
||||
- - 'trilio-wlm:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'trilio-wlm:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'trilio-wlm:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'glance:ceph'
|
||||
- 'ceph-mon:client'
|
||||
- - 'ceph-mon:osd'
|
||||
- 'ceph-osd:mon'
|
||||
- - 'ceph-osd:secrets-storage'
|
||||
- 'vault:secrets'
|
||||
- - 'cinder:storage-backend'
|
||||
- 'cinder-ceph:storage-backend'
|
||||
- - 'cinder-ceph:ceph'
|
||||
- 'ceph-mon:client'
|
||||
- - 'cinder-ceph:ceph-access'
|
||||
- 'nova-compute:ceph-access'
|
||||
- - 'vault:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'vault:certificates'
|
||||
- 'keystone:certificates'
|
||||
- - 'vault:certificates'
|
||||
- 'neutron-api:certificates'
|
||||
- - 'vault:certificates'
|
||||
- 'cinder:certificates'
|
||||
- - 'vault:certificates'
|
||||
- 'glance:certificates'
|
||||
- - 'vault:certificates'
|
||||
- 'nova-cloud-controller:certificates'
|
||||
- - 'placement:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'placement:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'placement:placement'
|
||||
- 'nova-cloud-controller:placement'
|
||||
- - 'vault:certificates'
|
||||
- 'placement:certificates'
|
||||
- - 'vault:certificates'
|
||||
- 'trilio-wlm:certificates'
|
||||
- - 'trilio-data-mover:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'trilio-data-mover:juju-info'
|
||||
- 'nova-compute:juju-info'
|
||||
- - 'trilio-dm-api:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'trilio-dm-api:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'trilio-dm-api:amqp'
|
||||
- 'rabbitmq-server:amqp'
|
||||
- - 'trilio-dm-api:certificates'
|
||||
- 'vault:certificates'
|
||||
- - 'trilio-horizon-plugin:dashboard-plugin'
|
||||
- 'openstack-dashboard:dashboard-plugin'
|
||||
- - 'openstack-dashboard:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'trilio-data-mover:ceph'
|
||||
- 'ceph-mon:client'
|
||||
- - 'trilio-data-mover:shared-db'
|
||||
- 'mysql:shared-db'
|
||||
- - 'ceph-radosgw:mon'
|
||||
- 'ceph-mon:radosgw'
|
||||
- - 'ceph-radosgw:identity-service'
|
||||
- 'keystone:identity-service'
|
||||
- - 'ceph-radosgw:certificates'
|
||||
- 'vault:certificates'
|
||||
applications:
|
||||
ceph-radosgw:
|
||||
charm: cs:~openstack-charmers-next/ceph-radosgw
|
||||
num_units: 1
|
||||
options:
|
||||
source: *openstack-origin
|
||||
glance:
|
||||
charm: cs:~openstack-charmers-next/glance
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
cinder:
|
||||
charm: cs:~openstack-charmers-next/cinder
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
block-device: "None"
|
||||
glance-api-version: 2
|
||||
keystone:
|
||||
charm: cs:~openstack-charmers-next/keystone
|
||||
series: bionic
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
admin-password: openstack
|
||||
mysql:
|
||||
charm: cs:~openstack-charmers-next/percona-cluster
|
||||
num_units: 1
|
||||
options:
|
||||
innodb-buffer-pool-size: 256M
|
||||
max-connections: 1000
|
||||
neutron-api:
|
||||
charm: cs:~openstack-charmers-next/neutron-api
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
flat-network-providers: physnet1
|
||||
neutron-security-groups: true
|
||||
neutron-gateway:
|
||||
charm: cs:~openstack-charmers-next/neutron-gateway
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
bridge-mappings: physnet1:br-ex
|
||||
neutron-openvswitch:
|
||||
charm: cs:~openstack-charmers-next/neutron-openvswitch
|
||||
num_units: 0
|
||||
nova-cloud-controller:
|
||||
charm: cs:~openstack-charmers-next/nova-cloud-controller
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
network-manager: Neutron
|
||||
debug: true
|
||||
nova-compute:
|
||||
charm: cs:~openstack-charmers-next/nova-compute
|
||||
num_units: 3
|
||||
constraints: mem=4G
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
config-flags: default_ephemeral_format=ext4
|
||||
enable-live-migration: true
|
||||
enable-resize: true
|
||||
migration-auth-type: ssh
|
||||
debug: true
|
||||
cpu-model: kvm64
|
||||
cpu-mode: custom
|
||||
# Allow for more retries when testing ontop of openstack
|
||||
config-flags: block_device_allocate_retries=120
|
||||
rabbitmq-server:
|
||||
charm: cs:~openstack-charmers-next/rabbitmq-server
|
||||
num_units: 1
|
||||
trilio-data-mover:
|
||||
charm: ../../../trilio-data-mover
|
||||
options:
|
||||
triliovault-pkg-source: *triliovault-pkg-source
|
||||
backup-target-type: experimental-s3
|
||||
trilio-dm-api:
|
||||
charm: cs:~openstack-charmers-next/trilio-dm-api
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
triliovault-pkg-source: *triliovault-pkg-source
|
||||
trilio-horizon-plugin:
|
||||
charm: cs:~openstack-charmers-next/trilio-horizon-plugin
|
||||
options:
|
||||
triliovault-pkg-source: *triliovault-pkg-source
|
||||
trilio-wlm:
|
||||
charm: cs:~openstack-charmers-next/trilio-wlm
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
triliovault-pkg-source: *triliovault-pkg-source
|
||||
backup-target-type: experimental-s3
|
||||
ceph-mon:
|
||||
charm: cs:~openstack-charmers-next/ceph-mon
|
||||
num_units: 3
|
||||
options:
|
||||
source: *openstack-origin
|
||||
expected-osd-count: 3
|
||||
ceph-osd:
|
||||
charm: cs:~openstack-charmers-next/ceph-osd
|
||||
constraints: mem=1G
|
||||
num_units: 3
|
||||
options:
|
||||
source: *openstack-origin
|
||||
osd-encrypt-keymanager: vault
|
||||
osd-encrypt: True
|
||||
storage:
|
||||
osd-devices: cinder,40G
|
||||
cinder-ceph:
|
||||
charm: cs:~openstack-charmers-next/cinder-ceph
|
||||
vault:
|
||||
num_units: 1
|
||||
charm: cs:~openstack-charmers-next/vault
|
||||
placement:
|
||||
charm: cs:~openstack-charmers-next/placement
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
||||
openstack-dashboard:
|
||||
charm: cs:~openstack-charmers-next/openstack-dashboard
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: *openstack-origin
|
@ -1,15 +1,26 @@
|
||||
charm_name: trilio-data-mover
|
||||
tests:
|
||||
- zaza.openstack.charm_tests.trilio.tests.TrilioDataMoverTest
|
||||
- zaza.openstack.charm_tests.trilio.tests.TrilioDataMoverNFSTest
|
||||
- s3:
|
||||
- zaza.openstack.charm_tests.trilio.tests.TrilioDataMoverS3Test
|
||||
configure:
|
||||
- zaza.openstack.charm_tests.trilio.setup.basic_setup
|
||||
- s3:
|
||||
- zaza.openstack.charm_tests.vault.setup.auto_initialize
|
||||
- zaza.openstack.charm_tests.trilio.setup.basic_setup
|
||||
- zaza.openstack.charm_tests.glance.setup.add_cirros_image
|
||||
- zaza.openstack.charm_tests.glance.setup.add_lts_image
|
||||
- zaza.openstack.charm_tests.neutron.setup.basic_overcloud_network
|
||||
- zaza.openstack.charm_tests.nova.setup.create_flavors
|
||||
- zaza.openstack.charm_tests.nova.setup.manage_ssh_key
|
||||
- zaza.openstack.charm_tests.vault.setup.auto_initialize
|
||||
- zaza.openstack.charm_tests.trilio.setup.basic_setup
|
||||
- zaza.openstack.charm_tests.glance.setup.add_cirros_image
|
||||
- zaza.openstack.charm_tests.glance.setup.add_lts_image
|
||||
- zaza.openstack.charm_tests.neutron.setup.basic_overcloud_network
|
||||
- zaza.openstack.charm_tests.nova.setup.create_flavors
|
||||
- zaza.openstack.charm_tests.nova.setup.manage_ssh_key
|
||||
gate_bundles:
|
||||
- s3: bionic-train-41-s3
|
||||
- bionic-stein-40
|
||||
- bionic-stein-41
|
||||
- bionic-train-40
|
||||
@ -35,10 +46,13 @@ target_deploy_status:
|
||||
workload-status-message: "Ceph broker request incomplete"
|
||||
trilio-wlm:
|
||||
workload-status: blocked
|
||||
workload-status-message: "nfs-shares configuration not set"
|
||||
workload-status-message-regex: "configuration not set|Unit is ready"
|
||||
trilio-data-mover:
|
||||
workload-status: blocked
|
||||
workload-status-message: "nfs-shares configuration not set"
|
||||
workload-status-message-regex: "configuration not set|Unit is ready"
|
||||
glance:
|
||||
workload-status: waiting
|
||||
workload-status-message: "Incomplete relations: storage-backend"
|
||||
ceph-radosgw:
|
||||
workload-status: waiting
|
||||
workload-status-message: "Incomplete relations: mon"
|
||||
|
@ -39,7 +39,7 @@ class TestTrilioDataMoverRockyCharms(Helper):
|
||||
def test_services_s3(self):
|
||||
dm_charm = trilio_dm.TrilioDataMoverRockyCharm()
|
||||
self.patch_object(trilio_dm.hookenv, "config")
|
||||
self.config.return_value = "s3"
|
||||
self.config.return_value = "experimental-s3"
|
||||
self.assertEqual(
|
||||
dm_charm.services, ["tvault-contego", "tvault-object-store"]
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user