blob: b4866622a3569012be62ddccf19476077f750532 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
"""Helpers shared by the Linstor plugin integration tests."""
import socket
import time
from marvin.codes import FAILED
from marvin.lib.common import get_template
# Registered by `cs-linstor.py setup-test-template`. Optional: clusters without it fall
# back to the builtin template, so nothing here requires the extra setup step.
TEST_TEMPLATE_NAME = "marvin-alpine"
def get_guest_template(api_client, zone_id, hypervisor="KVM", name=TEST_TEMPLATE_NAME):
"""The small purpose-built guest template if it is registered, else the builtin one.
The builtin CentOS 5.5 template is an 8 GiB volume that takes ~55s to reach sshd.
Worse, creating an *encrypted* volume from a template cannot share blocks with the
source - a new passphrase means different ciphertext - so LINSTOR does a full
block-level re-encrypt copy of the whole virtual size, measured at ~500s per VM.
A ~512 MiB template makes that negligible.
Note marvin's get_template() only returns FAILED when the list call itself comes back
empty; otherwise it falls through to the first result even if nothing matched the
requested type. So detection has to key off FAILED, and the type has to be USER
because our template is registered rather than built in.
"""
template = get_template(api_client, zone_id, template_filter="all",
template_type="USER", template_name=name,
hypervisor=hypervisor)
if template != FAILED and getattr(template, "isready", False):
return template
return get_template(api_client, zone_id, hypervisor=hypervisor)
class ServiceReady:
@classmethod
def ready(cls, hostname: str, port: int) -> bool:
try:
s = socket.create_connection((hostname, port), timeout=1)
s.close()
return True
except (ConnectionRefusedError, socket.timeout, OSError):
return False
@classmethod
def wait(cls, hostname: str, port: int, wait_interval: float = 5, timeout: int = 120,
service_name: str = 'ssh') -> bool:
"""
Wait until the given service can be reached, raise RuntimeError on timeout.
:param hostname: host to connect to
:param port: port of the application
:param wait_interval: seconds between connection attempts
:param timeout: seconds to wait before raising
:param service_name: name of the service waited for (used in the error message)
"""
starttime = int(round(time.time() * 1000))
while not cls.ready(hostname, port):
if starttime + timeout * 1000 < int(round(time.time() * 1000)):
raise RuntimeError("{s} {h} cannot be reached.".format(s=service_name, h=hostname))
time.sleep(wait_interval)
return True
@classmethod
def wait_ssh_ready(cls, hostname: str, wait_interval: float = 2, timeout: int = 120) -> bool:
return cls.wait(hostname, 22, wait_interval, timeout, "ssh")