Refactor devenv service helper
Use docker API to operate with remote devenv, this makes code cleaner and more uniform between local devenv and remote devenv. Signed-off-by: Vladimir Domnich <v.domnich@yadro.com>
This commit is contained in:
parent
e88d64a263
commit
6110de9268
1 changed files with 39 additions and 55 deletions
|
@ -1,4 +1,3 @@
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
@ -22,19 +21,19 @@ class LocalDevEnvStorageServiceHelper:
|
||||||
"""
|
"""
|
||||||
def stop_node(self, node_name: str) -> None:
|
def stop_node(self, node_name: str) -> None:
|
||||||
container_name = _get_storage_container_name(node_name)
|
container_name = _get_storage_container_name(node_name)
|
||||||
client = docker.APIClient()
|
client = self._get_docker_client(node_name)
|
||||||
client.stop(container_name)
|
client.stop(container_name)
|
||||||
|
|
||||||
def start_node(self, node_name: str) -> None:
|
def start_node(self, node_name: str) -> None:
|
||||||
container_name = _get_storage_container_name(node_name)
|
container_name = _get_storage_container_name(node_name)
|
||||||
client = docker.APIClient()
|
client = self._get_docker_client(node_name)
|
||||||
client.start(container_name)
|
client.start(container_name)
|
||||||
|
|
||||||
def wait_for_node_to_start(self, node_name: str) -> None:
|
def wait_for_node_to_start(self, node_name: str) -> None:
|
||||||
container_name = _get_storage_container_name(node_name)
|
container_name = _get_storage_container_name(node_name)
|
||||||
expected_state = "running"
|
expected_state = "running"
|
||||||
for __attempt in range(10):
|
for __attempt in range(10):
|
||||||
container = self._get_container_by_name(container_name)
|
container = self._get_container_by_name(node_name, container_name)
|
||||||
if container and container["State"] == expected_state:
|
if container and container["State"] == expected_state:
|
||||||
return
|
return
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
@ -54,7 +53,7 @@ class LocalDevEnvStorageServiceHelper:
|
||||||
def delete_node_data(self, node_name: str) -> None:
|
def delete_node_data(self, node_name: str) -> None:
|
||||||
volume_name = _get_storage_volume_name(node_name)
|
volume_name = _get_storage_volume_name(node_name)
|
||||||
|
|
||||||
client = docker.APIClient()
|
client = self._get_docker_client(node_name)
|
||||||
volume_info = client.inspect_volume(volume_name)
|
volume_info = client.inspect_volume(volume_name)
|
||||||
volume_path = volume_info["Mountpoint"]
|
volume_path = volume_info["Mountpoint"]
|
||||||
|
|
||||||
|
@ -63,14 +62,19 @@ class LocalDevEnvStorageServiceHelper:
|
||||||
def get_binaries_version(self) -> dict:
|
def get_binaries_version(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _get_container_by_name(self, container_name: str) -> dict:
|
def _get_container_by_name(self, node_name: str, container_name: str) -> dict:
|
||||||
client = docker.APIClient()
|
client = self._get_docker_client(node_name)
|
||||||
containers = client.containers()
|
containers = client.containers()
|
||||||
for container in containers:
|
for container in containers:
|
||||||
if container_name in container["Names"]:
|
if container_name in container["Names"]:
|
||||||
return container
|
return container
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _get_docker_client(self, node_name: str) -> docker.APIClient:
|
||||||
|
# For local devenv we use default docker client that talks to unix socket
|
||||||
|
client = docker.APIClient()
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
class CloudVmStorageServiceHelper:
|
class CloudVmStorageServiceHelper:
|
||||||
STORAGE_SERVICE = "neofs-storage.service"
|
STORAGE_SERVICE = "neofs-storage.service"
|
||||||
|
@ -165,29 +169,19 @@ class CloudVmStorageServiceHelper:
|
||||||
return version_map
|
return version_map
|
||||||
|
|
||||||
|
|
||||||
class RemoteDevEnvStorageServiceHelper:
|
class RemoteDevEnvStorageServiceHelper(LocalDevEnvStorageServiceHelper):
|
||||||
"""
|
"""
|
||||||
Manages storage services running on remote devenv.
|
Manages storage services running on remote devenv.
|
||||||
|
|
||||||
|
Most of operations are identical to local devenv, however, any interactions
|
||||||
|
with host resources (files, etc.) require ssh into the devenv host machine.
|
||||||
"""
|
"""
|
||||||
def stop_node(self, node_name: str) -> None:
|
def _get_docker_client(self, node_name: str) -> docker.APIClient:
|
||||||
container_name = _get_storage_container_name(node_name)
|
# For remote devenv we use docker client that talks to tcp socket 2375:
|
||||||
with _create_ssh_client(node_name) as ssh_client:
|
# https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option
|
||||||
ssh_client.exec(f'docker stop {container_name}')
|
host = _get_node_host(node_name)
|
||||||
|
client = docker.APIClient(base_url=f"tcp://{host}:2375")
|
||||||
def start_node(self, node_name: str) -> None:
|
return client
|
||||||
container_name = _get_storage_container_name(node_name)
|
|
||||||
with _create_ssh_client(node_name) as ssh_client:
|
|
||||||
ssh_client.exec(f'docker start {container_name}')
|
|
||||||
|
|
||||||
def wait_for_node_to_start(self, node_name: str) -> None:
|
|
||||||
container_name = _get_storage_container_name(node_name)
|
|
||||||
expected_state = 'running'
|
|
||||||
for __attempt in range(10):
|
|
||||||
container = self._get_container_by_name(node_name, container_name)
|
|
||||||
if container and container["State"] == expected_state:
|
|
||||||
return
|
|
||||||
time.sleep(3)
|
|
||||||
raise AssertionError(f'Container {container_name} is not in {expected_state} state')
|
|
||||||
|
|
||||||
def run_control_command(self, node_name: str, command: str) -> str:
|
def run_control_command(self, node_name: str, command: str) -> str:
|
||||||
# On remote devenv it works same way as in cloud
|
# On remote devenv it works same way as in cloud
|
||||||
|
@ -195,30 +189,15 @@ class RemoteDevEnvStorageServiceHelper:
|
||||||
|
|
||||||
def delete_node_data(self, node_name: str) -> None:
|
def delete_node_data(self, node_name: str) -> None:
|
||||||
volume_name = _get_storage_volume_name(node_name)
|
volume_name = _get_storage_volume_name(node_name)
|
||||||
with _create_ssh_client(node_name) as ssh_client:
|
|
||||||
volume_info_raw = ssh_client.exec(f'docker volume inspect {volume_name}').stdout
|
|
||||||
volume_info = json.loads(volume_info_raw)
|
|
||||||
volume_path = volume_info[0]["Mountpoint"]
|
|
||||||
|
|
||||||
|
client = self._get_docker_client(node_name)
|
||||||
|
volume_info = client.inspect_volume(volume_name)
|
||||||
|
volume_path = volume_info["Mountpoint"]
|
||||||
|
|
||||||
|
# SSH into remote machine and delete files in host directory that is mounted as docker volume
|
||||||
|
with _create_ssh_client(node_name) as ssh_client:
|
||||||
ssh_client.exec(f'rm -rf {volume_path}/*')
|
ssh_client.exec(f'rm -rf {volume_path}/*')
|
||||||
|
|
||||||
def get_binaries_version(self) -> dict:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def _get_container_by_name(self, node_name: str, container_name: str) -> dict:
|
|
||||||
with _create_ssh_client(node_name) as ssh_client:
|
|
||||||
output = ssh_client.exec('docker ps -a --format "{{json .}}"').stdout
|
|
||||||
|
|
||||||
# output contains each container as separate JSON structure, so we find each JSON structure
|
|
||||||
# by curly brackets (works because JSON is not nested), parse it and find container by name
|
|
||||||
json_blocks = re.findall(r'\{.*?\}', output, re.DOTALL)
|
|
||||||
for json_block in json_blocks:
|
|
||||||
container = json.loads(json_block)
|
|
||||||
# unlike docker.API in docker ps output Names seems to be a string, so we check by equality
|
|
||||||
if container["Names"] == container_name:
|
|
||||||
return container
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_storage_service_helper():
|
def get_storage_service_helper():
|
||||||
if INFRASTRUCTURE_TYPE == "LOCAL_DEVENV":
|
if INFRASTRUCTURE_TYPE == "LOCAL_DEVENV":
|
||||||
|
@ -233,13 +212,7 @@ def get_storage_service_helper():
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def _create_ssh_client(node_name: str) -> HostClient:
|
def _create_ssh_client(node_name: str) -> HostClient:
|
||||||
if node_name not in NEOFS_NETMAP_DICT:
|
host = _get_node_host(node_name)
|
||||||
raise AssertionError(f'Node {node_name} is not found!')
|
|
||||||
|
|
||||||
# We use rpc endpoint to determine host address, because control endpoint
|
|
||||||
# (if it is private) will be a local address on the host machine
|
|
||||||
node_config = NEOFS_NETMAP_DICT.get(node_name)
|
|
||||||
host = node_config.get('rpc').split(':')[0]
|
|
||||||
ssh_client = HostClient(
|
ssh_client = HostClient(
|
||||||
host,
|
host,
|
||||||
login=STORAGE_NODE_SSH_USER,
|
login=STORAGE_NODE_SSH_USER,
|
||||||
|
@ -253,6 +226,17 @@ def _create_ssh_client(node_name: str) -> HostClient:
|
||||||
ssh_client.drop()
|
ssh_client.drop()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_node_host(node_name: str) -> str:
|
||||||
|
if node_name not in NEOFS_NETMAP_DICT:
|
||||||
|
raise AssertionError(f'Node {node_name} is not found!')
|
||||||
|
|
||||||
|
# We use rpc endpoint to determine host address, because control endpoint
|
||||||
|
# (if it is private) will be a local address on the host machine
|
||||||
|
node_config = NEOFS_NETMAP_DICT.get(node_name)
|
||||||
|
host = node_config.get('rpc').split(':')[0]
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
def _get_storage_container_name(node_name: str) -> str:
|
def _get_storage_container_name(node_name: str) -> str:
|
||||||
"""
|
"""
|
||||||
Converts name of storage node (as it is listed in netmap) into the name of docker container
|
Converts name of storage node (as it is listed in netmap) into the name of docker container
|
||||||
|
|
Loading…
Reference in a new issue