single node,w/o ceph networking implemented
This commit is contained in:
parent
2a66be07a6
commit
da5a600ccb
23 changed files with 866 additions and 147 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
54
api/main.py
54
api/main.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue