a
This commit is contained in:
		
					parent
					
						
							
								5d8dfb2055
							
						
					
				
			
			
				commit
				
					
						495fce8782
					
				
			
		
					 3 changed files with 114 additions and 52 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
.idea/
 | 
					.idea/
 | 
				
			||||||
.vscode/
 | 
					.vscode/
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
venv/
 | 
					venv/
 | 
				
			||||||
log.txt
 | 
					log.txt
 | 
				
			||||||
etcd3_wrapper
 | 
					etcd3_wrapper
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								config.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								config.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from etcd3_wrapper import Etcd3Wrapper
 | 
				
			||||||
 | 
					from decouple import config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BASE_PATH = config("BASE_DIR", "/var/www")
 | 
				
			||||||
 | 
					WITHOUT_CEPH = config("WITHOUT_CEPH", False)
 | 
				
			||||||
 | 
					ETCD_URL = config("ETCD_URL")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logging.basicConfig(
 | 
				
			||||||
 | 
					    level=logging.DEBUG,
 | 
				
			||||||
 | 
					    filename="log.txt",
 | 
				
			||||||
 | 
					    filemode="a",
 | 
				
			||||||
 | 
					    format="%(asctime)s: %(levelname)s - %(message)s",
 | 
				
			||||||
 | 
					    datefmt="%d-%b-%y %H:%M:%S",
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client = Etcd3Wrapper(host=ETCD_URL)
 | 
				
			||||||
							
								
								
									
										141
									
								
								main.py
									
										
									
									
									
								
							
							
						
						
									
										141
									
								
								main.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,66 +1,107 @@
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from decouple import config
 | 
					from config import logging, client, BASE_PATH, WITHOUT_CEPH
 | 
				
			||||||
from etcd3_wrapper import Etcd3Wrapper
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
BASE_PATH = config("BASE_DIR")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
logging.basicConfig(
 | 
					# If you are using WITHOUT_CEPH FLAG in .env
 | 
				
			||||||
    level=logging.DEBUG,
 | 
					# then please make sure that /var/vm directory
 | 
				
			||||||
    filename="log.txt",
 | 
					# exists otherwise this script would fail
 | 
				
			||||||
    filemode="a",
 | 
					if WITHOUT_CEPH and not os.path.isdir("/var/vm"):
 | 
				
			||||||
    format="%(asctime)s: %(levelname)s - %(message)s",
 | 
					    exit(1)
 | 
				
			||||||
    datefmt="%d-%b-%y %H:%M:%S",
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
client = Etcd3Wrapper(host=config("ETCD_URL"))
 | 
					
 | 
				
			||||||
images = list(client.get_prefix("/v1/image/", value_in_json=True))
 | 
					# We want to get images entries that requests images to be created
 | 
				
			||||||
images_to_be_created = list(filter(lambda e: e.value["status"] == "TO_BE_CREATED", images))
 | 
					images = client.get_prefix("/v1/image/", value_in_json=True)
 | 
				
			||||||
 | 
					images_to_be_created = filter(lambda im: im.value["status"] == "TO_BE_CREATED", images)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for image in images_to_be_created:
 | 
					for image in images_to_be_created:
 | 
				
			||||||
    image_uuid = image.key.split("/")[-1]
 | 
					    try:
 | 
				
			||||||
    image_full_path = f"{BASE_PATH}/{image.value['owner']}/{image.value['filename']}"
 | 
					        image_uuid = image.key.split('/')[-1]
 | 
				
			||||||
    if os.path.isfile(image_full_path):
 | 
					        image_owner = image.value['owner']
 | 
				
			||||||
        output = subprocess.check_output(["qemu-img", "info", image_full_path]).decode("utf-8")
 | 
					        image_filename = image.value['filename']
 | 
				
			||||||
        if "qcow2" in output:
 | 
					        image_store_name = image.value['store_name']
 | 
				
			||||||
            logging.info("Converting it to raw")
 | 
					        image_full_path = os.path.join(BASE_PATH, image_owner, image_filename)
 | 
				
			||||||
            subprocess.run(["qemu-img", "convert", "-f", "qcow2",
 | 
					 | 
				
			||||||
                            "-O", "raw", image_full_path, f"image.raw"])
 | 
					 | 
				
			||||||
            if os.path.isfile(f"image.raw"):
 | 
					 | 
				
			||||||
                # shutil.move(f"{image_uuid}.raw", f"/var/vm/{image_uuid}.raw")
 | 
					 | 
				
			||||||
                _store_name = image.value["store_name"]
 | 
					 | 
				
			||||||
                _image_stores = client.get_prefix("/v1/image_store/", value_in_json=True)
 | 
					 | 
				
			||||||
                _user_image_store = next(filter(lambda s: s.value["name"] == _store_name, _image_stores))
 | 
					 | 
				
			||||||
                if _user_image_store:
 | 
					 | 
				
			||||||
                    _image_store_pool = _user_image_store.value["attributes"]["pool"]
 | 
					 | 
				
			||||||
                    rc = subprocess.check_call(["rbd", "import", "image.raw",
 | 
					 | 
				
			||||||
                                f"{_image_store_pool}/{image_uuid}"])
 | 
					 | 
				
			||||||
                    if rc == 0:
 | 
					 | 
				
			||||||
                        _snapshot_creation_command = f"rbd snap create {_image_store_pool}/{image_uuid}@protected"
 | 
					 | 
				
			||||||
                        subprocess.check_call(_snapshot_creation_command.split(" "))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        _snapshot_protect_command = f"rbd snap protect {_image_store_pool}/{image_uuid}@protected"
 | 
					        image_stores = client.get_prefix("/v1/image_store/", value_in_json=True)
 | 
				
			||||||
                        subprocess.check_call(_snapshot_protect_command.split(" "))
 | 
					        user_image_store = next(filter(lambda s: s.value["name"] == image_store_name, image_stores))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        image.value["status"] = "CREATED"
 | 
					        image_store_pool = user_image_store.value['attributes']['pool']
 | 
				
			||||||
                        client.put(image.key, json.dumps(image.value))
 | 
					
 | 
				
			||||||
                    else:
 | 
					    except Exception as e:
 | 
				
			||||||
                        logging.info(f"Some error occurred while creating image {image_uuid}")
 | 
					        logging.exception(e)
 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    logging.info(f"Image store {_user_image_store} not found")
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                logging.info(f"{image_uuid}.raw not found")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            logging.info("not qcow2 format")
 | 
					 | 
				
			||||||
            image.value["status"] = "INVALID_FORMAT"
 | 
					 | 
				
			||||||
            client.put(image.key, json.dumps(image.value))
 | 
					 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        logging.info("File does not exists")
 | 
					        # At least our basic data is available
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        qemu_img_info_command = ["qemu-img", "info", image_full_path]
 | 
				
			||||||
 | 
					        qemu_img_convert_command = ["qemu-img", "convert", "-f", "qcow2", "-O", "raw", image_full_path, f"image.raw"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        image_import_command = ["rbd", "import", "image.raw", f"{image_store_pool}/{image_uuid}"]
 | 
				
			||||||
 | 
					        snapshot_creation_command = ["rbd", "snap", "create", f"{image_store_pool}/{image_uuid}@protected"]
 | 
				
			||||||
 | 
					        snapshot_protect_command = ["rbd", "snap", "protect", f"{image_store_pool}/{image_uuid}@protected"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if WITHOUT_CEPH:
 | 
				
			||||||
 | 
					            image_import_command = ["mv", "image.raw", os.path.join("/var/vm", image_uuid)]
 | 
				
			||||||
 | 
					            snapshot_creation_command = ["true"]
 | 
				
			||||||
 | 
					            snapshot_protect_command = ["true"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # First check whether the image is qcow2
 | 
				
			||||||
 | 
					        # This would also check whether the file exists or not
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            qemu_img_info = subprocess.Popen(qemu_img_info_command,
 | 
				
			||||||
 | 
					                                             stdout=subprocess.PIPE,
 | 
				
			||||||
 | 
					                                             stderr=subprocess.DEVNULL)
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            # Command itself failed i.e maybe qemu-img not found
 | 
				
			||||||
 | 
					            # Maybe Popen crashes some other ways
 | 
				
			||||||
 | 
					            logging.exception(e)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # At least we run the qemu-img info command
 | 
				
			||||||
 | 
					            # It may have not executed successfully. If thats
 | 
				
			||||||
 | 
					            # the case, our next command grep would also fail
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                # Fetching the lines containing "file format"
 | 
				
			||||||
 | 
					                command = ["grep", "-e", "file format"]
 | 
				
			||||||
 | 
					                output = subprocess.check_output(command, stdin=qemu_img_info.stdout)
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                logging.exception(e)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # grep command runs successfully, it implies
 | 
				
			||||||
 | 
					                # the previous command qemu-img would have
 | 
				
			||||||
 | 
					                # run successfully
 | 
				
			||||||
 | 
					                output = output.decode("utf-8").strip()
 | 
				
			||||||
 | 
					                if "qcow2" in output:
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        # Convert .qcow2 to .raw
 | 
				
			||||||
 | 
					                        subprocess.check_output(qemu_img_convert_command)
 | 
				
			||||||
 | 
					                        print("converting")
 | 
				
			||||||
 | 
					                    except Exception as e:
 | 
				
			||||||
 | 
					                        logging.exception(e)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        # Image successfully converted
 | 
				
			||||||
 | 
					                        try:
 | 
				
			||||||
 | 
					                            # Import image either to ceph/filesystem
 | 
				
			||||||
 | 
					                            subprocess.check_output(image_import_command)
 | 
				
			||||||
 | 
					                        except Exception as e:
 | 
				
			||||||
 | 
					                            logging.exception(e)
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            # Image imported successfully
 | 
				
			||||||
 | 
					                            try:
 | 
				
			||||||
 | 
					                                subprocess.check_output(snapshot_creation_command)
 | 
				
			||||||
 | 
					                                subprocess.check_output(snapshot_protect_command)
 | 
				
			||||||
 | 
					                            except Exception as e:
 | 
				
			||||||
 | 
					                                logging.exception(e)
 | 
				
			||||||
 | 
					                            else:
 | 
				
			||||||
 | 
					                                # Everything is successfully done
 | 
				
			||||||
 | 
					                                image.value["status"] = "CREATED"
 | 
				
			||||||
 | 
					                                client.put(image.key, json.dumps(image.value))
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    # The user provided image is not in qcow2 format
 | 
				
			||||||
 | 
					                    image.value["status"] = "INVALID_FORMAT"
 | 
				
			||||||
 | 
					                    client.put(image.key, json.dumps(image.value))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        os.remove("image.raw")
 | 
					        os.remove("image.raw")
 | 
				
			||||||
    except OSError:
 | 
					    except Exception:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue