Newer
Older
#!/usr/bin/python
# -*- coding: utf-8 -*-

Arthur Le Bars
committed
import argparse
import os
import subprocess

Arthur Le Bars
committed
import sys

Arthur Le Bars
committed
Usage: $ python3 gga_init.py -i example.yml [OPTIONS]

Arthur Le Bars
committed

Arthur Le Bars
committed
class DeploySpeciesStack(speciesData.SpeciesData):

Arthur Le Bars
committed
"""

Arthur Le Bars
committed
Child of SpeciesData

Arthur Le Bars
committed
Contains methods and attributes to deploy a stack of services for a given organism, from creating/updating
the organism's directory tree to create the required docker-compose files

Arthur Le Bars
committed
"""

Arthur Le Bars
committed
def make_directory_tree(self):

Arthur Le Bars
committed
"""
Generate the directory tree for an organism and move datasets into src_data

Arthur Le Bars
committed
"""

Arthur Le Bars
committed
os.chdir(self.main_dir)

Arthur Le Bars
committed

Arthur Le Bars
committed
try:
os.mkdir(self.species_dir)

Arthur Le Bars
committed
logging.info("Making directory tree for %s" % self.full_name)

Arthur Le Bars
committed
except FileExistsError:

Arthur Le Bars
committed
logging.info("Updating directory tree for %s" % self.full_name)

Arthur Le Bars
committed
try:
os.chdir(self.species_dir)
working_dir = os.getcwd()
except OSError:
logging.critical("Cannot access " + self.species_dir + ", run with higher privileges")

Arthur Le Bars
committed
sys.exit()
try:
os.mkdir("./nginx/")
os.mkdir("./nginx/conf")
with open(os.path.abspath("./nginx/conf/default.conf"), 'w') as conf:
conf.write("server {\n\tlisten 80;\n\tserver_name ~.;\n\tlocation /download/ {\n\t\talias /project_data/; \n\t\tautoindex on;\n\t}\n}") # The species nginx conf
logging.debug("NginX conf exists")

Arthur Le Bars
committed
organism_annotation_dir, organism_genome_dir = None, None

Arthur Le Bars
committed
# Creation (or updating) of the src_data directory tree

Arthur Le Bars
committed
# Depth 0

Arthur Le Bars
committed
try:
os.mkdir("./src_data")

Arthur Le Bars
committed
except FileExistsError:
logging.debug("src_data folder already exist for %s" % self.full_name)
except PermissionError:
logging.critical("Insufficient permission to create src_data directory tree")
sys.exit()
# Depth 1
try:
os.mkdir("./src_data/annotation")
os.mkdir("./src_data/genome")
os.mkdir("./src_data/tracks")

Arthur Le Bars
committed
except FileExistsError:

Arthur Le Bars
committed
logging.debug("Depth 1 src_data folder(s) already exist for %s" % self.full_name)

Arthur Le Bars
committed
except PermissionError:
logging.critical("Insufficient permission to create src_data directory tree")
sys.exit()
# Depth 2
try:
os.mkdir("./src_data/annotation/" + self.species_folder_name)
os.mkdir("./src_data/genome/" + self.species_folder_name)

Arthur Le Bars
committed
except FileExistsError:

Arthur Le Bars
committed
logging.debug("Depth 2 src_data folder(s) already exist for %s" % self.full_name)

Arthur Le Bars
committed
except PermissionError:
logging.critical("Insufficient permission to create src_data directory tree")
sys.exit()
# Depth 3
try:
os.mkdir("./src_data/annotation/" + self.species_folder_name + "/OGS" + self.ogs_version)
os.mkdir("./src_data/genome/" + self.species_folder_name + "/v" + self.genome_version)
organism_annotation_dir = os.path.abspath("./src_data/annotation/" + self.species_folder_name + "/OGS" + self.genome_version)
organism_genome_dir = os.path.abspath("./src_data/genome/" + self.species_folder_name + "/v" + self.genome_version)

Arthur Le Bars
committed
except FileExistsError:

Arthur Le Bars
committed
logging.debug("Depth 3 src_data folder(s) already exist for %s" % self.full_name)

Arthur Le Bars
committed
except PermissionError:
logging.critical("Insufficient permission to create src_data directory tree")

Arthur Le Bars
committed
# Return to main_dir
os.chdir(self.main_dir)

Arthur Le Bars
committed
def make_compose_files(self):
"""

Arthur Le Bars
committed
Create a formatted copy of the template compose file inside a species directory tree

Arthur Le Bars
committed
:return:
"""

Arthur Le Bars
committed
os.chdir(self.main_dir)
try:
os.chdir(self.species_dir)
except OSError:
logging.critical("Cannot access " + self.species_dir)
sys.exit(0)
# Path to the templates used to generate the custom docker-compose files for an input species
stack_template_path = self.script_dir + "/templates/compose_template.yml"

Arthur Le Bars
committed
traefik_template_path = self.script_dir + "/templates/traefik.yml"
authelia_config_path = self.script_dir + "/templates/authelia_config.yml"
authelia_users_path = self.script_dir + "/templates/authelia_users.yml"
if self.sex and self.strain:

Arthur Le Bars
committed
genus_species_strain_sex = "_".join([self.genus.lower(), self.species, self.strain, self.sex])

Arthur Le Bars
committed
else:

Arthur Le Bars
committed
genus_species_strain_sex = "{0}_{1}".format(self.genus.lower(), self.species)
with open(stack_template_path, 'r') as infile:
organism_content = list()
for line in infile:

Arthur Le Bars
committed
# Replace placeholders in the compose file and append line to output

Arthur Le Bars
committed
line.replace("genus_species",
str(self.genus.lower() + "_" + self.species)).replace("Genus species",
str(self.genus_uppercase + " " + self.species)).replace("Genus/species",
str(self.genus_uppercase + "/" + self.species)).replace("gspecies",
str(self.genus.lower()[0] + self.species)).replace("genus_species_strain_sex",
genus_species_strain_sex))
# Write output compose file

Arthur Le Bars
committed
outfile.truncate(0) # Delete file content
for line in organism_content: # Replace env variables by those in the config file
for env_variable, value in self.config.items():
if env_variable in line:
line = line.replace(env_variable, value)
break

Arthur Le Bars
committed
# Call create_mounts.py (replace subprocess.DEVNULL by subprocess.PIPE to get script stdout and stderr back)
subprocess.call(["python3", self.script_dir + "/create_mounts.py"], cwd=self.species_dir,

Arthur Le Bars
committed
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Create mounts for the containers

Arthur Le Bars
committed
try:
shutil.copy(os.path.join(self.script_dir, "galaxy_data_libs_SI.py"), "../galaxy_data_libs_SI.py")
except FileExistsError:
logging.debug("galxy_data_libs_SI.py already exists")
try:
os.mkdir("../traefik")
os.mkdir("../traefik/authelia")

Arthur Le Bars
committed
shutil.copy(authelia_config_path, "../traefik/authelia/configuration.yml")

Arthur Le Bars
committed
shutil.copy(authelia_users_path, "../traefik/authelia/users.yml")
subprocess.call(["python3", self.script_dir + "/create_mounts.py"], cwd=self.species_dir,

Arthur Le Bars
committed
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Create mounts for the containers

Arthur Le Bars
committed
logging.debug("Traefik directory already exists: %s" % os.path.abspath("../traefik"))
try:
shutil.copy(traefik_template_path, "../traefik/docker-compose.yml")
except FileExistsError:

Arthur Le Bars
committed
logging.debug("Traefik compose file already exists: %s" % os.path.abspath("../traefik/docker-compose.yml"))
subprocess.call(["python3", self.script_dir + "/create_mounts.py"], cwd=self.species_dir)

Arthur Le Bars
committed

Arthur Le Bars
committed
os.chdir(self.main_dir)

Arthur Le Bars
committed
Call the script "deploy.sh" used to initiliaze the swarm cluster if needed and
launch/update the current organism's stack
This script first try to deploy the traefik stack, then deploy the organism stack, then update the traefik stack
The stacks are updated if already deployed

Arthur Le Bars
committed
# Launch and update docker stacks
# noinspection PyArgumentList
deploy_stacks_popen = subprocess.Popen(["sh", self.script_dir + "/deploy.sh", self.genus_species,
self.main_dir + "/traefik"],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
universal_newlines=True)

Arthur Le Bars
committed
for stdout_line in iter(deploy_stacks_popen.stdout.readline, ""):
if "daemon" in stdout_line: # Ignore swarm init error output
pass
else:
logging.info("\t%s" % stdout_line.strip())
deploy_stacks_popen.stdout.close()

Arthur Le Bars
committed
if __name__ == "__main__":

Arthur Le Bars
committed
parser = argparse.ArgumentParser(description="Automatic data loading in containers and interaction "
"with galaxy instances for GGA"
", following the protocol @ "
"http://gitlab.sb-roscoff.fr/abims/e-infra/gga")

Arthur Le Bars
committed
parser.add_argument("input",
type=str,
help="Input file (yml)")
parser.add_argument("-v", "--verbose",
help="Increase output verbosity",

Arthur Le Bars
committed
action="store_true")
parser.add_argument("--config",
type=str,
help="Config path, default to the 'config' file inside the script repository")
parser.add_argument("--main-directory",
type=str,
help="Where the stack containers will be located, defaults to working directory")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)

Arthur Le Bars
committed
# Parsing the config file if provided, using the default config otherwise

Arthur Le Bars
committed
if not args.config:
args.config = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "config")
else:
args.config = os.path.abspath(args.config)
if not args.main_directory:
args.main_directory = os.getcwd()
else:
args.main_directory = os.path.abspath(args.main_directory)
sp_dict_list = utilities.parse_input(os.path.abspath(args.input))
logging.info("Deploying stacks for organisms in input file %s" % args.input)
for sp_dict in sp_dict_list:

Arthur Le Bars
committed

Arthur Le Bars
committed
# Init instance
deploy_stack_for_current_organism = DeploySpeciesStack(parameters_dictionary=sp_dict)
# Setting some of the instance attributes
deploy_stack_for_current_organism.main_dir = os.getcwd()
deploy_stack_for_current_organism.species_dir = os.path.join(deploy_stack_for_current_organism.main_dir,
deploy_stack_for_current_organism.genus_species +
"/")
deploy_stack_for_current_organism.config = utilities.parse_config(args.config)
# Starting
logging.info("gga_init.py called for %s" % deploy_stack_for_current_organism.full_name)

Arthur Le Bars
committed

Arthur Le Bars
committed
# Make/update directory tree
deploy_stack_for_current_organism.make_directory_tree()
logging.info("Successfully generated the directory tree for %s" % deploy_stack_for_current_organism.full_name)

Arthur Le Bars
committed

Arthur Le Bars
committed
# Make compose files
deploy_stack_for_current_organism.make_compose_files()
logging.info("Successfully generated the docker-compose files for %s" % deploy_stack_for_current_organism.full_name)

Arthur Le Bars
committed

Arthur Le Bars
committed
# Deploy the stack
logging.info("Deploying stack for %s..." % deploy_stack_for_current_organism.full_name)
deploy_stack_for_current_organism.deploy_stack()
logging.info("Successfully deployed stack for %s" % deploy_stack_for_current_organism.full_name)

Arthur Le Bars
committed

Arthur Le Bars
committed
logging.info("Stack deployed for %s" % deploy_stack_for_current_organism.full_name)

Arthur Le Bars
committed
logging.info("All stacks deployed for organisms in input file %s" % args.input)