def convert(input_format, output_format, input_path, output_path): """ Execute qemu-img inside a container that mounts input_path and output_path to itself """ # mount the input file to /work/<filename> inside the container path_in = Path(input_path) input_abspath = path_in.absolute().__str__() assert_path_exists(input_abspath) internal_input_path = f"/input/{path_in.name}" in_mount = f"-v {input_abspath}:{internal_input_path}" # The mount for the output dir varies depending on if its a file or block device path_out = Path(output_path) if path_out.is_block_device(): # directly map the block device to the container assert_path_exists(path_out) out_mount = f"--device {path_out}" internal_output_path = path_out else: # output is a file (or about to be), so mount the folder it exists in output_abspath = path_out.absolute().__str__() output_dir = Path(output_abspath).parent.__str__() assert_path_exists(output_dir) internal_output_dir = "/output" internal_output_path = f"{internal_output_dir}/{path_out.name}" out_mount = f"-v {output_dir}:{internal_output_dir}" name = "qemu-img" image = "breqwatr/qemu-img:latest" run = (f"qemu-img convert -f {input_format} -O {output_format} " f"{internal_input_path} {internal_output_path}") cmd = f"docker run -it --name {name} --rm {in_mount} {out_mount} {image} {run}" shell(cmd)
def kolla_ansible_globals(release): """ Genereate certificates directory """ cwd = os.getcwd() cmd = (f"docker run --rm -v {cwd}:/temp-dir " f"breqwatr/kolla-ansible:{release} " "cp /var/repos/kolla-ansible/etc/kolla/globals.yml temp-dir/") shell(cmd)
def pull_image_from_registry(name, tag, local_registry): """ Pull image from offline registry""" cmd = ( f"docker pull {local_registry}/breqwatr/{name}:{tag}" f" && docker tag {local_registry}/breqwatr/{name}:{tag} breqwatr/{name}:{tag}" ) shell(cmd)
def create_and_upload_offline_voithos_tar_file(voithos_branch): """ Creates voithos package, downlaod dependencies and upload them to s3""" voithos_requirements = pkg_resources.get_distribution("voithos").requires() requirements_str = " ".join(str(i) for i in voithos_requirements) home_dir = str(Path.home()) packages_dir = f"{home_dir}/voithos_packages" echo("Creating base directory: {}".format(packages_dir)) os.mkdir(packages_dir) os.mkdir(f"{packages_dir}/dependencies") os.chdir(packages_dir) requirements_file = open("requirements.txt", "w+") for r in requirements_str.split(): requirements_file.write(f"{r}\n") requirements_file.close() cmds = ( f"pip download -r {packages_dir}/requirements.txt -d {packages_dir}/dependencies/ " f"&& git clone --branch {voithos_branch} https://github.com/breqwatr/voithos.git " f"&& cd {packages_dir}/voithos " f"&& python3 setup.py sdist " f"&& mv {packages_dir}/voithos/dist/voithos*tar.gz {packages_dir} " f"&& cd {packages_dir} " f"&& rm -r {packages_dir}/voithos " f"&& cd && tar --remove-files -zcf voithos.tar.gz voithos_packages" ) shell(cmds) tar_file_path = f"{home_dir}/voithos.tar.gz" s3.upload(tar_file_path, "voithos-files", "voithos.tar.gz")
def start(release, openstack_vip, sql_pass, sql_ip, rabbit_ips_list, rabbit_pass, enable_ceph, kolla_ansible_dir, cloud_name): """ Start the arcus api """ rabbit_ips_csv = ",".join(rabbit_ips_list) image = f"breqwatr/arcus-mgr:{release}" env_vars = { "OPENSTACK_VIP": openstack_vip, "SQL_USERNAME": "******", "SQL_PASSWORD": sql_pass, "SQL_IP": sql_ip, "DR_SQL_USERNAME": "******", "DR_SQL_PASSWORD": sql_pass, "DR_SQL_IP": sql_ip, "RABBIT_NODES_CSV": rabbit_ips_csv, "RABBIT_USERNAME": "******", "RABBIT_PASSWORD": rabbit_pass, "ENABLE_CEPH": str(enable_ceph).lower(), "CLOUD_NAME": cloud_name } network = "--network=host" if DEV_MODE: network = "" env_str = env_string(env_vars) vols = volume_opt(kolla_ansible_dir, "/etc/kolla") name = "arcus_mgr" shell(f"docker rm -f {name} 2>/dev/null || true") cmd = f"docker run -d --restart=always --name {name} {network} {env_str} {vols} {image}" shell(cmd)
def login(): """ Log into ECR """ ecr_data = get_ecr_config() username = ecr_data["username"] password = ecr_data["password"] server = ecr_data["url"] shell(f"docker login --username {username} --password {password} {server}", print_cmd=False)
def start( release, fqdn, rabbit_pass, rabbit_ips_list, sql_ip, sql_password, https, port, secret, ): """ Start the arcus api """ image = f"breqwatr/arcus-api:{release}" rabbit_ips_csv = ",".join(rabbit_ips_list) env_vars = { "OPENSTACK_VIP": fqdn, "PUBLIC_ENDPOINT": "true", "HTTPS_OPENSTACK_APIS": str(https).lower(), "RABBITMQ_USERNAME": "******", "RABBITMQ_PASSWORD": rabbit_pass, "RABBIT_IPS_CSV": rabbit_ips_csv, "SQL_USERNAME": "******", "SQL_PASSWORD": sql_password, "SQL_IP": sql_ip, "ARCUS_INTEGRATION_SECRET": secret, } env_str = env_string(env_vars) daemon = "-d --restart=always" run = "" dev_mount = "" ceph_mount = "" network = "--network host" log_mount = "-v /var/log/arcus-api:/var/log/arcusweb" hosts_mount = "-v /etc/hosts:/etc/hosts" if DEV_MODE: log_mount = "" hosts_mount = "" if "ARCUS_API_DIR" not in os.environ: error("ERROR: must set $ARCUS_API_DIR when $VOITHOS_DEV==true", exit=True) api_dir = os.environ["ARCUS_API_DIR"] assert_path_exists(api_dir) daemon = "-it --rm" dev_mount = volume_opt(api_dir, "/app") network = f"-p 0.0.0.0:{port}:{port}" run = ('bash -c "' "/env_config.py && " "pip install -e . && " "gunicorn --workers 4 --error-logfile=- --access-logfile '-' " "--reload " f"--bind 0.0.0.0:{port}" ' arcusapi.wsgi:app" ') name = "arcus_api" shell(f"docker rm -f {name} 2>/dev/null || true") cmd = (f"docker run --name {name} {daemon} {network} " f"{hosts_mount} {log_mount} " f"{env_str} {ceph_mount} {dev_mount} {image} {run}") shell(cmd)
def kolla_ansible_inventory(release): """ Print the inventory template for the given release """ cwd = os.getcwd() inventory_file = "/var/repos/kolla-ansible/ansible/inventory/multinode" cmd = (f"docker run --rm " f"-v {cwd}:/etc/kolla " f"breqwatr/kolla-ansible:{release} " f"cp {inventory_file} /etc/kolla/inventory") shell(cmd)
def get_package_dependencies_list(package, apt_packages_dir): """ Returns a list of package dependencies""" output = subprocess.getoutput(f'apt-rdepends {package}|grep -v "^ "') if "Unable to locate package" in output: shell(f"rm -r {apt_packages_dir}") error(f"ERROR: Unable to locate package: {package}", exit=True) dependencies = subprocess.check_output( f'apt-rdepends {package}|grep -v "^ "', shell=True).decode("utf-8") return dependencies.replace("\n", " ").split()
def show(vol_path): """ Execute qemu-img show inside a container, direct mapping the volume """ name = "qemu-img" image = "breqwatr/qemu-img:latest" path = Path(vol_path) vol_abspath = path.absolute().__str__() run = f"qemu-img info {vol_abspath}" mount = f"-v {vol_abspath}:{vol_abspath}" cmd = f"docker run --rm -it --name {name} {mount} {image} {run}" shell(cmd)
def offline_start(ip_address, port, path): """ Load and start offline registry """ if not os.path.exists(path): error(f"ERROR: Registry image not found at {path}", exit=True) else: shell(f"docker load --input {path}") # Filename from file path filename = path.rsplit("/", 1)[1] image_name_tag = filename_to_image_name_tag(filename) shell(f"docker run -d --name registry -p {ip_address}:{port}:5000 {image_name_tag}")
def kolla_ansible_generate_certificates(release, passwords_path, globals_path): """ Genereate certificates directory """ cwd = os.getcwd() globals_vol = volume_opt(globals_path, "/etc/kolla/globals.yml") password_vol = volume_opt(passwords_path, "/etc/kolla/passwords.yml") certs_vol = f"-v {cwd}/certificates:/etc/kolla/certificates" cmd = (f"docker run --rm {globals_vol} {password_vol} {certs_vol} " f"breqwatr/kolla-ansible:{release} " "kolla-ansible certificates") shell(cmd)
def kolla_ansible_genpwd(release): """ Genereate passwords.yml and print to stdout """ cwd = os.getcwd() path = "/var/repos/kolla-ansible/etc/kolla/passwords.yml" cmd = (f"docker run --rm " f"-v {cwd}:/etc/kolla " f"breqwatr/kolla-ansible:{release} " f'bash -c "kolla-genpwd --passwords {path} ' f'&& cp {path} /etc/kolla/passwords.yml"') shell(cmd)
def _sync_image(repo, release, keep, registry): """ Sync a single image to the registry """ dh_image = f"breqwatr/{repo}:{release}" local_image = f"{registry}/{dh_image}" shell(f"docker pull {dh_image}") shell(f"docker tag {dh_image} {local_image}") shell(f"docker push {local_image}") if not keep: echo(f"Deleting local images {dh_image} and {local_image}") shell(f"docker rmi {dh_image}") shell(f"docker rmi {local_image}")
def start(interface, dhcp_start, dhcp_end, release="stable"): """ Start the PXE service """ image = f"breqwatr/pxe:{release}" env_vars = { "INTERFACE": interface, "DHCP_RANGE_START": dhcp_start, "DHCP_RANGE_END": dhcp_end } env_str = env_string(env_vars) shell( f"docker run -d --name pxe --privileged --network host {env_str} {image}" )
def cli_exec(release, openrc_path, command, volume=None, debug=False): """ Execute <command> using breqwatr/openstack-client:<release> Optionally, mount file(s) into the client with the volume arg """ command = "openstack" if command is None else command mount = f"-v {volume} " if volume is not None else " " openrc_vol = volume_opt(openrc_path, "/admin-openrc.sh") image = f"breqwatr/openstack-client:{release}" run = f'bash -c "source /admin-openrc.sh && . /var/repos/env/bin/activate && {command}"' cmd = f"docker run -it --rm --network host {openrc_vol} {mount} {image} {run}" shell(cmd, print_error=False, print_cmd=debug)
def start( release, allowed_senders, rsyslog_port, ): """ Start rsyslog service """ sender_ips_cs = ", ".join(allowed_senders) image = f"breqwatr/rsyslog:{release}" daemon = "-d --restart=always" mount = "-v /var/log/networks:/var/log/networks" ports = f"-p {rsyslog_port}:514 -p {rsyslog_port}:514/udp".format(rsyslog_port) env_variable = f'-e ALLOWEDSENDERS="{sender_ips_cs}"'.format(sender_ips_cs) cmd = (f" docker run {daemon} --name rsyslog {ports} {mount} {env_variable} {image}") shell(cmd)
def kolla_ansible_get_admin_openrc(release, inventory_path, globals_path, passwords_path): """ Save the admin-openrc.sh file to current working directory """ cwd = os.getcwd() inv_vol = volume_opt(inventory_path, "/etc/kolla/inventory") globals_vol = volume_opt(globals_path, "/etc/kolla/globals.yml") passwords_vol = volume_opt(passwords_path, "/etc/kolla/passwords.yml") cwd_vol = f"-v {cwd}:/target " cmd = ("docker run --rm --network host " f"{inv_vol} {globals_vol} {passwords_vol} {cwd_vol} " f"breqwatr/kolla-ansible:{release} " 'bash -c "kolla-ansible post-deploy -i /etc/kolla/inventory && ' 'cp /etc/kolla/admin-openrc.sh /target/"') shell(cmd)
def _sync_image(image_name, tag, keep, registry, path): """ Load, tag and push offline image to registry """ image_name_tag = f"breqwatr/{image_name}:{tag}" registry_image_name_tag = f"{registry}/{image_name_tag}" image_path = path if not image_path.endswith(".docker"): image_path = util.get_image_filename_path(image_name_tag, path) if not os.path.exists(image_path): error( f"ERROR: Image path {image_path} doesn't exist", exit=False ) return shell(f"docker load --input {image_path}") shell(f"docker tag {image_name_tag} {registry_image_name_tag}") shell(f"docker push {registry_image_name_tag}") echo("Done syncing {}".format(registry_image_name_tag)) if not keep: echo(f"Deleting local images {image_name_tag} and {registry_image_name_tag}") shell(f"docker rmi {image_name_tag}") shell(f"docker rmi {registry_image_name_tag}")
def smoke_test(release, openrc, image_path, **kwargs): """ Run the smoke test """ assert_path_exists(image_path) image_vol = volume_opt(image_path, "/image.qcow2") openrc_vol = volume_opt(openrc, "/admin-openrc.sh") env_var_list = [] for kwarg in kwargs: key = kwarg.upper() value = kwargs[kwarg] var = f"-e {key}={value}" env_var_list.append(var) env_vars_str = " ".join(env_var_list) run = ('bash -c "' "source /admin-openrc.sh && " ". /var/repos/env/bin/activate && " 'bash /smoke-test.sh"') cmd = ("docker run --rm " f"{openrc_vol} {image_vol} {env_vars_str} " f"breqwatr/openstack-client:{release} {run}") shell(cmd)
def create_and_upload_offline_apt_repo_tar_file(): """Downloads apt packages and their dependencies and create Packages.gz for all downloaded packages. Then uploads them to s3. """ packages_list = OFFLINE_DEPLOYMENT_SERVER_PACKAGES path = str(Path.home()) apt_packages_dir = f"{path}/apt_packages" echo("Creating base directory: {}".format(apt_packages_dir)) os.mkdir(apt_packages_dir) # 104 is uid of _apt and 0 is gid of root os.chown(apt_packages_dir, 104, 0) os.chdir(apt_packages_dir) shell("apt-get update && apt-get install -y apt-rdepends dpkg-dev") for package in packages_list: dependencies_list = get_package_dependencies_list(package, apt_packages_dir) echo("\nDownloading {} and its dependencies".format(package)) for dependency in dependencies_list: cmd = f"apt-get download {dependency}" try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError as e: print(e) # Creating Packages.gz containing downloaded packages info shell("dpkg-scanpackages ./ /dev/null | gzip -9c > ./Packages.gz") shell("cd && tar --remove-files -zcf apt.tar.gz apt_packages") tar_file_path = f"{path}/apt.tar.gz" s3.upload(tar_file_path, "voithos-files", "apt.tar.gz")
def pull(image): """ Pull an image from ECR & strip the AWS prefix """ login() ecr_data = get_ecr_config() registry = ecr_data["registry"] ecr_image = f"{registry}/{image}" shell(f"docker pull {ecr_image}", print_cmd=False) shell(f"docker tag {ecr_image} {image}", print_cmd=False) shell(f"docker rmi {ecr_image}", print_cmd=False)
def start( release, api_ip, openstack_ip, glance_https, arcus_https=False, cert_path=None, cert_key_path=None, http_port=80, https_port=443, ): """ Start the arcus api """ image = f"breqwatr/arcus-client:{release}" env_vars = { "ARCUS_API_IP": api_ip, "ARCUS_API_PORT": "1234", "OPENSTACK_VIP": openstack_ip, "ARCUS_USE_HTTPS": arcus_https, "GLANCE_HTTPS": str(glance_https).lower(), "VERSION": release, "ARCUS_CLIENT_HTTP_PORT": http_port, "ARCUS_CLIENT_HTTPS_PORT": https_port, } env_str = env_string(env_vars) cert_vol_mounts = "" if cert_path is not None and cert_key_path is not None: cert_mount = volume_opt(cert_path, "/etc/nginx/haproxy.crt") priv_key_mount = volume_opt(cert_key_path, "/etc/nginx/haproxy.key") cert_vol_mounts = f" {cert_mount} {priv_key_mount} " daemon = "-d --restart=always" run = "" dev_mount = "" network = "--network host" log_mount = "-v /var/log/arcus-client:/var/log/nginx" hosts_mount = "-v /etc/hosts:/etc/hosts" if DEV_MODE: log_mount = "" hosts_mount = "" if "ARCUS_CLIENT_DIR" not in os.environ: error("ERROR: must set $ARCUS_CLIENT_DIR when $VOITHOS_DEV==true", exit=True) client_dir = os.environ["ARCUS_CLIENT_DIR"] assert_path_exists(client_dir) https_network = "" if cert_path is None else f"-p 0.0.0.0:{https_port}:{https_port}" network = f"-p 0.0.0.0:{http_port}:{http_port} {https_network}" run = ('bash -c "' "/env_config.py && " "npm install && " "service nginx start && " "grunt && " 'tail -f /dev/null"') sys.stdout.write( "TO REBUILD, EXECUTE:\n docker exec -it arcus_client grunt rebuild\n" ) dev_mount = volume_opt(client_dir, "/app") name = "arcus_client" shell(f"docker rm -f {name} 2>/dev/null || true") cmd = (f"docker run --name {name} " f"{daemon} {network} {env_str} " f"{cert_vol_mounts} {dev_mount} {log_mount} {hosts_mount} " f"{image} {run}") shell(cmd)
def pull(image_name_tag): """ Pull single image from bw dockerhub """ try: shell(f"docker pull {image_name_tag}") except: error(f"ERROR: Image {image_name_tag} not found", exit=False)
def kolla_ansible_exec(release, inventory_path, globals_path, passwords_path, ssh_key_path, certificates_dir, config_dir, command, tag=None, overrides=None): """ Execute kolla-ansible commands """ valid_cmds = [ "deploy", "mariadb_recovery", "prechecks", "post-deploy", "pull", "reconfigure", "upgrade", "check", "stop", "deploy-containers", "prune-images", "bootstrap-servers", "destroy", "destroy --yes-i-really-really-mean-it", "DEBUG", ] if command not in valid_cmds: error( f'ERROR: Invalid command "{command}" - Valid commands: {valid_cmds}', exit=True) config_vol = " " if config_dir is not None: config_vol = volume_opt(config_dir, "/etc/kolla/config") rm_arg = "" inv_vol = volume_opt(inventory_path, "/etc/kolla/inventory") globals_vol = volume_opt(globals_path, "/etc/kolla/globals.yml") passwd_vol = volume_opt(passwords_path, "/etc/kolla/passwords.yml") ssh_vol = volume_opt(ssh_key_path, "/root/.ssh/id_rsa") cert_vol = volume_opt(certificates_dir, "/etc/kolla/certificates") if command == "DEBUG": name = f"kolla-ansible-{release}" rm_arg = f"-d --name {name}" run_cmd = "tail -f /dev/null" shell(f"docker rm -f {name} 2>/dev/null || true") print(f"Starting persistent container named {name} for debugging") else: run_cmd = f"kolla-ansible {command} -i /etc/kolla/inventory" rm_arg = "--rm" tag_opt = "" if tag is None else f"--tag {tag}" override_vol_mnt = "" if overrides is not None: override_vol_mnt = volume_opt(overrides, '/overrides') run_cmd = f"bash -c 'cp -r overrides/* / && {run_cmd}'" cmd = ( f"docker run {rm_arg} --network host {override_vol_mnt} " "-e PY_COLORS=1 -e ANSIBLE_FORCE_COLOR=1 " f"{inv_vol} {globals_vol} {passwd_vol} {ssh_vol} {cert_vol} {config_vol}" f"breqwatr/kolla-ansible:{release} {run_cmd} {tag_opt}") shell(cmd)
def rebuild(): """ run grunt rebuild for dev mode """ shell("docker exec -it arcus_client grunt rebuild")
def start(ip_address, port, tag): """ Start the local pip container""" shell(f"docker run -d --name pip -p {ip_address}:{port}:3141 breqwatr/pip:{tag}")
def start(ip_address, port): """ Start the local registry """ shell(f"docker run -d --name registry -p {ip_address}:{port}:5000 registry:2")
def start(ip_address, port, tag): """ Start the local apt container""" shell(f"docker run -d --name apt -p {ip_address}:{port}:80 breqwatr/apt:{tag}")
def start( ip_address, port, internal_vip, control_node_ip, release="train", conf_dir="/etc/kolla/horizon", name="horizon", ): """ Generate Horizon's config files and start the container""" # TO DO: Support HTTPS by allowing mounts # ...cert: /etc/horizon/certs/horizon-cert.pem # ...key: /etc/horizon/certs/horizon-key.pem try: Path(conf_dir).mkdir(parents=True, exist_ok=True) except PermissionError: error(f"ERROR: Permission denied creating {conf_dir}. Try sudo?", exit=True) jinja2.apply_template( jinja2_file="horizon/horizon.json.j2", output_file=f"{conf_dir}/config.json", replacements={}, ) jinja2.apply_template( jinja2_file="horizon/horizon.conf.j2", output_file=f"{conf_dir}/horizon.conf", replacements={ "ip_address": ip_address, "port": port }, ) jinja2.apply_template( jinja2_file="horizon/local_settings.j2", output_file=f"{conf_dir}/local_settings", replacements={"internal_vip": internal_vip, "control_node_ip": control_node_ip}, ) jinja2.apply_template( jinja2_file="horizon/custom_local_settings.j2", output_file=f"{conf_dir}/custom_local_settings", replacements={}, ) run_cmd = ( f"docker run --name={name} " "--hostname=voithos " "--env=ENABLE_CLOUDKITTY=no " "--env=ENABLE_MANILA=no " "--env=ENABLE_SENLIN=no " "--env=ENABLE_OCTAVIA=no " "--env=ENABLE_MISTRAL=no " "--env=KOLLA_INSTALL_TYPE=binary " "--env='PS1=$(tput bold)($(printenv KOLLA_SERVICE_NAME))$(tput sgr0)[$(id -un)@$(hostname -s) $(pwd)]$ ' " "--env=ENABLE_FREEZER=no " "--env=ENABLE_HEAT=yes " "--env=KOLLA_INSTALL_METATYPE=rdo " "--env=KOLLA_DISTRO_PYTHON_VERSION=3.6 " "--env=ENABLE_FWAAS=no " "--env=KOLLA_CONFIG_STRATEGY=COPY_ALWAYS " "--env=ENABLE_SOLUM=no " "--env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin " "--env=KOLLA_BASE_ARCH=x86_64 " "--env=ENABLE_NEUTRON_VPNAAS=no " "--env=ENABLE_BLAZAR=no " "--env=ENABLE_MASAKARI=no " "--env=ENABLE_QINLING=no " "--env=ENABLE_ZUN=no " "--env=ENABLE_SAHARA=no " "--env=ENABLE_CONGRESS=no " "--env=ENABLE_IRONIC=no " "--env=ENABLE_WATCHER=no " "--env=KOLLA_SERVICE_NAME=horizon " "--env=PIP_INDEX_URL=http://mirror.bhs1.ovh.opendev.org:8080/pypi/simple " "--env=ENABLE_TROVE=no " "--env=ENABLE_VITRAGE=no " "--env=KOLLA_BASE_DISTRO=ubuntu " "--env=DEBIAN_FRONTEND=noninteractive " "--env=ENABLE_DESIGNATE=no " "--env=ENABLE_MAGNUM=no " "--env=ENABLE_TACKER=no " "--env=PIP_TRUSTED_HOST=mirror.bhs1.ovh.opendev.org " "--env=PIP_EXTRA_INDEX_URL=https://mirror.bhs1.ovh.opendev.org/wheel/ubuntu-18.04-x86_64 " "--env=ENABLE_KARBOR=no " "--env=ENABLE_SEARCHLIGHT=no " "--env=LANG=en_US.UTF-8 " "--env=ENABLE_MURANO=no " "--env=FORCE_GENERATE=no " "--volume=/etc/timezone:/etc/timezone:ro " "--volume=/tmp:/tmp:rw " "--volume=kolla_logs:/var/log/kolla/:rw " "--volume=/etc/localtime:/etc/localtime:ro " f"--volume={conf_dir}/:/var/lib/kolla/config_files/:ro " "--volume=/etc/localtime " "--volume=/etc/timezone " "--volume=/tmp " "--volume=/var/lib/kolla/config_files/ " "--volume=/var/log/kolla/ " "--network=host " "--restart=unless-stopped " "--label kolla_version=9.2.1 " "--label build-date=20200812 " "--label maintainer='Kolla Project (https://launchpad.net/kolla)' " "--label name=horizon " "--log-opt max-size=50m " "--log-opt max-file=5 " "--detach=true " f"kolla/ubuntu-binary-horizon:{release} " "kolla_start " ) shell(run_cmd)