Fix issues in naming and few other things
This commit is contained in:
		
					parent
					
						
							
								f919719b1e
							
						
					
				
			
			
				commit
				
					
						71279a968f
					
				
			
		
					 21 changed files with 274 additions and 281 deletions
				
			
		|  | @ -1,60 +1,53 @@ | ||||||
| # This section contains default values for all other sections | [otp] | ||||||
| # | server = https://otp.ungleich.ch/ungleichotp/ | ||||||
| [DEFAULT] | verify_endpoint = verify/ | ||||||
| 
 | auth_name = replace_me | ||||||
| AUTH_NAME = "replace me" | auth_realm = replace_me | ||||||
| AUTH_SEED = "replace me" | auth_seed = replace_me | ||||||
| AUTH_REALM = "replace me" |  | ||||||
| 
 |  | ||||||
| NETWORK_PREFIX = moo |  | ||||||
| 
 |  | ||||||
| OTP_VERIFY_ENDPOINT = verify/ |  | ||||||
| 
 |  | ||||||
| [api] |  | ||||||
| NETWORK_PREFIX = foo |  | ||||||
| 
 | 
 | ||||||
| [network] | [network] | ||||||
| PREFIX_LENGTH = 64 | prefix_length = 64 | ||||||
| PREFIX = 2001:db8::/48 | prefix = 2001:db8::/48 | ||||||
|  | vxlan_phy_dev = eno1 | ||||||
| 
 | 
 | ||||||
| [netbox] | [netbox] | ||||||
| NETBOX_URL   = https://replace-me.example.com | url   = https://replace-me.example.com | ||||||
| NETBOX_TOKEN = replace me | token = replace_me | ||||||
| 
 | 
 | ||||||
| [etcd] | [etcd] | ||||||
| ETCD_URL = localhost | url = localhost | ||||||
| ETCD_PORT = 2379 | port = 2379 | ||||||
| 
 | 
 | ||||||
| CA_CERT | ca_cert | ||||||
| CERT_CERT | cert_cert | ||||||
| CERT_KEY | cert_key | ||||||
| 
 | 
 | ||||||
| 
 | file_prefix        = /files/ | ||||||
| FILE_PREFIX        = files | host_prefix        = /hosts/ | ||||||
| HOST_PREFIx        = hosts | image_prefix       = /images/ | ||||||
| IMAGE_PREFIX       = images | image_store_prefix = /imagestore/ | ||||||
| IMAGE_STORE_PREFIX = imagestore | network_prefix     = /networks/ | ||||||
| 
 | request_prefix     = /requests/ | ||||||
| NETWORK_PREFIX     = networks | user_prefix        = /users/ | ||||||
| REQUEST_PREFIX     = requests | vm_prefix          = /vms/ | ||||||
| USER_PREFIX        = users |  | ||||||
| VM_PREFIX          = vms |  | ||||||
| 
 | 
 | ||||||
| [storage] | [storage] | ||||||
|  | 
 | ||||||
| #values = filesystem, ceph | #values = filesystem, ceph | ||||||
| STORAGE_BACKEND = | backend = filesystem | ||||||
| 
 | 
 | ||||||
| # if STORAGE_BACKEND = filesystem | # if STORAGE_BACKEND = filesystem | ||||||
| VM_DIR = | vm_dir  = /var/lib/ucloud/vms | ||||||
| IMG_DIR = | image_dir = /var/lib/ucloud/images | ||||||
| 
 | 
 | ||||||
| # if STORAGE_BACKEND = ceph | # if STORAGE_BACKEND = ceph | ||||||
| CEPH_VM_POOL = | ceph_vm_pool  = ssd | ||||||
| CEPH_IMG_POOL = | ceph_image_pool = ssd | ||||||
| 
 | 
 | ||||||
| # Importing uploaded files | # Importing uploaded files | ||||||
| FILE_DIR = /var/lib/ucloud/files | file_dir = /var/lib/ucloud/files | ||||||
| 
 | 
 | ||||||
|  | # For Migrating VMs over ssh/tcp | ||||||
| [ssh] | [ssh] | ||||||
| SSH_USERNAME = | username | ||||||
| SSH_PRIVATEKEY = | private_key_path | ||||||
|  | @ -5,11 +5,17 @@ import logging | ||||||
| import importlib | import importlib | ||||||
| import sys | import sys | ||||||
| import os | import os | ||||||
|  | import multiprocessing as mp | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| COMMANDS = ['api', 'scheduler', 'host', 'filescanner', 'imagescanner', 'metadata'] | COMMANDS = ['api', 'scheduler', 'host', 'filescanner', 'imagescanner', 'metadata'] | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     log = logging.getLogger("ucloud") |     logging.basicConfig(level=logging.DEBUG, | ||||||
|  |                     format='%(pathname)s:%(lineno)d -- %(levelname)-8s %(message)s', | ||||||
|  |                     filename='/var/log/ucloud.log', filemode='a') | ||||||
|  | 
 | ||||||
|  |     logger = logging.getLogger("ucloud") | ||||||
| 
 | 
 | ||||||
|     arg_parser = argparse.ArgumentParser(prog='ucloud', |     arg_parser = argparse.ArgumentParser(prog='ucloud', | ||||||
|                                          description='Open Source Cloud Management Software') |                                          description='Open Source Cloud Management Software') | ||||||
|  | @ -22,12 +28,12 @@ if __name__ == "__main__": | ||||||
|         os.environ['UCLOUD_CONF_DIR'] = args.conf_dir |         os.environ['UCLOUD_CONF_DIR'] = args.conf_dir | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|  |         mp.set_start_method('spawn') | ||||||
|         name = args.component |         name = args.component | ||||||
|         mod = importlib.import_module("ucloud.{}.main".format(name)) |         mod = importlib.import_module("ucloud.{}.main".format(name)) | ||||||
|         main = getattr(mod, "main") |         main = getattr(mod, "main") | ||||||
| 
 |         main(*args.component_args) | ||||||
|         main() |  | ||||||
| 
 | 
 | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         logging.exception(e) |         logger.exception(e) | ||||||
|         print(e) |         print(e) | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										9
									
								
								setup.py
									
										
									
									
									
								
							|  | @ -8,8 +8,8 @@ try: | ||||||
|     version = ucloud.version.VERSION |     version = ucloud.version.VERSION | ||||||
| except: | except: | ||||||
|     import subprocess |     import subprocess | ||||||
|     c = subprocess.run(["git", "describe"], capture_output=True) |     c = subprocess.check_output(['git', 'describe']) | ||||||
|     version = c.stdout.decode("utf-8") |     version = c.decode("utf-8").strip() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| setup(name='ucloud', | setup(name='ucloud', | ||||||
|  | @ -28,8 +28,7 @@ setup(name='ucloud', | ||||||
|       packages=find_packages(), |       packages=find_packages(), | ||||||
|       install_requires=[ |       install_requires=[ | ||||||
|           'requests', |           'requests', | ||||||
|           'python-decouple', |           'Flask', | ||||||
|           'flask', |  | ||||||
|           'flask-restful', |           'flask-restful', | ||||||
|           'bitmath', |           'bitmath', | ||||||
|           'pyotp', |           'pyotp', | ||||||
|  | @ -37,8 +36,8 @@ setup(name='ucloud', | ||||||
|           'sphinx', |           'sphinx', | ||||||
|           'pynetbox', |           'pynetbox', | ||||||
|           'sphinx-rtd-theme', |           'sphinx-rtd-theme', | ||||||
|           'etcd3_wrapper @ https://code.ungleich.ch/ungleich-public/etcd3_wrapper/repository/master/archive.tar.gz#egg=etcd3_wrapper', |  | ||||||
|           'etcd3 @ https://github.com/kragniz/python-etcd3/tarball/master#egg=etcd3', |           'etcd3 @ https://github.com/kragniz/python-etcd3/tarball/master#egg=etcd3', | ||||||
|       ], |       ], | ||||||
|       scripts=['scripts/ucloud'], |       scripts=['scripts/ucloud'], | ||||||
|  |       data_files=[('/etc/ucloud/', ['conf/ucloud.conf'])], | ||||||
|       zip_safe=False) |       zip_safe=False) | ||||||
|  |  | ||||||
|  | @ -47,6 +47,6 @@ class VmUUIDField(Field): | ||||||
|         self.validation = self.vm_uuid_validation |         self.validation = self.vm_uuid_validation | ||||||
| 
 | 
 | ||||||
|     def vm_uuid_validation(self): |     def vm_uuid_validation(self): | ||||||
|         r = etcd_client.get(os.path.join(config['api']['VM_PREFIX'], self.uuid)) |         r = etcd_client.get(os.path.join(config['etcd']['vm_prefix'], self.uuid)) | ||||||
|         if not r: |         if not r: | ||||||
|             self.add_error("VM with uuid {} does not exists".format(self.uuid)) |             self.add_error("VM with uuid {} does not exists".format(self.uuid)) | ||||||
|  |  | ||||||
|  | @ -13,4 +13,4 @@ data = { | ||||||
|     "attributes": {"list": [], "key": [], "pool": "images"}, |     "attributes": {"list": [], "key": [], "pool": "images"}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| etcd_client.put(os.path.join(config['api']['IMAGE_STORE_PREFIX'], uuid4().hex), json.dumps(data)) | etcd_client.put(os.path.join(config['etcd']['image_store_prefix'], uuid4().hex), json.dumps(data)) | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import binascii | ||||||
| import ipaddress | import ipaddress | ||||||
| import random | import random | ||||||
| import subprocess as sp | import subprocess as sp | ||||||
| 
 | import logging | ||||||
| import requests | import requests | ||||||
| 
 | 
 | ||||||
| from pyotp import TOTP | from pyotp import TOTP | ||||||
|  | @ -10,23 +10,28 @@ from pyotp import TOTP | ||||||
| from ucloud.config import vm_pool, config | from ucloud.config import vm_pool, config | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | logger = logging.getLogger("ucloud.api.helper") | ||||||
|  | 
 | ||||||
| def check_otp(name, realm, token): | def check_otp(name, realm, token): | ||||||
|     try: |     try: | ||||||
|         data = { |         data = { | ||||||
|             "auth_name": config['api']["AUTH_NAME"], |             "auth_name": config['otp']['auth_name'], | ||||||
|             "auth_token": TOTP(config['api']["AUTH_SEED"]).now(), |             "auth_token": TOTP(config['otp']['auth_seed']).now(), | ||||||
|             "auth_realm": config['api']["AUTH_REALM"], |             "auth_realm": config['otp']['auth_realm'], | ||||||
|             "name": name, |             "name": name, | ||||||
|             "realm": realm, |             "realm": realm, | ||||||
|             "token": token, |             "token": token, | ||||||
|         } |         } | ||||||
|     except binascii.Error: |     except binascii.Error as err: | ||||||
|  |         logger.error( | ||||||
|  |             "Cannot compute OTP for seed: {}".format(config['otp']['auth_seed']) | ||||||
|  |         ) | ||||||
|         return 400 |         return 400 | ||||||
| 
 | 
 | ||||||
|     response = requests.post( |     response = requests.post( | ||||||
|         "{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format( |         "{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format( | ||||||
|             OTP_SERVER=config['api']["OTP_SERVER"], |             OTP_SERVER=config['otp']['server'], | ||||||
|             OTP_VERIFY_ENDPOINT=config['api']["OTP_VERIFY_ENDPOINT"] |             OTP_VERIFY_ENDPOINT=config['otp']['verify_endpoint'] | ||||||
|         ), |         ), | ||||||
|         json=data, |         json=data, | ||||||
|     ) |     ) | ||||||
|  | @ -80,7 +85,7 @@ def resolve_image_name(name, etcd_client): | ||||||
|     except Exception: |     except Exception: | ||||||
|         raise ValueError("Image name not in correct format i.e {store_name}:{image_name}") |         raise ValueError("Image name not in correct format i.e {store_name}:{image_name}") | ||||||
| 
 | 
 | ||||||
|     images = etcd_client.get_prefix(config['api']['IMAGE_PREFIX'], value_in_json=True) |     images = etcd_client.get_prefix(config['etcd']['image_prefix'], value_in_json=True) | ||||||
| 
 | 
 | ||||||
|     # Try to find image with name == image_name and store_name == store_name |     # Try to find image with name == image_name and store_name == store_name | ||||||
|     try: |     try: | ||||||
|  |  | ||||||
|  | @ -10,7 +10,10 @@ from flask_restful import Resource, Api | ||||||
| from ucloud.common import counters | from ucloud.common import counters | ||||||
| from ucloud.common.vm import VMStatus | from ucloud.common.vm import VMStatus | ||||||
| from ucloud.common.request import RequestEntry, RequestType | from ucloud.common.request import RequestEntry, RequestType | ||||||
| from ucloud.config import (etcd_client, request_pool, vm_pool, host_pool, config, image_storage_handler) | from ucloud.config import ( | ||||||
|  |     etcd_client, request_pool, vm_pool, | ||||||
|  |     host_pool, config, image_storage_handler | ||||||
|  | ) | ||||||
| from . import schemas | from . import schemas | ||||||
| from .helper import generate_mac, mac2ipv6 | from .helper import generate_mac, mac2ipv6 | ||||||
| from . import logger | from . import logger | ||||||
|  | @ -28,7 +31,7 @@ class CreateVM(Resource): | ||||||
|         validator = schemas.CreateVMSchema(data) |         validator = schemas.CreateVMSchema(data) | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             vm_uuid = uuid4().hex |             vm_uuid = uuid4().hex | ||||||
|             vm_key = join_path(config['etcd']["VM_PREFIX"], vm_uuid) |             vm_key = join_path(config['etcd']['vm_prefix'], vm_uuid) | ||||||
|             specs = { |             specs = { | ||||||
|                 "cpu": validator.specs["cpu"], |                 "cpu": validator.specs["cpu"], | ||||||
|                 "ram": validator.specs["ram"], |                 "ram": validator.specs["ram"], | ||||||
|  | @ -56,7 +59,7 @@ class CreateVM(Resource): | ||||||
|             # Create ScheduleVM Request |             # Create ScheduleVM Request | ||||||
|             r = RequestEntry.from_scratch( |             r = RequestEntry.from_scratch( | ||||||
|                 type=RequestType.ScheduleVM, uuid=vm_uuid, |                 type=RequestType.ScheduleVM, uuid=vm_uuid, | ||||||
|                 request_prefix=config['etcd']["REQUEST_PREFIX"] |                 request_prefix=config['etcd']['request_prefix'] | ||||||
|             ) |             ) | ||||||
|             request_pool.put(r) |             request_pool.put(r) | ||||||
| 
 | 
 | ||||||
|  | @ -71,7 +74,7 @@ class VmStatus(Resource): | ||||||
|         validator = schemas.VMStatusSchema(data) |         validator = schemas.VMStatusSchema(data) | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             vm = vm_pool.get( |             vm = vm_pool.get( | ||||||
|                 join_path(config['etcd']["VM_PREFIX"], data["uuid"]) |                 join_path(config['etcd']['vm_prefix'], data["uuid"]) | ||||||
|             ) |             ) | ||||||
|             vm_value = vm.value.copy() |             vm_value = vm.value.copy() | ||||||
|             vm_value["ip"] = [] |             vm_value["ip"] = [] | ||||||
|  | @ -79,7 +82,7 @@ class VmStatus(Resource): | ||||||
|                 network_name, mac, tap = network_mac_and_tap |                 network_name, mac, tap = network_mac_and_tap | ||||||
|                 network = etcd_client.get( |                 network = etcd_client.get( | ||||||
|                     join_path( |                     join_path( | ||||||
|                         config['etcd']["NETWORK_PREFIX"], |                         config['etcd']['network_prefix'], | ||||||
|                         data["name"], |                         data["name"], | ||||||
|                         network_name, |                         network_name, | ||||||
|                     ), |                     ), | ||||||
|  | @ -100,7 +103,7 @@ class CreateImage(Resource): | ||||||
|         validator = schemas.CreateImageSchema(data) |         validator = schemas.CreateImageSchema(data) | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             file_entry = etcd_client.get( |             file_entry = etcd_client.get( | ||||||
|                 join_path(config['etcd']["FILE_PREFIX"], data["uuid"]) |                 join_path(config['etcd']['file_prefix'], data["uuid"]) | ||||||
|             ) |             ) | ||||||
|             file_entry_value = json.loads(file_entry.value) |             file_entry_value = json.loads(file_entry.value) | ||||||
| 
 | 
 | ||||||
|  | @ -113,7 +116,7 @@ class CreateImage(Resource): | ||||||
|                 "visibility": "public", |                 "visibility": "public", | ||||||
|             } |             } | ||||||
|             etcd_client.put( |             etcd_client.put( | ||||||
|                 join_path(config['etcd']["IMAGE_PREFIX"], data["uuid"]), |                 join_path(config['etcd']['image_prefix'], data["uuid"]), | ||||||
|                 json.dumps(image_entry_json), |                 json.dumps(image_entry_json), | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | @ -125,7 +128,7 @@ class ListPublicImages(Resource): | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def get(): |     def get(): | ||||||
|         images = etcd_client.get_prefix( |         images = etcd_client.get_prefix( | ||||||
|             config['etcd']["IMAGE_PREFIX"], value_in_json=True |             config['etcd']['image_prefix'], value_in_json=True | ||||||
|         ) |         ) | ||||||
|         r = { |         r = { | ||||||
|             "images": [] |             "images": [] | ||||||
|  | @ -148,7 +151,7 @@ class VMAction(Resource): | ||||||
| 
 | 
 | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             vm_entry = vm_pool.get( |             vm_entry = vm_pool.get( | ||||||
|                 join_path(config['etcd']["VM_PREFIX"], data["uuid"]) |                 join_path(config['etcd']['vm_prefix'], data["uuid"]) | ||||||
|             ) |             ) | ||||||
|             action = data["action"] |             action = data["action"] | ||||||
| 
 | 
 | ||||||
|  | @ -172,7 +175,7 @@ class VMAction(Resource): | ||||||
|                 type="{}VM".format(action.title()), |                 type="{}VM".format(action.title()), | ||||||
|                 uuid=data["uuid"], |                 uuid=data["uuid"], | ||||||
|                 hostname=vm_entry.hostname, |                 hostname=vm_entry.hostname, | ||||||
|                 request_prefix=config['etcd']["REQUEST_PREFIX"] |                 request_prefix=config['etcd']['request_prefix'] | ||||||
|             ) |             ) | ||||||
|             request_pool.put(r) |             request_pool.put(r) | ||||||
|             return {"message": "VM {} Queued".format(action.title())}, 200 |             return {"message": "VM {} Queued".format(action.title())}, 200 | ||||||
|  | @ -193,10 +196,10 @@ class VMMigration(Resource): | ||||||
|                 type=RequestType.ScheduleVM, |                 type=RequestType.ScheduleVM, | ||||||
|                 uuid=vm.uuid, |                 uuid=vm.uuid, | ||||||
|                 destination=join_path( |                 destination=join_path( | ||||||
|                     config['etcd']["HOST_PREFIX"], validator.destination.value |                     config['etcd']['host_prefix'], validator.destination.value | ||||||
|                 ), |                 ), | ||||||
|                 migration=True, |                 migration=True, | ||||||
|                 request_prefix=config['etcd']["REQUEST_PREFIX"] |                 request_prefix=config['etcd']['request_prefix'] | ||||||
|             ) |             ) | ||||||
|             request_pool.put(r) |             request_pool.put(r) | ||||||
|             return {"message": "VM Migration Initialization Queued"}, 200 |             return {"message": "VM Migration Initialization Queued"}, 200 | ||||||
|  | @ -212,7 +215,7 @@ class ListUserVM(Resource): | ||||||
| 
 | 
 | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             vms = etcd_client.get_prefix( |             vms = etcd_client.get_prefix( | ||||||
|                 config['etcd']["VM_PREFIX"], value_in_json=True |                 config['etcd']['vm_prefix'], value_in_json=True | ||||||
|             ) |             ) | ||||||
|             return_vms = [] |             return_vms = [] | ||||||
|             user_vms = filter(lambda v: v.value["owner"] == data["name"], vms) |             user_vms = filter(lambda v: v.value["owner"] == data["name"], vms) | ||||||
|  | @ -246,7 +249,7 @@ class ListUserFiles(Resource): | ||||||
| 
 | 
 | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             files = etcd_client.get_prefix( |             files = etcd_client.get_prefix( | ||||||
|                 config['etcd']["FILE_PREFIX"], value_in_json=True |                 config['etcd']['file_prefix'], value_in_json=True | ||||||
|             ) |             ) | ||||||
|             return_files = [] |             return_files = [] | ||||||
|             user_files = list( |             user_files = list( | ||||||
|  | @ -270,7 +273,7 @@ class CreateHost(Resource): | ||||||
|         data = request.json |         data = request.json | ||||||
|         validator = schemas.CreateHostSchema(data) |         validator = schemas.CreateHostSchema(data) | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             host_key = join_path(config['etcd']["HOST_PREFIX"], uuid4().hex) |             host_key = join_path(config['etcd']['host_prefix'], uuid4().hex) | ||||||
|             host_entry = { |             host_entry = { | ||||||
|                 "specs": data["specs"], |                 "specs": data["specs"], | ||||||
|                 "hostname": data["hostname"], |                 "hostname": data["hostname"], | ||||||
|  | @ -309,7 +312,7 @@ class GetSSHKeys(Resource): | ||||||
| 
 | 
 | ||||||
|                 # {user_prefix}/{realm}/{name}/key/ |                 # {user_prefix}/{realm}/{name}/key/ | ||||||
|                 etcd_key = join_path( |                 etcd_key = join_path( | ||||||
|                     config['etcd']['USER_PREFIX'], |                     config['etcd']['user_prefix'], | ||||||
|                     data["realm"], |                     data["realm"], | ||||||
|                     data["name"], |                     data["name"], | ||||||
|                     "key", |                     "key", | ||||||
|  | @ -326,7 +329,7 @@ class GetSSHKeys(Resource): | ||||||
| 
 | 
 | ||||||
|                 # {user_prefix}/{realm}/{name}/key/{key_name} |                 # {user_prefix}/{realm}/{name}/key/{key_name} | ||||||
|                 etcd_key = join_path( |                 etcd_key = join_path( | ||||||
|                     config['etcd']['USER_PREFIX'], |                     config['etcd']['user_prefix'], | ||||||
|                     data["realm"], |                     data["realm"], | ||||||
|                     data["name"], |                     data["name"], | ||||||
|                     "key", |                     "key", | ||||||
|  | @ -355,7 +358,7 @@ class AddSSHKey(Resource): | ||||||
| 
 | 
 | ||||||
|             # {user_prefix}/{realm}/{name}/key/{key_name} |             # {user_prefix}/{realm}/{name}/key/{key_name} | ||||||
|             etcd_key = join_path( |             etcd_key = join_path( | ||||||
|                 config['etcd']["USER_PREFIX"], |                 config['etcd']['user_prefix'], | ||||||
|                 data["realm"], |                 data["realm"], | ||||||
|                 data["name"], |                 data["name"], | ||||||
|                 "key", |                 "key", | ||||||
|  | @ -385,7 +388,7 @@ class RemoveSSHKey(Resource): | ||||||
| 
 | 
 | ||||||
|             # {user_prefix}/{realm}/{name}/key/{key_name} |             # {user_prefix}/{realm}/{name}/key/{key_name} | ||||||
|             etcd_key = join_path( |             etcd_key = join_path( | ||||||
|                 config['etcd']["USER_PREFIX"], |                 config['etcd']['user_prefix'], | ||||||
|                 data["realm"], |                 data["realm"], | ||||||
|                 data["name"], |                 data["name"], | ||||||
|                 "key", |                 "key", | ||||||
|  | @ -420,31 +423,35 @@ class CreateNetwork(Resource): | ||||||
|                 "type": data["type"], |                 "type": data["type"], | ||||||
|             } |             } | ||||||
|             if validator.user.value: |             if validator.user.value: | ||||||
|                 nb = pynetbox.api( |                 try: | ||||||
|                     url=config['netbox']["NETBOX_URL"], |                     nb = pynetbox.api( | ||||||
|                     token=config['netbox']["NETBOX_TOKEN"], |                         url=config['netbox']['url'], | ||||||
|                 ) |                         token=config['netbox']['token'], | ||||||
|                 nb_prefix = nb.ipam.prefixes.get( |                     ) | ||||||
|                     prefix=config['network']["PREFIX"] |                     nb_prefix = nb.ipam.prefixes.get( | ||||||
|                 ) |                         prefix=config['network']['prefix'] | ||||||
| 
 |                     ) | ||||||
|                 prefix = nb_prefix.available_prefixes.create( |                     prefix = nb_prefix.available_prefixes.create( | ||||||
|                     data={ |                         data={ | ||||||
|                         "prefix_length": config['network']["PREFIX_LENGTH"], |                             "prefix_length": int(config['network']['prefix_length']), | ||||||
|                         "description": '{}\'s network "{}"'.format( |                             "description": '{}\'s network "{}"'.format( | ||||||
|                             data["name"], data["network_name"] |                                 data["name"], data["network_name"] | ||||||
|                         ), |                             ), | ||||||
|                         "is_pool": True, |                             "is_pool": True, | ||||||
|                     } |                         } | ||||||
|                 ) |                     ) | ||||||
|                 network_entry["ipv6"] = prefix["prefix"] |                 except Exception: | ||||||
|  |                     logger.exception("Exception occur while contacting netbox") | ||||||
|  |                     return {"message": "Error occured while creating network."} | ||||||
|  |                 else: | ||||||
|  |                     network_entry["ipv6"] = prefix["prefix"] | ||||||
|             else: |             else: | ||||||
|                 network_entry["ipv6"] = "fd00::/64" |                 network_entry["ipv6"] = "fd00::/64" | ||||||
| 
 | 
 | ||||||
|             network_key = join_path( |             network_key = join_path( | ||||||
|                 config['network']["NETWORK_PREFIX"], |                 config['etcd']['network_prefix'], | ||||||
|                 data["name"], |                 data['name'], | ||||||
|                 data["network_name"], |                 data['network_name'], | ||||||
|             ) |             ) | ||||||
|             etcd_client.put(network_key, network_entry, value_in_json=True) |             etcd_client.put(network_key, network_entry, value_in_json=True) | ||||||
|             return {"message": "Network successfully added."} |             return {"message": "Network successfully added."} | ||||||
|  | @ -460,7 +467,7 @@ class ListUserNetwork(Resource): | ||||||
| 
 | 
 | ||||||
|         if validator.is_valid(): |         if validator.is_valid(): | ||||||
|             prefix = join_path( |             prefix = join_path( | ||||||
|                 config['network']["NETWORK_PREFIX"], data["name"] |                 config['etcd']['network_prefix'], data["name"] | ||||||
|             ) |             ) | ||||||
|             networks = etcd_client.get_prefix(prefix, value_in_json=True) |             networks = etcd_client.get_prefix(prefix, value_in_json=True) | ||||||
|             user_networks = [] |             user_networks = [] | ||||||
|  | @ -496,7 +503,7 @@ api.add_resource(CreateNetwork, "/network/create") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def main(): | def main(): | ||||||
|     image_stores = list(etcd_client.get_prefix(config['etcd']['IMAGE_STORE_PREFIX'], value_in_json=True)) |     image_stores = list(etcd_client.get_prefix(config['etcd']['image_store_prefix'], value_in_json=True)) | ||||||
|     if len(image_stores) == 0: |     if len(image_stores) == 0: | ||||||
|         data = { |         data = { | ||||||
|             "is_public": True, |             "is_public": True, | ||||||
|  | @ -506,7 +513,7 @@ def main(): | ||||||
|             "attributes": {"list": [], "key": [], "pool": "images"}, |             "attributes": {"list": [], "key": [], "pool": "images"}, | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         etcd_client.put(join_path(config['etcd']['IMAGE_STORE_PREFIX'], uuid4().hex), json.dumps(data)) |         etcd_client.put(join_path(config['etcd']['image_store_prefix'], uuid4().hex), json.dumps(data)) | ||||||
| 
 | 
 | ||||||
|     app.run(host="::", debug=True) |     app.run(host="::", debug=True) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ import bitmath | ||||||
| from ucloud.common.host import HostStatus | from ucloud.common.host import HostStatus | ||||||
| from ucloud.common.vm import VMStatus | from ucloud.common.vm import VMStatus | ||||||
| from ucloud.config import etcd_client, config, vm_pool, host_pool | from ucloud.config import etcd_client, config, vm_pool, host_pool | ||||||
| from . import helper | from . import helper, logger | ||||||
| from .common_fields import Field, VmUUIDField | from .common_fields import Field, VmUUIDField | ||||||
| from .helper import check_otp, resolve_vm_name | from .helper import check_otp, resolve_vm_name | ||||||
| 
 | 
 | ||||||
|  | @ -102,14 +102,14 @@ class CreateImageSchema(BaseSchema): | ||||||
|         super().__init__(data, fields) |         super().__init__(data, fields) | ||||||
| 
 | 
 | ||||||
|     def file_uuid_validation(self): |     def file_uuid_validation(self): | ||||||
|         file_entry = etcd_client.get(os.path.join(config['etcd']['FILE_PREFIX'], self.uuid.value)) |         file_entry = etcd_client.get(os.path.join(config['etcd']['file_prefix'], self.uuid.value)) | ||||||
|         if file_entry is None: |         if file_entry is None: | ||||||
|             self.add_error( |             self.add_error( | ||||||
|                 "Image File with uuid '{}' Not Found".format(self.uuid.value) |                 "Image File with uuid '{}' Not Found".format(self.uuid.value) | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     def image_store_name_validation(self): |     def image_store_name_validation(self): | ||||||
|         image_stores = list(etcd_client.get_prefix(config['etcd']['IMAGE_STORE_PREFIX'])) |         image_stores = list(etcd_client.get_prefix(config['etcd']['image_store_prefix'])) | ||||||
| 
 | 
 | ||||||
|         image_store = next( |         image_store = next( | ||||||
|             filter( |             filter( | ||||||
|  | @ -220,6 +220,7 @@ class CreateVMSchema(OTPSchema): | ||||||
|         try: |         try: | ||||||
|             image_uuid = helper.resolve_image_name(self.image.value, etcd_client) |             image_uuid = helper.resolve_image_name(self.image.value, etcd_client) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|  |             logger.exception("Cannot resolve image name = %s", self.image.value) | ||||||
|             self.add_error(str(e)) |             self.add_error(str(e)) | ||||||
|         else: |         else: | ||||||
|             self.image_uuid = image_uuid |             self.image_uuid = image_uuid | ||||||
|  | @ -235,7 +236,7 @@ class CreateVMSchema(OTPSchema): | ||||||
| 
 | 
 | ||||||
|         if _network: |         if _network: | ||||||
|             for net in _network: |             for net in _network: | ||||||
|                 network = etcd_client.get(os.path.join(config['etcd']['NETWORK_PREFIX'], |                 network = etcd_client.get(os.path.join(config['etcd']['network_prefix'], | ||||||
|                                                        self.name.value, |                                                        self.name.value, | ||||||
|                                                        net), value_in_json=True) |                                                        net), value_in_json=True) | ||||||
|                 if not network: |                 if not network: | ||||||
|  | @ -400,7 +401,7 @@ class VmMigrationSchema(OTPSchema): | ||||||
|         if vm.status != VMStatus.running: |         if vm.status != VMStatus.running: | ||||||
|             self.add_error("Can't migrate non-running VM") |             self.add_error("Can't migrate non-running VM") | ||||||
| 
 | 
 | ||||||
|         if vm.hostname == os.path.join(config['etcd']['HOST_PREFIX'], self.destination.value): |         if vm.hostname == os.path.join(config['etcd']['host_prefix'], self.destination.value): | ||||||
|             self.add_error("Destination host couldn't be same as Source Host") |             self.add_error("Destination host couldn't be same as Source Host") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -442,7 +443,7 @@ class CreateNetwork(OTPSchema): | ||||||
|         super().__init__(data, fields=fields) |         super().__init__(data, fields=fields) | ||||||
| 
 | 
 | ||||||
|     def network_name_validation(self): |     def network_name_validation(self): | ||||||
|         network = etcd_client.get(os.path.join(config['etcd']['NETWORK_PREFIX'], |         network = etcd_client.get(os.path.join(config['etcd']['network_prefix'], | ||||||
|                                                self.name.value, |                                                self.name.value, | ||||||
|                                                self.network_name.value), |                                                self.network_name.value), | ||||||
|                                   value_in_json=True) |                                   value_in_json=True) | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| from etcd3_wrapper import EtcdEntry | from .etcd_wrapper import EtcdEntry | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SpecificEtcdEntryBase: | class SpecificEtcdEntryBase: | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| from etcd3_wrapper import Etcd3Wrapper | from .etcd_wrapper import Etcd3Wrapper | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def increment_etcd_counter(etcd_client: Etcd3Wrapper, key): | def increment_etcd_counter(etcd_client: Etcd3Wrapper, key): | ||||||
|  |  | ||||||
							
								
								
									
										74
									
								
								ucloud/common/etcd_wrapper.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								ucloud/common/etcd_wrapper.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | import etcd3 | ||||||
|  | import json | ||||||
|  | import queue | ||||||
|  | import copy | ||||||
|  | 
 | ||||||
|  | from collections import namedtuple | ||||||
|  | 
 | ||||||
|  | PseudoEtcdMeta = namedtuple("PseudoEtcdMeta", ["key"]) | ||||||
|  | 
 | ||||||
|  | class EtcdEntry: | ||||||
|  |     # key: str | ||||||
|  |     # value: str | ||||||
|  | 
 | ||||||
|  |     def __init__(self, meta, value, value_in_json=False): | ||||||
|  |         self.key = meta.key.decode("utf-8") | ||||||
|  |         self.value = value.decode("utf-8") | ||||||
|  | 
 | ||||||
|  |         if value_in_json: | ||||||
|  |             self.value = json.loads(self.value) | ||||||
|  | 
 | ||||||
|  | class Etcd3Wrapper: | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         self.client = etcd3.client(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |     def get(self, *args, value_in_json=False, **kwargs): | ||||||
|  |         _value, _key = self.client.get(*args, **kwargs) | ||||||
|  |         if _key is None or _value is None: | ||||||
|  |             return None | ||||||
|  |         return EtcdEntry(_key, _value, value_in_json=value_in_json) | ||||||
|  | 
 | ||||||
|  |     def put(self, *args, value_in_json=False, **kwargs): | ||||||
|  |         _key, _value = args | ||||||
|  |         if value_in_json: | ||||||
|  |             _value = json.dumps(_value) | ||||||
|  | 
 | ||||||
|  |         if not isinstance(_key, str): | ||||||
|  |             _key = _key.decode("utf-8") | ||||||
|  | 
 | ||||||
|  |         return self.client.put(_key, _value, **kwargs) | ||||||
|  | 
 | ||||||
|  |     def get_prefix(self, *args, value_in_json=False, **kwargs): | ||||||
|  |         r = self.client.get_prefix(*args, **kwargs) | ||||||
|  |         for entry in r: | ||||||
|  |             e = EtcdEntry(*entry[::-1], value_in_json=value_in_json) | ||||||
|  |             if e.value: | ||||||
|  |                 yield e | ||||||
|  | 
 | ||||||
|  |     def watch_prefix(self, key, timeout=0, value_in_json=False): | ||||||
|  |         timeout_event = EtcdEntry(PseudoEtcdMeta(key=b"TIMEOUT"), | ||||||
|  |                                   value=str.encode(json.dumps({"status": "TIMEOUT", | ||||||
|  |                                                                "type": "TIMEOUT"})), | ||||||
|  |                                   value_in_json=value_in_json) | ||||||
|  | 
 | ||||||
|  |         event_queue = queue.Queue() | ||||||
|  | 
 | ||||||
|  |         def add_event_to_queue(event): | ||||||
|  |             for e in event.events: | ||||||
|  |                 if e.value: | ||||||
|  |                     event_queue.put(EtcdEntry(e, e.value, value_in_json=value_in_json)) | ||||||
|  | 
 | ||||||
|  |         self.client.add_watch_prefix_callback(key, add_event_to_queue) | ||||||
|  | 
 | ||||||
|  |         while True: | ||||||
|  |             try: | ||||||
|  |                 while True: | ||||||
|  |                     v = event_queue.get(timeout=timeout) | ||||||
|  |                     yield v | ||||||
|  |             except queue.Empty: | ||||||
|  |                 event_queue.put(copy.deepcopy(timeout_event)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class PsuedoEtcdEntry(EtcdEntry): | ||||||
|  |     def __init__(self, key, value, value_in_json=False): | ||||||
|  |         super().__init__(PseudoEtcdMeta(key=key.encode("utf-8")), value, value_in_json=value_in_json) | ||||||
|  | @ -6,21 +6,7 @@ import json | ||||||
| from ipaddress import ip_address | from ipaddress import ip_address | ||||||
| 
 | 
 | ||||||
| from os.path import join as join_path | from os.path import join as join_path | ||||||
| 
 | from . import logger | ||||||
| 
 |  | ||||||
| def create_package_loggers(packages, base_path, mode="a"): |  | ||||||
|     loggers = {} |  | ||||||
|     for pkg in packages: |  | ||||||
|         logger = logging.getLogger(pkg) |  | ||||||
|         logger_handler = logging.FileHandler( |  | ||||||
|             join_path(base_path, "{}.txt".format(pkg)), |  | ||||||
|             mode=mode |  | ||||||
|         ) |  | ||||||
|         logger.setLevel(logging.DEBUG) |  | ||||||
|         logger_handler.setFormatter(logging.Formatter(fmt="%(asctime)s: %(levelname)s - %(message)s", |  | ||||||
|                                                       datefmt="%d-%b-%y %H:%M:%S")) |  | ||||||
|         logger.addHandler(logger_handler) |  | ||||||
|         loggers[pkg] = logger |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # TODO: Should be removed as soon as migration | # TODO: Should be removed as soon as migration | ||||||
|  | @ -35,7 +21,7 @@ def get_ipv4_address(): | ||||||
|         except socket.timeout: |         except socket.timeout: | ||||||
|             address = "127.0.0.1" |             address = "127.0.0.1" | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             logging.getLogger().exception(e) |             logger.exception(e) | ||||||
|             address = "127.0.0.1" |             address = "127.0.0.1" | ||||||
|         else: |         else: | ||||||
|             address = s.getsockname()[0] |             address = s.getsockname()[0] | ||||||
|  | @ -49,6 +35,6 @@ def get_ipv6_address(): | ||||||
|         content = json.loads(r.content.decode("utf-8")) |         content = json.loads(r.content.decode("utf-8")) | ||||||
|         ip = ip_address(content["ip"]).exploded |         ip = ip_address(content["ip"]).exploded | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         logging.exception(e) |         logger.exception(e) | ||||||
|     else: |     else: | ||||||
|         return ip |         return ip | ||||||
|  |  | ||||||
|  | @ -2,8 +2,7 @@ import json | ||||||
| from os.path import join | from os.path import join | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
| 
 | 
 | ||||||
| from etcd3_wrapper.etcd3_wrapper import PsuedoEtcdEntry | from .etcd_wrapper import PsuedoEtcdEntry | ||||||
| 
 |  | ||||||
| from .classes import SpecificEtcdEntryBase | from .classes import SpecificEtcdEntryBase | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										144
									
								
								ucloud/config.py
									
										
									
									
									
								
							
							
						
						
									
										144
									
								
								ucloud/config.py
									
										
									
									
									
								
							|  | @ -1,133 +1,53 @@ | ||||||
|  | import configparser | ||||||
|  | import os | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
| from ucloud.common.host import HostPool | from ucloud.common.host import HostPool | ||||||
| from ucloud.common.request import RequestPool | from ucloud.common.request import RequestPool | ||||||
| from ucloud.common.vm import VmPool | from ucloud.common.vm import VmPool | ||||||
| from ucloud.common.storage_handlers import FileSystemBasedImageStorageHandler, CEPHBasedImageStorageHandler | from ucloud.common.storage_handlers import FileSystemBasedImageStorageHandler, CEPHBasedImageStorageHandler | ||||||
|  | from ucloud.common.etcd_wrapper import Etcd3Wrapper | ||||||
| 
 | 
 | ||||||
| # Replacing decouple inline | log = logging.getLogger('ucloud.config') | ||||||
| import configparser |  | ||||||
| import os |  | ||||||
| import os.path |  | ||||||
| 
 |  | ||||||
| import logging |  | ||||||
| log = logging.getLogger("ucloud.config") |  | ||||||
| 
 |  | ||||||
| conf_name = "ucloud.conf" |  | ||||||
| 
 |  | ||||||
| try: |  | ||||||
|     conf_dir = os.environ["UCLOUD_CONF_DIR"] |  | ||||||
| except KeyError: |  | ||||||
|     conf_dir = "/etc/ucloud" |  | ||||||
| 
 | 
 | ||||||
|  | conf_name = 'ucloud.conf' | ||||||
|  | conf_dir = os.environ.get('UCLOUD_CONF_DIR', '/etc/ucloud') | ||||||
| config_file = os.path.join(conf_dir, conf_name) | config_file = os.path.join(conf_dir, conf_name) | ||||||
| 
 | 
 | ||||||
| config = configparser.ConfigParser() | config = configparser.ConfigParser(allow_no_value=True) | ||||||
| 
 | 
 | ||||||
| try: | if os.access(config_file, os.R_OK): | ||||||
|     config.read(config_file) |     config.read(config_file) | ||||||
| except FileNotFoundError: | else: | ||||||
|     log.warn("Configuration file not found - using defaults") |     log.warning('Configuration file not found - using defaults') | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ################################################################################ |  | ||||||
| # ETCD3 support |  | ||||||
| import etcd3 |  | ||||||
| import json |  | ||||||
| import queue |  | ||||||
| import copy |  | ||||||
| from collections import namedtuple |  | ||||||
| 
 |  | ||||||
| PseudoEtcdMeta = namedtuple("PseudoEtcdMeta", ["key"]) |  | ||||||
| 
 |  | ||||||
| class EtcdEntry: |  | ||||||
|     # key: str |  | ||||||
|     # value: str |  | ||||||
| 
 |  | ||||||
|     def __init__(self, meta, value, value_in_json=False): |  | ||||||
|         self.key = meta.key.decode("utf-8") |  | ||||||
|         self.value = value.decode("utf-8") |  | ||||||
| 
 |  | ||||||
|         if value_in_json: |  | ||||||
|             self.value = json.loads(self.value) |  | ||||||
| 
 |  | ||||||
| class Etcd3Wrapper: |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         self.client = etcd3.client(*args, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def get(self, *args, value_in_json=False, **kwargs): |  | ||||||
|         _value, _key = self.client.get(*args, **kwargs) |  | ||||||
|         if _key is None or _value is None: |  | ||||||
|             return None |  | ||||||
|         return EtcdEntry(_key, _value, value_in_json=value_in_json) |  | ||||||
| 
 |  | ||||||
|     def put(self, *args, value_in_json=False, **kwargs): |  | ||||||
|         _key, _value = args |  | ||||||
|         if value_in_json: |  | ||||||
|             _value = json.dumps(_value) |  | ||||||
| 
 |  | ||||||
|         if not isinstance(_key, str): |  | ||||||
|             _key = _key.decode("utf-8") |  | ||||||
| 
 |  | ||||||
|         return self.client.put(_key, _value, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def get_prefix(self, *args, value_in_json=False, **kwargs): |  | ||||||
|         r = self.client.get_prefix(*args, **kwargs) |  | ||||||
|         for entry in r: |  | ||||||
|             e = EtcdEntry(*entry[::-1], value_in_json=value_in_json) |  | ||||||
|             if e.value: |  | ||||||
|                 yield e |  | ||||||
| 
 |  | ||||||
|     def watch_prefix(self, key, timeout=0, value_in_json=False): |  | ||||||
|         timeout_event = EtcdEntry(PseudoEtcdMeta(key=b"TIMEOUT"), |  | ||||||
|                                   value=str.encode(json.dumps({"status": "TIMEOUT", |  | ||||||
|                                                                "type": "TIMEOUT"})), |  | ||||||
|                                   value_in_json=value_in_json) |  | ||||||
| 
 |  | ||||||
|         event_queue = queue.Queue() |  | ||||||
| 
 |  | ||||||
|         def add_event_to_queue(event): |  | ||||||
|             for e in event.events: |  | ||||||
|                 if e.value: |  | ||||||
|                     event_queue.put(EtcdEntry(e, e.value, value_in_json=value_in_json)) |  | ||||||
| 
 |  | ||||||
|         self.client.add_watch_prefix_callback(key, add_event_to_queue) |  | ||||||
| 
 |  | ||||||
|         while True: |  | ||||||
|             try: |  | ||||||
|                 while True: |  | ||||||
|                     v = event_queue.get(timeout=timeout) |  | ||||||
|                     yield v |  | ||||||
|             except queue.Empty: |  | ||||||
|                 event_queue.put(copy.deepcopy(timeout_event)) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class PsuedoEtcdEntry(EtcdEntry): |  | ||||||
|     def __init__(self, key, value, value_in_json=False): |  | ||||||
|         super().__init__(PseudoEtcdMeta(key=key.encode("utf-8")), value, value_in_json=value_in_json) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| etcd_wrapper_args = () | etcd_wrapper_args = () | ||||||
| etcd_wrapper_kwargs = { | etcd_wrapper_kwargs = { | ||||||
|     'host':      config['etcd']['ETCD_URL'], |     'host':      config['etcd']['url'], | ||||||
|     'port':      config['etcd']['ETCD_PORT'], |     'port':      config['etcd']['port'], | ||||||
|     'ca_cert':   config['etcd']['CA_CERT'], |     'ca_cert':   config['etcd']['ca_cert'], | ||||||
|     'cert_cert': config['etcd']['CERT_CERT'], |     'cert_cert': config['etcd']['cert_cert'], | ||||||
|     'cert_key':  config['etcd']['CERT_KEY'] |     'cert_key':  config['etcd']['cert_key'] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| etcd_client = Etcd3Wrapper(*etcd_wrapper_args, **etcd_wrapper_kwargs) | etcd_client = Etcd3Wrapper(*etcd_wrapper_args, **etcd_wrapper_kwargs) | ||||||
| 
 | 
 | ||||||
| host_pool = HostPool(etcd_client, config['etcd']['HOST_PREFIX']) | host_pool = HostPool(etcd_client, config['etcd']['host_prefix']) | ||||||
| vm_pool = VmPool(etcd_client, config['etcd']['VM_PREFIX']) | vm_pool = VmPool(etcd_client, config['etcd']['vm_prefix']) | ||||||
| request_pool = RequestPool(etcd_client, config['etcd']['REQUEST_PREFIX']) | request_pool = RequestPool(etcd_client, config['etcd']['request_prefix']) | ||||||
| 
 | 
 | ||||||
| running_vms = [] | running_vms = [] | ||||||
| 
 | 
 | ||||||
| __storage_backend = config['storage']["STORAGE_BACKEND"] | __storage_backend = config['storage']['backend'] | ||||||
| if __storage_backend == "filesystem": | if __storage_backend == 'filesystem': | ||||||
|     image_storage_handler = FileSystemBasedImageStorageHandler(vm_base=config['storage']["VM_DIR"], |     image_storage_handler = FileSystemBasedImageStorageHandler( | ||||||
|                                                                image_base=config['storage']["IMAGE_DIR"]) |         vm_base=config['storage']['vm_dir'], | ||||||
| elif __storage_backend == "ceph": |         image_base=config['storage']['image_dir'] | ||||||
|     image_storage_handler = CEPHBasedImageStorageHandler(vm_base=config['storage']["CEPH_VM_POOL"], |     ) | ||||||
|                                                          image_base=config['storage']["CEPH_IMAGE_POOL"]) | elif __storage_backend == 'ceph': | ||||||
|  |     image_storage_handler = CEPHBasedImageStorageHandler( | ||||||
|  |         vm_base=config['storage']['ceph_vm_pool'], | ||||||
|  |         image_base=config['storage']['ceph_image_pool'] | ||||||
|  |     ) | ||||||
| else: | else: | ||||||
|     raise Exception("Unknown Image Storage Handler") |     raise Exception('Unknown Image Storage Handler') | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import os | ||||||
| import pathlib | import pathlib | ||||||
| import subprocess as sp | import subprocess as sp | ||||||
| import time | import time | ||||||
|  | import sys | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
| 
 | 
 | ||||||
| from . import logger | from . import logger | ||||||
|  | @ -19,7 +20,6 @@ def getxattr(file, attr): | ||||||
|                                  '--absolute-names'], stderr=sp.DEVNULL) |                                  '--absolute-names'], stderr=sp.DEVNULL) | ||||||
|         value = value.decode("utf-8") |         value = value.decode("utf-8") | ||||||
|     except sp.CalledProcessError as e: |     except sp.CalledProcessError as e: | ||||||
|         logger.exception(e) |  | ||||||
|         value = None |         value = None | ||||||
| 
 | 
 | ||||||
|     return value |     return value | ||||||
|  | @ -63,14 +63,13 @@ try: | ||||||
|     sp.check_output(['which', 'getfattr']) |     sp.check_output(['which', 'getfattr']) | ||||||
|     sp.check_output(['which', 'setfattr']) |     sp.check_output(['which', 'setfattr']) | ||||||
| except Exception as e: | except Exception as e: | ||||||
|     logger.exception(e) |     logger.error("You don't seems to have both getfattr and setfattr") | ||||||
|     print('Make sure you have getfattr and setfattr available') |     sys.exit(1) | ||||||
|     exit(1) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def main(): | def main(): | ||||||
|     BASE_DIR    = config['storage']["FILE_DIR"] |     BASE_DIR    = config['storage']['file_dir'] | ||||||
|     FILE_PREFIX = config['storage']["FILE_PREFIX"] |     FILE_PREFIX = config['etcd']['file_prefix'] | ||||||
| 
 | 
 | ||||||
|     # Recursively Get All Files and Folder below BASE_DIR |     # Recursively Get All Files and Folder below BASE_DIR | ||||||
|     files = glob.glob("{}/**".format(BASE_DIR), recursive=True) |     files = glob.glob("{}/**".format(BASE_DIR), recursive=True) | ||||||
|  | @ -79,7 +78,7 @@ def main(): | ||||||
|     files = list(filter(os.path.isfile, files)) |     files = list(filter(os.path.isfile, files)) | ||||||
| 
 | 
 | ||||||
|     untracked_files = list( |     untracked_files = list( | ||||||
|         filter(lambda f: not bool(getxattr(f, "user.utracked")), files) |         filter(lambda f: not bool(getxattr(f, "utracked")), files) | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     tracked_files = list( |     tracked_files = list( | ||||||
|  | @ -89,7 +88,8 @@ def main(): | ||||||
|         file_id = uuid4() |         file_id = uuid4() | ||||||
| 
 | 
 | ||||||
|         # Get Username |         # Get Username | ||||||
|         owner = pathlib.Path(file).parts[3] |         owner = pathlib.Path(file).parts[len(pathlib.Path(BASE_DIR).parts)] | ||||||
|  |          | ||||||
|         # Get Creation Date of File |         # Get Creation Date of File | ||||||
|         # Here, we are assuming that ctime is creation time |         # Here, we are assuming that ctime is creation time | ||||||
|         # which is mostly not true. |         # which is mostly not true. | ||||||
|  | @ -101,9 +101,7 @@ def main(): | ||||||
|         # Compute sha512 sum |         # Compute sha512 sum | ||||||
|         sha_sum = sha512sum(file) |         sha_sum = sha512sum(file) | ||||||
| 
 | 
 | ||||||
|         # File Path excluding base and username |         file_path = pathlib.Path(file).parts[-1] | ||||||
|         file_path = pathlib.Path(file).parts[4:] |  | ||||||
|         file_path = os.path.join(*file_path) |  | ||||||
| 
 | 
 | ||||||
|         # Create Entry |         # Create Entry | ||||||
|         entry_key = os.path.join(FILE_PREFIX, str(file_id)) |         entry_key = os.path.join(FILE_PREFIX, str(file_id)) | ||||||
|  | @ -115,10 +113,10 @@ def main(): | ||||||
|             "size": size |             "size": size | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         print("Tracking {}".format(file)) |         logger.info("Tracking %s", file) | ||||||
|         # Insert Entry |         # Insert Entry | ||||||
|         etcd_client.put(entry_key, entry_value, value_in_json=True) |         etcd_client.put(entry_key, entry_value, value_in_json=True) | ||||||
|         setxattr(file, "user.utracked", True) |         setxattr(file, "utracked", True) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|  |  | ||||||
|  | @ -1,9 +1,10 @@ | ||||||
| import argparse | import argparse | ||||||
| import multiprocessing as mp | import multiprocessing as mp | ||||||
| import time | import time | ||||||
|  | import sys | ||||||
| 
 | 
 | ||||||
| from etcd3_wrapper import Etcd3Wrapper | from os.path import isdir | ||||||
| 
 | from ucloud.common.etcd_wrapper import Etcd3Wrapper | ||||||
| from ucloud.common.request import RequestEntry, RequestType | from ucloud.common.request import RequestEntry, RequestType | ||||||
| from ucloud.config import (vm_pool, request_pool, | from ucloud.config import (vm_pool, request_pool, | ||||||
|                     etcd_client, running_vms, |                     etcd_client, running_vms, | ||||||
|  | @ -18,7 +19,7 @@ from ucloud.host import logger | ||||||
| def update_heartbeat(hostname): | def update_heartbeat(hostname): | ||||||
|     """Update Last HeartBeat Time for :param hostname: in etcd""" |     """Update Last HeartBeat Time for :param hostname: in etcd""" | ||||||
|     client = Etcd3Wrapper(*etcd_wrapper_args, **etcd_wrapper_kwargs) |     client = Etcd3Wrapper(*etcd_wrapper_args, **etcd_wrapper_kwargs) | ||||||
|     host_pool = HostPool(client, config['etcd']['HOST_PREFIX']) |     host_pool = HostPool(client, config['etcd']['host_prefix']) | ||||||
|     this_host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None) |     this_host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None) | ||||||
| 
 | 
 | ||||||
|     while True: |     while True: | ||||||
|  | @ -72,9 +73,11 @@ def maintenance(host): | ||||||
|                 running_vms.remove(_vm) |                 running_vms.remove(_vm) | ||||||
| 
 | 
 | ||||||
| def check(): | def check(): | ||||||
|     if config['etcd']['STORAGE_BACKEND'] == 'filesystem' and not isdir(config['etcd']['VM_DIR']): |     if config['storage']['backend'] == 'filesystem' and \ | ||||||
|  |         not isdir(config['storage']['vm_dir']): | ||||||
|  |          | ||||||
|         print("You have set STORAGE_BACKEND to filesystem. So, the vm directory mentioned" |         print("You have set STORAGE_BACKEND to filesystem. So, the vm directory mentioned" | ||||||
|               " in .env file must exists. But, it don't.") |               " in /etc/ucloud/ucloud.conf file must exists. But, it don't.") | ||||||
|         sys.exit(1) |         sys.exit(1) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -84,7 +87,7 @@ def main(hostname): | ||||||
| 
 | 
 | ||||||
|     heartbeat_updating_process = mp.Process(target=update_heartbeat, args=(hostname,)) |     heartbeat_updating_process = mp.Process(target=update_heartbeat, args=(hostname,)) | ||||||
| 
 | 
 | ||||||
|     host_pool = HostPool(etcd_client, config['etcd']['HOST_PREFIX']) |     host_pool = HostPool(etcd_client, config['etcd']['host_prefix']) | ||||||
|     host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None) |     host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None) | ||||||
|     assert host is not None, "No such host with name = {}".format(hostname) |     assert host is not None, "No such host with name = {}".format(hostname) | ||||||
| 
 | 
 | ||||||
|  | @ -106,8 +109,8 @@ def main(hostname): | ||||||
|     # beat updating mechanism in separated thread |     # beat updating mechanism in separated thread | ||||||
| 
 | 
 | ||||||
|     for events_iterator in [ |     for events_iterator in [ | ||||||
|         etcd_client.get_prefix(config['etcd']['REQUEST_PREFIX'], value_in_json=True), |         etcd_client.get_prefix(config['etcd']['request_prefix'], value_in_json=True), | ||||||
|         etcd_client.watch_prefix(config['etcd']['REQUEST_PREFIX'], timeout=10, value_in_json=True), |         etcd_client.watch_prefix(config['etcd']['request_prefix'], timeout=10, value_in_json=True), | ||||||
|     ]: |     ]: | ||||||
|         for request_event in events_iterator: |         for request_event in events_iterator: | ||||||
|             request_event = RequestEntry(request_event) |             request_event = RequestEntry(request_event) | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ def delete_network_interface(iface): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def resolve_network(network_name, network_owner): | def resolve_network(network_name, network_owner): | ||||||
|     network = etcd_client.get(join_path(config['etcd']["NETWORK_PREFIX"], |     network = etcd_client.get(join_path(config['etcd']['network_prefix'], | ||||||
|                                         network_owner, |                                         network_owner, | ||||||
|                                         network_name), |                                         network_name), | ||||||
|                               value_in_json=True) |                               value_in_json=True) | ||||||
|  | @ -179,7 +179,7 @@ def get_start_command_args(vm_entry, vnc_sock_filename: str, migration=False, mi | ||||||
|     for network_mac_and_tap in vm_networks: |     for network_mac_and_tap in vm_networks: | ||||||
|         network_name, mac, tap = network_mac_and_tap |         network_name, mac, tap = network_mac_and_tap | ||||||
| 
 | 
 | ||||||
|         _key = os.path.join(config['etcd']['NETWORK_PREFIX'], vm_entry.owner, network_name) |         _key = os.path.join(config['etcd']['network_prefix'], vm_entry.owner, network_name) | ||||||
|         network = etcd_client.get(_key, value_in_json=True) |         network = etcd_client.get(_key, value_in_json=True) | ||||||
|         network_type = network.value["type"] |         network_type = network.value["type"] | ||||||
|         network_id = str(network.value["id"]) |         network_id = str(network.value["id"]) | ||||||
|  | @ -187,7 +187,7 @@ def get_start_command_args(vm_entry, vnc_sock_filename: str, migration=False, mi | ||||||
| 
 | 
 | ||||||
|         if network_type == "vxlan": |         if network_type == "vxlan": | ||||||
|             tap = create_vxlan_br_tap(_id=network_id, |             tap = create_vxlan_br_tap(_id=network_id, | ||||||
|                                       _dev=config['etcd']["VXLAN_PHY_DEV"], |                                       _dev=config['network']['vxlan_phy_dev'], | ||||||
|                                       tap_id=tap, |                                       tap_id=tap, | ||||||
|                                       ip=network_ipv6) |                                       ip=network_ipv6) | ||||||
|             update_radvd_conf(etcd_client) |             update_radvd_conf(etcd_client) | ||||||
|  | @ -303,13 +303,13 @@ def transfer(request_event): | ||||||
|     _host, _port = request_event.parameters["host"], request_event.parameters["port"] |     _host, _port = request_event.parameters["host"], request_event.parameters["port"] | ||||||
|     _uuid = request_event.uuid |     _uuid = request_event.uuid | ||||||
|     _destination = request_event.destination_host_key |     _destination = request_event.destination_host_key | ||||||
|     vm = get_vm(running_vms, join_path(config['etcd']['VM_PREFIX'], _uuid)) |     vm = get_vm(running_vms, join_path(config['etcd']['vm_prefix'], _uuid)) | ||||||
| 
 | 
 | ||||||
|     if vm: |     if vm: | ||||||
|         tunnel = sshtunnel.SSHTunnelForwarder( |         tunnel = sshtunnel.SSHTunnelForwarder( | ||||||
|             _host, |             _host, | ||||||
|             ssh_username=config['ssh']["ssh_username"], |             ssh_username=config['ssh']['username'], | ||||||
|             ssh_pkey=config['ssh']["SSH_PRIVATEKEY"], |             ssh_pkey=config['ssh']['private_key_path'], | ||||||
|             remote_bind_address=("127.0.0.1", _port), |             remote_bind_address=("127.0.0.1", _port), | ||||||
|             ssh_proxy_enabled=True, |             ssh_proxy_enabled=True, | ||||||
|             ssh_proxy=(_host, 22) |             ssh_proxy=(_host, 22) | ||||||
|  | @ -373,7 +373,7 @@ def launch_vm(vm_entry, migration=False, migration_port=None, destination_host_k | ||||||
|                 parameters={"host": get_ipv6_address(), "port": migration_port}, |                 parameters={"host": get_ipv6_address(), "port": migration_port}, | ||||||
|                 uuid=vm_entry.uuid, |                 uuid=vm_entry.uuid, | ||||||
|                 destination_host_key=destination_host_key, |                 destination_host_key=destination_host_key, | ||||||
|                 request_prefix=config['etcd']["REQUEST_PREFIX"] |                 request_prefix=config['etcd']['request_prefix'] | ||||||
|             ) |             ) | ||||||
|             request_pool.put(r) |             request_pool.put(r) | ||||||
|         else: |         else: | ||||||
|  |  | ||||||
|  | @ -20,9 +20,9 @@ def qemu_img_type(path): | ||||||
| 
 | 
 | ||||||
| def check(): | def check(): | ||||||
|     """ check whether settings are sane, refuse to start if they aren't """ |     """ check whether settings are sane, refuse to start if they aren't """ | ||||||
|     if config['etcd']['STORAGE_BACKEND'] == 'filesystem' and not isdir(config['etcd']['IMAGE_DIR']): |     if config['storage']['backend'] == 'filesystem' and not isdir(config['storage']['image_dir']): | ||||||
|         print("You have set STORAGE_BACKEND to filesystem, but " |         print("You have set STORAGE_BACKEND to filesystem, but " | ||||||
|               "{} does not exist. Refusing to start".format(config['etcd']['IMAGE_DIR'])) |               "{} does not exist. Refusing to start".format(config['storage']['image_dir'])) | ||||||
|         sys.exit(1) |         sys.exit(1) | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|  | @ -34,7 +34,7 @@ def check(): | ||||||
| 
 | 
 | ||||||
| def main(): | def main(): | ||||||
|     # We want to get images entries that requests images to be created |     # We want to get images entries that requests images to be created | ||||||
|     images = etcd_client.get_prefix(config['etcd']['IMAGE_PREFIX'], value_in_json=True) |     images = etcd_client.get_prefix(config['etcd']['image_prefix'], value_in_json=True) | ||||||
|     images_to_be_created = list(filter(lambda im: im.value['status'] == 'TO_BE_CREATED', images)) |     images_to_be_created = list(filter(lambda im: im.value['status'] == 'TO_BE_CREATED', images)) | ||||||
| 
 | 
 | ||||||
|     for image in images_to_be_created: |     for image in images_to_be_created: | ||||||
|  | @ -43,9 +43,10 @@ def main(): | ||||||
|             image_owner = image.value['owner'] |             image_owner = image.value['owner'] | ||||||
|             image_filename = image.value['filename'] |             image_filename = image.value['filename'] | ||||||
|             image_store_name = image.value['store_name'] |             image_store_name = image.value['store_name'] | ||||||
|             image_full_path = join_path(config['etcd']['BASE_DIR'], image_owner, image_filename) |             image_full_path = join_path(config['storage']['file_dir'], image_owner, image_filename) | ||||||
| 
 | 
 | ||||||
|             image_stores = etcd_client.get_prefix(config['etcd']['IMAGE_STORE_PREFIX'], value_in_json=True) |             image_stores = etcd_client.get_prefix(config['etcd']['image_store_prefix'], | ||||||
|  |                                                   value_in_json=True) | ||||||
|             user_image_store = next(filter( |             user_image_store = next(filter( | ||||||
|                 lambda s, store_name=image_store_name: s.value["name"] == store_name, |                 lambda s, store_name=image_store_name: s.value["name"] == store_name, | ||||||
|                 image_stores |                 image_stores | ||||||
|  |  | ||||||
|  | @ -43,7 +43,8 @@ class Root(Resource): | ||||||
|         if not data: |         if not data: | ||||||
|             return {'message': 'Metadata for such VM does not exists.'}, 404 |             return {'message': 'Metadata for such VM does not exists.'}, 404 | ||||||
|         else: |         else: | ||||||
|             etcd_key = os.path.join(config['etcd']['USER_PREFIX'], data.value['owner_realm'], |             etcd_key = os.path.join(config['etcd']['user_prefix'], | ||||||
|  |                                     data.value['owner_realm'], | ||||||
|                                     data.value['owner'], 'key') |                                     data.value['owner'], 'key') | ||||||
|             etcd_entry = etcd_client.get_prefix(etcd_key, value_in_json=True) |             etcd_entry = etcd_client.get_prefix(etcd_key, value_in_json=True) | ||||||
|             user_personal_ssh_keys = [key.value for key in etcd_entry] |             user_personal_ssh_keys = [key.value for key in etcd_entry] | ||||||
|  |  | ||||||
|  | @ -106,7 +106,7 @@ def assign_host(vm): | ||||||
|     r = RequestEntry.from_scratch(type=RequestType.StartVM, |     r = RequestEntry.from_scratch(type=RequestType.StartVM, | ||||||
|                                   uuid=vm.uuid, |                                   uuid=vm.uuid, | ||||||
|                                   hostname=vm.hostname, |                                   hostname=vm.hostname, | ||||||
|                                   request_prefix=config['etcd']['REQUEST_PREFIX']) |                                   request_prefix=config['etcd']['request_prefix']) | ||||||
|     request_pool.put(r) |     request_pool.put(r) | ||||||
| 
 | 
 | ||||||
|     vm.log.append("VM scheduled for starting") |     vm.log.append("VM scheduled for starting") | ||||||
|  |  | ||||||
|  | @ -18,8 +18,8 @@ def main(): | ||||||
|     pending_vms = [] |     pending_vms = [] | ||||||
| 
 | 
 | ||||||
|     for request_iterator in [ |     for request_iterator in [ | ||||||
|         etcd_client.get_prefix(config['etcd']['REQUEST_PREFIX'], value_in_json=True), |         etcd_client.get_prefix(config['etcd']['request_prefix'], value_in_json=True), | ||||||
|         etcd_client.watch_prefix(config['etcd']['REQUEST_PREFIX'], timeout=5, value_in_json=True), |         etcd_client.watch_prefix(config['etcd']['request_prefix'], timeout=5, value_in_json=True), | ||||||
|     ]: |     ]: | ||||||
|         for request_event in request_iterator: |         for request_event in request_iterator: | ||||||
|             request_entry = RequestEntry(request_event) |             request_entry = RequestEntry(request_event) | ||||||
|  | @ -46,7 +46,7 @@ def main(): | ||||||
|                     r = RequestEntry.from_scratch(type="ScheduleVM", |                     r = RequestEntry.from_scratch(type="ScheduleVM", | ||||||
|                                                   uuid=pending_vm_entry.uuid, |                                                   uuid=pending_vm_entry.uuid, | ||||||
|                                                   hostname=pending_vm_entry.hostname, |                                                   hostname=pending_vm_entry.hostname, | ||||||
|                                                   request_prefix=config['etcd']['REQUEST_PREFIX']) |                                                   request_prefix=config['etcd']['request_prefix']) | ||||||
|                     request_pool.put(r) |                     request_pool.put(r) | ||||||
| 
 | 
 | ||||||
|             elif request_entry.type == RequestType.ScheduleVM: |             elif request_entry.type == RequestType.ScheduleVM: | ||||||
|  | @ -72,7 +72,7 @@ def main(): | ||||||
|                         r = RequestEntry.from_scratch(type=RequestType.InitVMMigration, |                         r = RequestEntry.from_scratch(type=RequestType.InitVMMigration, | ||||||
|                                                       uuid=request_entry.uuid, |                                                       uuid=request_entry.uuid, | ||||||
|                                                       destination=request_entry.destination, |                                                       destination=request_entry.destination, | ||||||
|                                                       request_prefix=config['etcd']['REQUEST_PREFIX']) |                                                       request_prefix=config['etcd']['request_prefix']) | ||||||
|                         request_pool.put(r) |                         request_pool.put(r) | ||||||
| 
 | 
 | ||||||
|                 # If the Request is about a VM that just want to get started/created |                 # If the Request is about a VM that just want to get started/created | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue