single node,w/o ceph networking implemented

This commit is contained in:
ahmadbilalkhalid 2019-11-11 23:42:57 +05:00
commit da5a600ccb
23 changed files with 866 additions and 147 deletions

View file

@ -4,6 +4,10 @@ from config import etcd_client as client
from config import VM_PREFIX
class Optional:
pass
class Field:
def __init__(self, _name, _type, _value=None):
self.name = _name
@ -18,7 +22,9 @@ class Field:
if self.value == KeyError:
self.add_error("'{}' field is a required field".format(self.name))
else:
if not isinstance(self.value, self.type):
if isinstance(self.value, Optional):
pass
elif not isinstance(self.value, self.type):
self.add_error("Incorrect Type for '{}' field".format(self.name))
else:
self.validation()

View file

@ -24,6 +24,7 @@ REQUEST_PREFIX = config("REQUEST_PREFIX")
FILE_PREFIX = config("FILE_PREFIX")
IMAGE_PREFIX = config("IMAGE_PREFIX")
IMAGE_STORE_PREFIX = config("IMAGE_STORE_PREFIX")
NETWORK_PREFIX = config("NETWORK_PREFIX")
etcd_client = Etcd3Wrapper(host=config("ETCD_URL"))

View file

@ -92,9 +92,11 @@ def resolve_image_name(name, etcd_client):
return image_uuid
def random_bytes(num=6):
return [random.randrange(256) for _ in range(num)]
def generate_mac(uaa=False, multicast=False, oui=None, separator=':', byte_fmt='%02x'):
mac = random_bytes()
if oui:
@ -112,6 +114,7 @@ def generate_mac(uaa=False, multicast=False, oui=None, separator=':', byte_fmt='
mac[0] |= 1 << 1 # set bit 1
return separator.join(byte_fmt % b for b in mac)
def get_ip_addr(mac_address, device):
"""Return IP address of a device provided its mac address / link local address
and the device with which it is connected.
@ -140,3 +143,23 @@ def get_ip_addr(mac_address, device):
if ip.is_global and mac_address == mac:
result.append(ip)
return result
def increment_etcd_counter(etcd_client, key):
kv = etcd_client.get(key)
if kv:
counter = int(kv.value)
counter = counter + 1
else:
counter = 1
etcd_client.put(key, str(counter))
return counter
def get_etcd_counter(etcd_client, key):
kv = etcd_client.get(key)
if kv:
return int(kv.value)
return None

View file

@ -12,7 +12,7 @@ from flask_restful import Resource, Api
from ucloud_common.vm import VMStatus
from ucloud_common.request import RequestEntry, RequestType
from helper import generate_mac, get_ip_addr
from helper import generate_mac, get_ip_addr, get_etcd_counter, increment_etcd_counter
from config import (
etcd_client,
@ -21,6 +21,7 @@ from config import (
HOST_PREFIX,
FILE_PREFIX,
IMAGE_PREFIX,
NETWORK_PREFIX,
logging,
REQUEST_POOL,
VM_POOL,
@ -35,7 +36,6 @@ class CreateVM(Resource):
@staticmethod
def post():
data = request.json
print(data)
validator = schemas.CreateVMSchema(data)
if validator.is_valid():
vm_uuid = uuid4().hex
@ -57,10 +57,10 @@ class CreateVM(Resource):
"image_uuid": validator.image_uuid,
"log": [],
"vnc_socket": "",
"mac": str(generate_mac()),
"network": data["network"],
"metadata": {
"ssh-keys": []
}
},
}
etcd_client.put(vm_key, vm_entry, value_in_json=True)
@ -80,9 +80,8 @@ class VmStatus(Resource):
if validator.is_valid():
vm = VM_POOL.get(os.path.join(VM_PREFIX, data["uuid"]))
vm_value = vm.value.copy()
vm_value["ip"] = list(map(str, get_ip_addr(vm.mac, "br0")))
# vm_value["ip"] = list(map(str, get_ip_addr(vm.mac, "br0")))
vm.value = vm_value
print(vm.value)
return vm.value
else:
return validator.get_errors(), 400
@ -217,7 +216,7 @@ class ListUserVM(Resource):
"specs": vm.value["specs"],
"status": vm.value["status"],
"hostname": vm.value["hostname"],
"mac": vm.value["mac"],
# "mac": vm.value["mac"],
"vnc_socket": None
if vm.value.get("vnc_socket", None) is None
else vm.value["vnc_socket"],
@ -357,6 +356,44 @@ class RemoveSSHKey(Resource):
else:
return validator.get_errors(), 400
class CreateNetwork(Resource):
@staticmethod
def post():
data = request.json
validator = schemas.CreateNetwork(data)
if validator.is_valid():
network_entry = {
"id": increment_etcd_counter(etcd_client, "/v1/counter/vxlan"),
"type": data["type"]
}
network_key = os.path.join(NETWORK_PREFIX, data["name"], data["network_name"])
etcd_client.put(network_key, network_entry, value_in_json=True)
return {"message": "Network successfully added."}
else:
return validator.get_errors(), 400
class ListUserNetwork(Resource):
@staticmethod
def get():
data = request.json
validator = schemas.OTPSchema(data)
if validator.is_valid():
prefix = os.path.join(NETWORK_PREFIX, data["name"])
networks = etcd_client.get_prefix(prefix, value_in_json=True)
user_networks = []
for net in networks:
net.value["name"] = net.key.split("/")[-1]
user_networks.append(net.value)
return {"networks": user_networks}, 200
else:
return validator.get_errors(), 400
api.add_resource(CreateVM, "/vm/create")
api.add_resource(VmStatus, "/vm/status")
@ -368,6 +405,7 @@ api.add_resource(ListPublicImages, "/image/list-public")
api.add_resource(ListUserVM, "/user/vms")
api.add_resource(ListUserFiles, "/user/files")
api.add_resource(ListUserNetwork, "/user/networks")
api.add_resource(AddSSHKey, "/user/add-ssh")
api.add_resource(RemoveSSHKey, "/user/remove-ssh")
@ -376,5 +414,7 @@ api.add_resource(GetSSHKeys, "/user/get-ssh")
api.add_resource(CreateHost, "/host/create")
api.add_resource(ListHost, "/host/list")
api.add_resource(CreateNetwork, "/network/create")
if __name__ == "__main__":
app.run(host="::", debug=True)

View file

@ -23,11 +23,11 @@ import helper
from ucloud_common.host import HostPool, HostStatus
from ucloud_common.vm import VmPool, VMStatus
from common_fields import Field, VmUUIDField
from common_fields import Field, VmUUIDField, Optional
from helper import check_otp, resolve_vm_name
from config import etcd_client as client
from config import (HOST_PREFIX, VM_PREFIX, IMAGE_PREFIX,
FILE_PREFIX, IMAGE_STORE_PREFIX)
FILE_PREFIX, IMAGE_STORE_PREFIX, NETWORK_PREFIX)
HOST_POOL = HostPool(client, HOST_PREFIX)
VM_POOL = VmPool(client, VM_PREFIX)
@ -85,7 +85,6 @@ class OTPSchema(BaseSchema):
super().__init__(data=data, fields=_fields)
def validation(self):
print(self.name.value, self.realm.value, self.token.value)
if check_otp(self.name.value, self.realm.value, self.token.value) != 200:
self.add_error("Wrong Credentials")
@ -206,20 +205,24 @@ class CreateHostSchema(OTPSchema):
class CreateVMSchema(OTPSchema):
def __init__(self, data):
self.parsed_specs = {}
# Fields
self.specs = Field("specs", dict, data.get("specs", KeyError))
self.vm_name = Field("vm_name", str, data.get("vm_name", KeyError))
self.image = Field("image", str, data.get("image", KeyError))
self.network = Field("network", list, data.get("network", KeyError))
# Validation
self.image.validation = self.image_validation
self.vm_name.validation = self.vm_name_validation
self.specs.validation = self.specs_validation
self.network.validation = self.network_validation
fields = [self.vm_name, self.image, self.specs]
fields = [self.vm_name, self.image, self.specs, self.network]
super().__init__(data=data, fields=fields)
def image_validation(self):
try:
image_uuid = helper.resolve_image_name(self.image.value, client)
@ -234,6 +237,18 @@ class CreateVMSchema(OTPSchema):
'VM with same name "{}" already exists'.format(self.vm_name.value)
)
def network_validation(self):
_network = self.network.value
if _network:
for net in _network:
network = client.get(os.path.join(NETWORK_PREFIX,
self.name.value,
net), value_in_json=True)
if not network:
self.add_error("Network with name {} does not exists"\
.format(net))
def specs_validation(self):
ALLOWED_BASE = 10
@ -416,4 +431,30 @@ class GetSSHSchema(OTPSchema):
self.key_name = Field("key_name", str, data.get("key_name", None))
fields = [self.key_name]
super().__init__(data=data, fields=fields)
super().__init__(data=data, fields=fields)
class CreateNetwork(OTPSchema):
def __init__(self, data):
self.network_name = Field("network_name", str, data.get("network_name", KeyError))
self.type = Field("type", str, data.get("type", KeyError))
self.network_name.validation = self.network_name_validation
self.type.validation = self.network_type_validation
fields = [self.network_name, self.type]
super().__init__(data, fields=fields)
def network_name_validation(self):
network = client.get(os.path.join(NETWORK_PREFIX,
self.name.value,
self.network_name.value),
value_in_json=True)
if network:
self.add_error("Network with name {} already exists"\
.format(self.network_name.value))
def network_type_validation(self):
supported_network_types = ["vxlan"]
if self.type.value not in supported_network_types:
self.add_error("Unsupported Network Type. Supported network types are {}".format(supported_network_types))