Implemented support for VM operations through VM name, Allow user of realm ungleich-admin to do operation on someone's else VM (for providing support etc), Fix few hidden issues, Refactoring/Formating + Little bit Documenting
This commit is contained in:
parent
0f1cf2c7fa
commit
f18dbbe62b
7 changed files with 250 additions and 122 deletions
14
Pipfile.lock
generated
14
Pipfile.lock
generated
|
|
@ -54,7 +54,7 @@
|
||||||
"etcd3-wrapper": {
|
"etcd3-wrapper": {
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"git": "https://code.ungleich.ch/ungleich-public/etcd3_wrapper.git",
|
"git": "https://code.ungleich.ch/ungleich-public/etcd3_wrapper.git",
|
||||||
"ref": "b1893fc286e9ed59876a526d81094edd796d6815"
|
"ref": "76fb0bdf797199e9ea161dad1d004eea9b4520f8"
|
||||||
},
|
},
|
||||||
"flask": {
|
"flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|
@ -240,17 +240,17 @@
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
|
"sha256:2f3eadfea5d92bc7899e75b5968410b749a054b492d5a6379c1344a1481bc2cb",
|
||||||
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
|
"sha256:9c6c593cb28f52075016307fc26b0a0f8e82bc7d1ff19aaaa959b91710a56c47"
|
||||||
],
|
],
|
||||||
"version": "==1.25.3"
|
"version": "==1.25.5"
|
||||||
},
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:00d32beac38fcd48d329566f80d39f10ec2ed994efbecfb8dd4b320062d05902",
|
"sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7",
|
||||||
"sha256:0a24d43be6a7dce81bae05292356176d6c46d63e42a0dd3f9504b210a9cfaa43"
|
"sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4"
|
||||||
],
|
],
|
||||||
"version": "==0.15.6"
|
"version": "==0.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
|
|
|
||||||
|
|
@ -62,5 +62,7 @@ class SpecsField(Field):
|
||||||
|
|
||||||
def specs_validation(self):
|
def specs_validation(self):
|
||||||
if not specs_parser.transform_specs(self.specs):
|
if not specs_parser.transform_specs(self.specs):
|
||||||
self.add_error("Invalid unit - "
|
self.add_error(
|
||||||
"Please use following units {}".format(specs_parser.get_allowed_units()))
|
"Invalid unit - "
|
||||||
|
"Please use following units {}".format(specs_parser.get_allowed_units())
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,11 @@ import logging
|
||||||
from etcd3_wrapper import Etcd3Wrapper
|
from etcd3_wrapper import Etcd3Wrapper
|
||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
|
|
||||||
|
from ucloud_common.vm import VmPool
|
||||||
|
from ucloud_common.host import HostPool
|
||||||
|
from ucloud_common.request import RequestPool
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
filename="log.txt",
|
filename="log.txt",
|
||||||
|
|
@ -21,3 +26,7 @@ IMAGE_PREFIX = config("IMAGE_PREFIX")
|
||||||
IMAGE_STORE_PREFIX = config("IMAGE_STORE_PREFIX")
|
IMAGE_STORE_PREFIX = config("IMAGE_STORE_PREFIX")
|
||||||
|
|
||||||
etcd_client = Etcd3Wrapper(host=config("ETCD_URL"))
|
etcd_client = Etcd3Wrapper(host=config("ETCD_URL"))
|
||||||
|
|
||||||
|
VM_POOL = VmPool(etcd_client, VM_PREFIX)
|
||||||
|
HOST_POOL = HostPool(etcd_client, HOST_PREFIX)
|
||||||
|
REQUEST_POOL = RequestPool(etcd_client, REQUEST_PREFIX)
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,7 @@ data = {
|
||||||
"type": "ceph",
|
"type": "ceph",
|
||||||
"name": "images",
|
"name": "images",
|
||||||
"description": "first ever public image-store",
|
"description": "first ever public image-store",
|
||||||
"attributes": {
|
"attributes": {"list": [], "key": [], "pool": "images"},
|
||||||
"list": [],
|
|
||||||
"key": [],
|
|
||||||
"pool": "images",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.put(
|
client.put(os.path.join(IMAGE_STORE_PREFIX, uuid4().hex), json.dumps(data))
|
||||||
os.path.join(IMAGE_STORE_PREFIX, uuid4().hex),
|
|
||||||
json.dumps(data),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
17
helper.py
17
helper.py
|
|
@ -3,6 +3,7 @@ import requests
|
||||||
|
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from pyotp import TOTP
|
from pyotp import TOTP
|
||||||
|
from config import VM_POOL
|
||||||
|
|
||||||
|
|
||||||
def check_otp(name, realm, token):
|
def check_otp(name, realm, token):
|
||||||
|
|
@ -26,3 +27,19 @@ def check_otp(name, realm, token):
|
||||||
data=data,
|
data=data,
|
||||||
)
|
)
|
||||||
return response.status_code
|
return response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_vm_name(name, owner):
|
||||||
|
"""
|
||||||
|
Input: name of vm, owner of vm
|
||||||
|
Output: uuid of vm if found otherwise None
|
||||||
|
"""
|
||||||
|
result = next(
|
||||||
|
filter(
|
||||||
|
lambda vm: vm.value["owner"] == owner and vm.value["name"] == name,
|
||||||
|
VM_POOL.vms,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if result:
|
||||||
|
return result.key.split("/")[-1]
|
||||||
|
|
|
||||||
106
main.py
106
main.py
|
|
@ -1,7 +1,7 @@
|
||||||
# TODO
|
# TODO: realm should be part of user's name. Because, there can be multiple
|
||||||
# 1. Allow user of realm ungleich-admin to perform any action on
|
# name but with different realms. For Example, nico exists in both
|
||||||
# any user vm.
|
# ungleich-admin and ungleich-twitter realm. So, to differentiate between
|
||||||
|
# them, we need to make realm part of user's name
|
||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
@ -10,29 +10,34 @@ from uuid import uuid4
|
||||||
|
|
||||||
from flask import Flask, request
|
from flask import Flask, request
|
||||||
from flask_restful import Resource, Api
|
from flask_restful import Resource, Api
|
||||||
from ucloud_common.vm import VmPool, VMStatus
|
from ucloud_common.vm import VMStatus
|
||||||
from ucloud_common.host import HostPool
|
from ucloud_common.request import RequestEntry, RequestType
|
||||||
from ucloud_common.request import (RequestEntry,
|
|
||||||
RequestPool,
|
|
||||||
RequestType)
|
|
||||||
|
|
||||||
from config import etcd_client as client
|
from config import etcd_client as client
|
||||||
from config import (WITHOUT_CEPH, VM_PREFIX,
|
from config import (
|
||||||
HOST_PREFIX, REQUEST_PREFIX,
|
WITHOUT_CEPH,
|
||||||
FILE_PREFIX, IMAGE_PREFIX,
|
VM_PREFIX,
|
||||||
logging)
|
HOST_PREFIX,
|
||||||
from schemas import (CreateVMSchema, VMStatusSchema,
|
FILE_PREFIX,
|
||||||
CreateImageSchema, VmActionSchema,
|
IMAGE_PREFIX,
|
||||||
OTPSchema, CreateHostSchema,
|
logging,
|
||||||
VmMigrationSchema)
|
REQUEST_POOL,
|
||||||
|
VM_POOL,
|
||||||
|
HOST_POOL,
|
||||||
|
)
|
||||||
|
from schemas import (
|
||||||
|
CreateVMSchema,
|
||||||
|
VMStatusSchema,
|
||||||
|
CreateImageSchema,
|
||||||
|
VmActionSchema,
|
||||||
|
OTPSchema,
|
||||||
|
CreateHostSchema,
|
||||||
|
VmMigrationSchema,
|
||||||
|
)
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
api = Api(app)
|
api = Api(app)
|
||||||
|
|
||||||
VM_POOL = VmPool(client, VM_PREFIX)
|
|
||||||
HOST_POOL = HostPool(client, HOST_PREFIX)
|
|
||||||
REQUEST_POOL = RequestPool(client, REQUEST_PREFIX)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateVM(Resource):
|
class CreateVM(Resource):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -43,6 +48,7 @@ class CreateVM(Resource):
|
||||||
vm_uuid = uuid4().hex
|
vm_uuid = uuid4().hex
|
||||||
vm_key = os.path.join(VM_PREFIX, vm_uuid)
|
vm_key = os.path.join(VM_PREFIX, vm_uuid)
|
||||||
vm_entry = {
|
vm_entry = {
|
||||||
|
"name": data["vm_name"],
|
||||||
"owner": data["name"],
|
"owner": data["name"],
|
||||||
"specs": data["specs"],
|
"specs": data["specs"],
|
||||||
"hostname": "",
|
"hostname": "",
|
||||||
|
|
@ -50,13 +56,12 @@ class CreateVM(Resource):
|
||||||
"image_uuid": data["image_uuid"],
|
"image_uuid": data["image_uuid"],
|
||||||
"log": [],
|
"log": [],
|
||||||
"storage_attachment": [],
|
"storage_attachment": [],
|
||||||
"vnc_socket": ""
|
"vnc_socket": "",
|
||||||
}
|
}
|
||||||
client.put(vm_key, vm_entry, value_in_json=True)
|
client.put(vm_key, vm_entry, value_in_json=True)
|
||||||
|
|
||||||
# Create ScheduleVM Request
|
# Create ScheduleVM Request
|
||||||
r = RequestEntry.from_scratch(type=RequestType.ScheduleVM,
|
r = RequestEntry.from_scratch(type=RequestType.ScheduleVM, uuid=vm_uuid)
|
||||||
uuid=vm_uuid)
|
|
||||||
REQUEST_POOL.put(r)
|
REQUEST_POOL.put(r)
|
||||||
|
|
||||||
return {"message": "VM Creation Queued"}, 200
|
return {"message": "VM Creation Queued"}, 200
|
||||||
|
|
@ -69,7 +74,7 @@ class VmStatus(Resource):
|
||||||
data = request.json
|
data = request.json
|
||||||
validator = VMStatusSchema(data)
|
validator = VMStatusSchema(data)
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
vm = VM_POOL.get(os.path.join(VM_PREFIX, data['uuid']))
|
vm = VM_POOL.get(os.path.join(VM_PREFIX, data["uuid"]))
|
||||||
return str(vm)
|
return str(vm)
|
||||||
else:
|
else:
|
||||||
return validator.get_errors(), 400
|
return validator.get_errors(), 400
|
||||||
|
|
@ -81,7 +86,7 @@ class CreateImage(Resource):
|
||||||
data = request.json
|
data = request.json
|
||||||
validator = CreateImageSchema(data)
|
validator = CreateImageSchema(data)
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
file_entry = client.get(os.path.join(FILE_PREFIX, data['uuid']))
|
file_entry = client.get(os.path.join(FILE_PREFIX, data["uuid"]))
|
||||||
file_entry_value = json.loads(file_entry.value)
|
file_entry_value = json.loads(file_entry.value)
|
||||||
|
|
||||||
image_entry_json = {
|
image_entry_json = {
|
||||||
|
|
@ -92,7 +97,9 @@ class CreateImage(Resource):
|
||||||
"store_name": data["image_store"],
|
"store_name": data["image_store"],
|
||||||
"visibility": "public",
|
"visibility": "public",
|
||||||
}
|
}
|
||||||
client.put(os.path.join(IMAGE_PREFIX, data['uuid']), json.dumps(image_entry_json))
|
client.put(
|
||||||
|
os.path.join(IMAGE_PREFIX, data["uuid"]), json.dumps(image_entry_json)
|
||||||
|
)
|
||||||
|
|
||||||
return {"message": "Image successfully created"}
|
return {"message": "Image successfully created"}
|
||||||
return validator.get_errors(), 400
|
return validator.get_errors(), 400
|
||||||
|
|
@ -115,7 +122,7 @@ class VMAction(Resource):
|
||||||
validator = VmActionSchema(data)
|
validator = VmActionSchema(data)
|
||||||
|
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
vm_entry = VM_POOL.get(os.path.join(VM_PREFIX, data['uuid']))
|
vm_entry = VM_POOL.get(os.path.join(VM_PREFIX, data["uuid"]))
|
||||||
action = data["action"]
|
action = data["action"]
|
||||||
|
|
||||||
if action == "start":
|
if action == "start":
|
||||||
|
|
@ -125,10 +132,13 @@ class VMAction(Resource):
|
||||||
|
|
||||||
if action == "delete" and vm_entry.hostname == "":
|
if action == "delete" and vm_entry.hostname == "":
|
||||||
try:
|
try:
|
||||||
path_without_protocol = vm_entry.path[vm_entry.path.find(":")+1:]
|
path_without_protocol = vm_entry.path[vm_entry.path.find(":") + 1 :]
|
||||||
|
|
||||||
if WITHOUT_CEPH:
|
if WITHOUT_CEPH:
|
||||||
command_to_delete = ["rm", os.path.join("/var/vm", vm_entry.uuid)]
|
command_to_delete = [
|
||||||
|
"rm",
|
||||||
|
os.path.join("/var/vm", vm_entry.uuid),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
command_to_delete = ["rbd", "rm", path_without_protocol]
|
command_to_delete = ["rbd", "rm", path_without_protocol]
|
||||||
|
|
||||||
|
|
@ -144,9 +154,11 @@ class VMAction(Resource):
|
||||||
client.client.delete(vm_entry.key)
|
client.client.delete(vm_entry.key)
|
||||||
return {"message": "VM successfully deleted"}
|
return {"message": "VM successfully deleted"}
|
||||||
|
|
||||||
r = RequestEntry.from_scratch(type="{}VM".format(action.title()),
|
r = RequestEntry.from_scratch(
|
||||||
uuid=data['uuid'],
|
type="{}VM".format(action.title()),
|
||||||
hostname=vm_entry.hostname)
|
uuid=data["uuid"],
|
||||||
|
hostname=vm_entry.hostname,
|
||||||
|
)
|
||||||
REQUEST_POOL.put(r)
|
REQUEST_POOL.put(r)
|
||||||
return {"message": "VM {} Queued".format(action.title())}, 200
|
return {"message": "VM {} Queued".format(action.title())}, 200
|
||||||
else:
|
else:
|
||||||
|
|
@ -160,12 +172,14 @@ class VMMigration(Resource):
|
||||||
validator = VmMigrationSchema(data)
|
validator = VmMigrationSchema(data)
|
||||||
|
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
vm = VM_POOL.get(data['uuid'])
|
vm = VM_POOL.get(data["uuid"])
|
||||||
|
|
||||||
r = RequestEntry.from_scratch(type=RequestType.ScheduleVM,
|
r = RequestEntry.from_scratch(
|
||||||
|
type=RequestType.ScheduleVM,
|
||||||
uuid=vm.uuid,
|
uuid=vm.uuid,
|
||||||
destination=os.path.join(HOST_PREFIX, data["destination"]),
|
destination=os.path.join(HOST_PREFIX, data["destination"]),
|
||||||
migration=True)
|
migration=True,
|
||||||
|
)
|
||||||
REQUEST_POOL.put(r)
|
REQUEST_POOL.put(r)
|
||||||
return {"message": "VM Migration Initialization Queued"}, 200
|
return {"message": "VM Migration Initialization Queued"}, 200
|
||||||
else:
|
else:
|
||||||
|
|
@ -180,22 +194,25 @@ class ListUserVM(Resource):
|
||||||
|
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
vms = client.get_prefix(VM_PREFIX, value_in_json=True)
|
vms = client.get_prefix(VM_PREFIX, value_in_json=True)
|
||||||
if vms:
|
|
||||||
return_vms = []
|
return_vms = []
|
||||||
user_vms = list(filter(lambda v: v.value["owner"] == data["name"], vms))
|
user_vms = filter(lambda v: v.value["owner"] == data["name"], vms)
|
||||||
for vm in user_vms:
|
for vm in user_vms:
|
||||||
return_vms.append(
|
return_vms.append(
|
||||||
{
|
{
|
||||||
|
"name": vm.value["name"],
|
||||||
"vm_uuid": vm.key.split("/")[-1],
|
"vm_uuid": vm.key.split("/")[-1],
|
||||||
"specs": vm.value["specs"],
|
"specs": vm.value["specs"],
|
||||||
"status": vm.value["status"],
|
"status": vm.value["status"],
|
||||||
"hostname": vm.value["hostname"],
|
"hostname": vm.value["hostname"],
|
||||||
"vnc_socket": None or vm.value["vnc_socket"]
|
"vnc_socket": None
|
||||||
|
if vm.value.get("vnc_socket", None) == None
|
||||||
|
else vm.value["vnc_socket"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if return_vms:
|
||||||
return {"message": return_vms}, 200
|
return {"message": return_vms}, 200
|
||||||
else:
|
|
||||||
return {"message": "No VM found"}, 404
|
return {"message": "No VM found"}, 404
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return validator.get_errors(), 400
|
return validator.get_errors(), 400
|
||||||
|
|
||||||
|
|
@ -246,7 +263,14 @@ class ListHost(Resource):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get():
|
def get():
|
||||||
hosts = HOST_POOL.hosts
|
hosts = HOST_POOL.hosts
|
||||||
r = {host.key: {"status": host.status, "specs": host.specs, "hostname": host.hostname} for host in hosts}
|
r = {
|
||||||
|
host.key: {
|
||||||
|
"status": host.status,
|
||||||
|
"specs": host.specs,
|
||||||
|
"hostname": host.hostname,
|
||||||
|
}
|
||||||
|
for host in hosts
|
||||||
|
}
|
||||||
return r, 200
|
return r, 200
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
181
schemas.py
181
schemas.py
|
|
@ -1,3 +1,19 @@
|
||||||
|
"""
|
||||||
|
This module contain classes thats validates and intercept/modify
|
||||||
|
data coming from ucloud-cli (user)
|
||||||
|
|
||||||
|
It was primarily developed as an alternative to argument parser
|
||||||
|
of Flask_Restful which is going to be deprecated. I also tried
|
||||||
|
marshmallow for that purpose but it was an overkill (because it
|
||||||
|
do validation + serialization + deserialization) and little
|
||||||
|
inflexible for our purpose.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: Fix error message when user's mentioned VM (referred by name)
|
||||||
|
# does not exists.
|
||||||
|
#
|
||||||
|
# Currently, it says uuid is a required field.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
@ -5,10 +21,9 @@ from ucloud_common.host import HostPool, HostStatus
|
||||||
from ucloud_common.vm import VmPool, VMStatus
|
from ucloud_common.vm import VmPool, VMStatus
|
||||||
|
|
||||||
from common_fields import Field, VmUUIDField, SpecsField
|
from common_fields import Field, VmUUIDField, SpecsField
|
||||||
from helper import check_otp
|
from helper import check_otp, resolve_vm_name
|
||||||
from config import etcd_client as client
|
from config import etcd_client as client
|
||||||
from config import (HOST_PREFIX, VM_PREFIX, IMAGE_PREFIX,
|
from config import HOST_PREFIX, VM_PREFIX, IMAGE_PREFIX, FILE_PREFIX, IMAGE_STORE_PREFIX
|
||||||
FILE_PREFIX, IMAGE_STORE_PREFIX)
|
|
||||||
|
|
||||||
HOST_POOL = HostPool(client, HOST_PREFIX)
|
HOST_POOL = HostPool(client, HOST_PREFIX)
|
||||||
VM_POOL = VmPool(client, VM_PREFIX)
|
VM_POOL = VmPool(client, VM_PREFIX)
|
||||||
|
|
@ -70,30 +85,7 @@ class OTPSchema(BaseSchema):
|
||||||
self.add_error("Wrong Credentials")
|
self.add_error("Wrong Credentials")
|
||||||
|
|
||||||
|
|
||||||
class CreateVMSchema(OTPSchema):
|
########################## Image Operations ###############################################
|
||||||
def __init__(self, data):
|
|
||||||
self.specs = SpecsField(data)
|
|
||||||
|
|
||||||
self.image_uuid = Field("image_uuid", str, data.get("image_uuid", KeyError))
|
|
||||||
self.image_uuid.validation = self.image_uuid_validation
|
|
||||||
|
|
||||||
fields = [self.specs, self.image_uuid]
|
|
||||||
super().__init__(data=data, fields=fields)
|
|
||||||
|
|
||||||
def image_uuid_validation(self):
|
|
||||||
images = client.get_prefix(IMAGE_PREFIX)
|
|
||||||
|
|
||||||
if self.image_uuid.value not in [i.key.split("/")[-1] for i in images]:
|
|
||||||
self.add_error("Image UUID not valid")
|
|
||||||
|
|
||||||
|
|
||||||
class VMStatusSchema(BaseSchema):
|
|
||||||
def __init__(self, data):
|
|
||||||
self.uuid = VmUUIDField(data)
|
|
||||||
|
|
||||||
fields = [self.uuid]
|
|
||||||
|
|
||||||
super().__init__(data, fields)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateImageSchema(BaseSchema):
|
class CreateImageSchema(BaseSchema):
|
||||||
|
|
@ -114,46 +106,149 @@ class CreateImageSchema(BaseSchema):
|
||||||
def file_uuid_validation(self):
|
def file_uuid_validation(self):
|
||||||
file_entry = client.get(os.path.join(FILE_PREFIX, self.uuid.value))
|
file_entry = client.get(os.path.join(FILE_PREFIX, self.uuid.value))
|
||||||
if file_entry is None:
|
if file_entry is None:
|
||||||
self.add_error("Image File with uuid '{}' Not Found".format(self.uuid.value))
|
self.add_error(
|
||||||
|
"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(client.get_prefix(IMAGE_STORE_PREFIX))
|
image_stores = list(client.get_prefix(IMAGE_STORE_PREFIX))
|
||||||
|
|
||||||
image_store = next(filter(lambda s: json.loads(s.value)["name"] == self.image_store.value,
|
image_store = next(
|
||||||
image_stores), None)
|
filter(
|
||||||
|
lambda s: json.loads(s.value)["name"] == self.image_store.value,
|
||||||
|
image_stores,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
if not image_store:
|
if not image_store:
|
||||||
self.add_error("Store '{}' does not exists".format(self.image_store.value))
|
self.add_error("Store '{}' does not exists".format(self.image_store.value))
|
||||||
|
|
||||||
|
|
||||||
|
########################## Host Operations ###############################################
|
||||||
|
|
||||||
|
|
||||||
|
class CreateHostSchema(OTPSchema):
|
||||||
|
def __init__(self, data):
|
||||||
|
self.specs = SpecsField(data)
|
||||||
|
self.hostname = Field("hostname", str, data.get("hostname", KeyError))
|
||||||
|
|
||||||
|
fields = [self.specs, self.hostname]
|
||||||
|
|
||||||
|
super().__init__(data=data, fields=fields)
|
||||||
|
|
||||||
|
def validation(self):
|
||||||
|
if self.realm.value != "ungleich-admin":
|
||||||
|
self.add_error("Invalid Credentials/Insufficient Permission")
|
||||||
|
|
||||||
|
|
||||||
|
########################## VM Operations ###############################################
|
||||||
|
|
||||||
|
|
||||||
|
class CreateVMSchema(OTPSchema):
|
||||||
|
def __init__(self, data):
|
||||||
|
self.specs = SpecsField(data)
|
||||||
|
|
||||||
|
self.vm_name = Field("vm_name", str, data.get("vm_name", KeyError))
|
||||||
|
self.image_uuid = Field("image_uuid", str, data.get("image_uuid", KeyError))
|
||||||
|
|
||||||
|
self.image_uuid.validation = self.image_uuid_validation
|
||||||
|
self.vm_name.validation = self.vm_name_validation
|
||||||
|
|
||||||
|
fields = [self.vm_name, self.specs, self.image_uuid]
|
||||||
|
super().__init__(data=data, fields=fields)
|
||||||
|
|
||||||
|
def image_uuid_validation(self):
|
||||||
|
images = client.get_prefix(IMAGE_PREFIX)
|
||||||
|
|
||||||
|
if self.image_uuid.value not in [i.key.split("/")[-1] for i in images]:
|
||||||
|
self.add_error("Image UUID not valid")
|
||||||
|
|
||||||
|
def vm_name_validation(self):
|
||||||
|
if resolve_vm_name(name=self.vm_name.value, owner=self.name.value):
|
||||||
|
self.add_error(
|
||||||
|
'VM with same name "{}" already exists'.format(self.vm_name.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VMStatusSchema(OTPSchema):
|
||||||
|
def __init__(self, data):
|
||||||
|
data["uuid"] = (
|
||||||
|
resolve_vm_name(
|
||||||
|
name=data.get("vm_name", None),
|
||||||
|
owner=(data.get("in_support_of", None) or data.get("name", None)),
|
||||||
|
)
|
||||||
|
or KeyError
|
||||||
|
)
|
||||||
|
self.uuid = VmUUIDField(data)
|
||||||
|
|
||||||
|
fields = [self.uuid]
|
||||||
|
|
||||||
|
super().__init__(data, fields)
|
||||||
|
|
||||||
|
def validation(self):
|
||||||
|
vm = VM_POOL.get(self.uuid.value)
|
||||||
|
if not (
|
||||||
|
vm.value["owner"] == self.name.value or self.realm.value == "ungleich-admin"
|
||||||
|
):
|
||||||
|
self.add_error("Invalid User")
|
||||||
|
|
||||||
|
|
||||||
class VmActionSchema(OTPSchema):
|
class VmActionSchema(OTPSchema):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
data["uuid"] = (
|
||||||
|
resolve_vm_name(
|
||||||
|
name=data.get("vm_name", None),
|
||||||
|
owner=(data.get("in_support_of", None) or data.get("name", None)),
|
||||||
|
)
|
||||||
|
or KeyError
|
||||||
|
)
|
||||||
self.uuid = VmUUIDField(data)
|
self.uuid = VmUUIDField(data)
|
||||||
self.action = Field("action", str, data.get("action", KeyError))
|
self.action = Field("action", str, data.get("action", KeyError))
|
||||||
|
|
||||||
self.action.validation = self.action_validation
|
self.action.validation = self.action_validation
|
||||||
|
|
||||||
_fields = [self.uuid, self.action]
|
_fields = [self.uuid, self.action]
|
||||||
|
|
||||||
super().__init__(data=data, fields=_fields)
|
super().__init__(data=data, fields=_fields)
|
||||||
|
|
||||||
def action_validation(self):
|
def action_validation(self):
|
||||||
allowed_actions = ["start", "stop", "delete"]
|
allowed_actions = ["start", "stop", "delete"]
|
||||||
if self.action.value not in allowed_actions:
|
if self.action.value not in allowed_actions:
|
||||||
self.add_error("Invalid Action. Allowed Actions are {}".format(allowed_actions))
|
self.add_error(
|
||||||
|
"Invalid Action. Allowed Actions are {}".format(allowed_actions)
|
||||||
|
)
|
||||||
|
|
||||||
def validation(self):
|
def validation(self):
|
||||||
vm = VM_POOL.get(self.uuid.value)
|
vm = VM_POOL.get(self.uuid.value)
|
||||||
if vm.value["owner"] != self.name.value:
|
if not (
|
||||||
|
vm.value["owner"] == self.name.value or self.realm.value == "ungleich-admin"
|
||||||
|
):
|
||||||
self.add_error("Invalid User")
|
self.add_error("Invalid User")
|
||||||
|
|
||||||
if self.action.value == "start" and vm.status == VMStatus.running and vm.hostname != "":
|
if (
|
||||||
|
self.action.value == "start"
|
||||||
|
and vm.status == VMStatus.running
|
||||||
|
and vm.hostname != ""
|
||||||
|
):
|
||||||
self.add_error("VM Already Running")
|
self.add_error("VM Already Running")
|
||||||
|
|
||||||
if self.action.value == "stop" and vm.status == VMStatus.stopped:
|
if self.action.value == "stop":
|
||||||
|
if vm.status == VMStatus.stopped:
|
||||||
self.add_error("VM Already Stopped")
|
self.add_error("VM Already Stopped")
|
||||||
|
elif vm.status != VMStatus.running:
|
||||||
|
self.add_error("Cannot stop non-running VM")
|
||||||
|
|
||||||
|
|
||||||
class VmMigrationSchema(OTPSchema):
|
class VmMigrationSchema(OTPSchema):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
data["uuid"] = (
|
||||||
|
resolve_vm_name(
|
||||||
|
name=data.get("vm_name", None),
|
||||||
|
owner=(data.get("in_support_of", None) or data.get("name", None)),
|
||||||
|
)
|
||||||
|
or KeyError
|
||||||
|
)
|
||||||
|
|
||||||
self.uuid = VmUUIDField(data)
|
self.uuid = VmUUIDField(data)
|
||||||
self.destination = Field("destination", str, data.get("destination", KeyError))
|
self.destination = Field("destination", str, data.get("destination", KeyError))
|
||||||
|
|
||||||
|
|
@ -172,7 +267,9 @@ class VmMigrationSchema(OTPSchema):
|
||||||
|
|
||||||
def validation(self):
|
def validation(self):
|
||||||
vm = VM_POOL.get(self.uuid.value)
|
vm = VM_POOL.get(self.uuid.value)
|
||||||
if vm.owner != self.name.value:
|
if not (
|
||||||
|
vm.value["owner"] == self.name.value or self.realm.value == "ungleich-admin"
|
||||||
|
):
|
||||||
self.add_error("Invalid User")
|
self.add_error("Invalid User")
|
||||||
|
|
||||||
if vm.status != VMStatus.running:
|
if vm.status != VMStatus.running:
|
||||||
|
|
@ -180,17 +277,3 @@ class VmMigrationSchema(OTPSchema):
|
||||||
|
|
||||||
if vm.hostname == os.path.join(HOST_PREFIX, self.destination.value):
|
if vm.hostname == os.path.join(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")
|
||||||
|
|
||||||
|
|
||||||
class CreateHostSchema(OTPSchema):
|
|
||||||
def __init__(self, data):
|
|
||||||
self.specs = SpecsField(data)
|
|
||||||
self.hostname = Field("hostname", str, data.get("hostname", KeyError))
|
|
||||||
|
|
||||||
fields = [self.specs, self.hostname]
|
|
||||||
|
|
||||||
super().__init__(data=data, fields=fields)
|
|
||||||
|
|
||||||
def validation(self):
|
|
||||||
if self.realm.value != "ungleich-admin":
|
|
||||||
self.add_error("Invalid Credentials/Insufficient Permission")
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue