Working dedicated appliance management API. Ref https://github.com/GNS3/gns3-server/issues/1427

This commit is contained in:
grossmj 2018-11-13 14:59:18 +08:00
parent f0fe9d39fa
commit 089fdff4f1
5 changed files with 55 additions and 35 deletions

View File

@ -174,9 +174,12 @@ class Controller:
""" """
appliance = self._appliances.get(appliance_id) appliance = self._appliances.get(appliance_id)
if not appliance:
raise aiohttp.web.HTTPNotFound(text="Appliance ID {} doesn't exist".format(appliance_id))
if appliance.builtin: if appliance.builtin:
raise aiohttp.web.HTTPConflict(text="Appliance ID {} cannot be deleted because it is builtin".format(appliance_id)) raise aiohttp.web.HTTPConflict(text="Appliance ID {} cannot be deleted because it is a builtin".format(appliance_id))
self._appliances.pop(appliance_id) self._appliances.pop(appliance_id)
self.save()
def load_appliances(self): def load_appliances(self):
@ -184,17 +187,17 @@ class Controller:
# Add builtins # Add builtins
builtins = [] builtins = []
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "cloud"), {"node_type": "cloud", "name": "Cloud", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "cloud"), {"appliance_type": "cloud", "name": "Cloud", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "nat"), {"node_type": "nat", "name": "NAT", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "nat"), {"appliance_type": "nat", "name": "NAT", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "vpcs"), {"node_type": "vpcs", "name": "VPCS", "default_name_format": "PC-{0}", "category": 2, "symbol": ":/symbols/vpcs_guest.svg", "properties": {"base_script_file": "vpcs_base_config.txt"}}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "vpcs"), {"appliance_type": "vpcs", "name": "VPCS", "default_name_format": "PC-{0}", "category": 2, "symbol": ":/symbols/vpcs_guest.svg", "properties": {"base_script_file": "vpcs_base_config.txt"}}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_switch"), {"node_type": "ethernet_switch", "console_type": "telnet", "name": "Ethernet switch", "category": 1, "symbol": ":/symbols/ethernet_switch.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_switch"), {"appliance_type": "ethernet_switch", "console_type": "telnet", "name": "Ethernet switch", "category": 1, "symbol": ":/symbols/ethernet_switch.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_hub"), {"node_type": "ethernet_hub", "name": "Ethernet hub", "category": 1, "symbol": ":/symbols/hub.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_hub"), {"appliance_type": "ethernet_hub", "name": "Ethernet hub", "category": 1, "symbol": ":/symbols/hub.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "frame_relay_switch"), {"node_type": "frame_relay_switch", "name": "Frame Relay switch", "category": 1, "symbol": ":/symbols/frame_relay_switch.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "frame_relay_switch"), {"appliance_type": "frame_relay_switch", "name": "Frame Relay switch", "category": 1, "symbol": ":/symbols/frame_relay_switch.svg"}, builtin=True))
builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "atm_switch"), {"node_type": "atm_switch", "name": "ATM switch", "category": 1, "symbol": ":/symbols/atm_switch.svg"}, builtin=True)) builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "atm_switch"), {"appliance_type": "atm_switch", "name": "ATM switch", "category": 1, "symbol": ":/symbols/atm_switch.svg"}, builtin=True))
#FIXME: disable TraceNG #FIXME: disable TraceNG
#if sys.platform.startswith("win"): #if sys.platform.startswith("win"):
# builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "traceng"), {"node_type": "traceng", "name": "TraceNG", "default_name_format": "TraceNG-{0}", "category": 2, "symbol": ":/symbols/traceng.svg", "properties": {}}, builtin=True)) # builtins.append(Appliance(uuid.uuid3(uuid.NAMESPACE_DNS, "traceng"), {"appliance_type": "traceng", "name": "TraceNG", "default_name_format": "TraceNG-{0}", "category": 2, "symbol": ":/symbols/traceng.svg", "properties": {}}, builtin=True))
for b in builtins: for b in builtins:
self._appliances[b.id] = b self._appliances[b.id] = b
@ -464,37 +467,37 @@ class Controller:
vms = [] vms = []
for vm in settings.get("Qemu", {}).get("vms", []): for vm in settings.get("Qemu", {}).get("vms", []):
vm["node_type"] = "qemu" vm["appliance_type"] = "qemu"
vms.append(vm) vms.append(vm)
for vm in settings.get("IOU", {}).get("devices", []): for vm in settings.get("IOU", {}).get("devices", []):
vm["node_type"] = "iou" vm["appliance_type"] = "iou"
vms.append(vm) vms.append(vm)
for vm in settings.get("Docker", {}).get("containers", []): for vm in settings.get("Docker", {}).get("containers", []):
vm["node_type"] = "docker" vm["appliance_type"] = "docker"
vms.append(vm) vms.append(vm)
for vm in settings.get("Builtin", {}).get("cloud_nodes", []): for vm in settings.get("Builtin", {}).get("cloud_nodes", []):
vm["node_type"] = "cloud" vm["appliance_type"] = "cloud"
vms.append(vm) vms.append(vm)
for vm in settings.get("Builtin", {}).get("ethernet_switches", []): for vm in settings.get("Builtin", {}).get("ethernet_switches", []):
vm["node_type"] = "ethernet_switch" vm["appliance_type"] = "ethernet_switch"
vms.append(vm) vms.append(vm)
for vm in settings.get("Builtin", {}).get("ethernet_hubs", []): for vm in settings.get("Builtin", {}).get("ethernet_hubs", []):
vm["node_type"] = "ethernet_hub" vm["appliance_type"] = "ethernet_hub"
vms.append(vm) vms.append(vm)
for vm in settings.get("Dynamips", {}).get("routers", []): for vm in settings.get("Dynamips", {}).get("routers", []):
vm["node_type"] = "dynamips" vm["appliance_type"] = "dynamips"
vms.append(vm) vms.append(vm)
for vm in settings.get("VMware", {}).get("vms", []): for vm in settings.get("VMware", {}).get("vms", []):
vm["node_type"] = "vmware" vm["appliance_type"] = "vmware"
vms.append(vm) vms.append(vm)
for vm in settings.get("VirtualBox", {}).get("vms", []): for vm in settings.get("VirtualBox", {}).get("vms", []):
vm["node_type"] = "virtualbox" vm["appliance_type"] = "virtualbox"
vms.append(vm) vms.append(vm)
for vm in settings.get("VPCS", {}).get("nodes", []): for vm in settings.get("VPCS", {}).get("nodes", []):
vm["node_type"] = "vpcs" vm["appliance_type"] = "vpcs"
vms.append(vm) vms.append(vm)
for vm in settings.get("TraceNG", {}).get("nodes", []): for vm in settings.get("TraceNG", {}).get("nodes", []):
vm["node_type"] = "traceng" vm["appliance_type"] = "traceng"
vms.append(vm) vms.append(vm)
for vm in vms: for vm in vms:

View File

@ -56,8 +56,12 @@ class Appliance:
if "server" in self._settings: if "server" in self._settings:
self._settings["compute_id"] = self._settings.pop("server") self._settings["compute_id"] = self._settings.pop("server")
# The "node_type" setting has been replaced by "appliance_type" setting in version 2.2
if "node_type" in self._settings:
self._settings["appliance_type"] = self._settings.pop("node_type")
# Remove an old IOU setting # Remove an old IOU setting
if settings["node_type"] == "iou" and "image" in settings: if self._settings["appliance_type"] == "iou" and "image" in self._settings:
del self._settings["image"] del self._settings["image"]
self._builtin = builtin self._builtin = builtin
@ -72,7 +76,6 @@ class Appliance:
@settings.setter @settings.setter
def settings(self, settings): def settings(self, settings):
self._settings.update(settings) self._settings.update(settings)
@property @property
@ -84,17 +87,21 @@ class Appliance:
return self._settings["compute_id"] return self._settings["compute_id"]
@property @property
def node_type(self): def appliance_type(self):
return self._settings["node_type"] return self._settings["appliance_type"]
@property @property
def builtin(self): def builtin(self):
return self._builtin return self._builtin
def update(self, **kwargs):
self._settings.update(kwargs)
def __json__(self): def __json__(self):
""" """
Appliance settings. Appliance settings.
""" """
settings = self._settings settings = self._settings
settings.update({"appliance_id": self._id, settings.update({"appliance_id": self._id,
"default_name_format": settings.get("default_name_format", "{name}-{0}"), "default_name_format": settings.get("default_name_format", "{name}-{0}"),

View File

@ -465,14 +465,14 @@ class Project:
Create a node from an appliance Create a node from an appliance
""" """
try: try:
template = self.controller.appliances[appliance_id].settings template = copy.deepcopy(self.controller.appliances[appliance_id].settings)
except KeyError: except KeyError:
msg = "Appliance {} doesn't exist".format(appliance_id) msg = "Appliance {} doesn't exist".format(appliance_id)
log.error(msg) log.error(msg)
raise aiohttp.web.HTTPNotFound(text=msg) raise aiohttp.web.HTTPNotFound(text=msg)
template["x"] = x template["x"] = x
template["y"] = y template["y"] = y
node_type = template.pop("node_type") node_type = template.pop("appliance_type")
compute = self.controller.get_compute(template.pop("compute_id", compute_id)) compute = self.controller.get_compute(template.pop("compute_id", compute_id))
name = template.pop("name") name = template.pop("name")
default_name_format = template.pop("default_name_format", "{name}-{0}") default_name_format = template.pop("default_name_format", "{name}-{0}")

View File

@ -20,6 +20,9 @@ from gns3server.controller import Controller
from gns3server.schemas.node import NODE_OBJECT_SCHEMA from gns3server.schemas.node import NODE_OBJECT_SCHEMA
from gns3server.schemas.appliance import APPLIANCE_USAGE_SCHEMA from gns3server.schemas.appliance import APPLIANCE_USAGE_SCHEMA
import hashlib
import json
from gns3server.schemas.appliance import ( from gns3server.schemas.appliance import (
APPLIANCE_OBJECT_SCHEMA, APPLIANCE_OBJECT_SCHEMA,
APPLIANCE_UPDATE_SCHEMA, APPLIANCE_UPDATE_SCHEMA,
@ -74,10 +77,17 @@ class ApplianceHandler:
output=APPLIANCE_OBJECT_SCHEMA) output=APPLIANCE_OBJECT_SCHEMA)
def get(request, response): def get(request, response):
request_etag = request.headers.get("If-None-Match", "")
controller = Controller.instance() controller = Controller.instance()
appliance = controller.get_appliance(request.match_info["appliance_id"]) appliance = controller.get_appliance(request.match_info["appliance_id"])
response.set_status(200) data = json.dumps(appliance.__json__())
response.json(appliance) appliance_etag = '"' + hashlib.md5(data.encode()).hexdigest() + '"'
if appliance_etag == request_etag:
response.set_status(304)
else:
response.headers["ETag"] = appliance_etag
response.set_status(200)
response.json(appliance)
@Route.put( @Route.put(
r"/appliances/{appliance_id}", r"/appliances/{appliance_id}",
@ -93,8 +103,7 @@ class ApplianceHandler:
controller = Controller.instance() controller = Controller.instance()
appliance = controller.get_appliance(request.match_info["appliance_id"]) appliance = controller.get_appliance(request.match_info["appliance_id"])
#TODO: update appliance! appliance.update(**request.json)
#appliance.settings = request.json
response.set_status(200) response.set_status(200)
response.json(appliance) response.json(appliance)
@ -123,8 +132,6 @@ class ApplianceHandler:
}) })
def list(request, response): def list(request, response):
#old_etag = request.headers.get('If-None-Match', '')
#print("ETAG => ", old_etag)
controller = Controller.instance() controller = Controller.instance()
response.json([c for c in controller.appliances.values()]) response.json([c for c in controller.appliances.values()])

View File

@ -18,6 +18,7 @@
import copy import copy
from .node import NODE_TYPE_SCHEMA from .node import NODE_TYPE_SCHEMA
APPLIANCE_TYPE_SCHEMA = NODE_TYPE_SCHEMA
APPLIANCE_OBJECT_SCHEMA = { APPLIANCE_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
@ -35,7 +36,7 @@ APPLIANCE_OBJECT_SCHEMA = {
"description": "Compute identifier", "description": "Compute identifier",
"type": "string" "type": "string"
}, },
"node_type": NODE_TYPE_SCHEMA, "appliance_type": APPLIANCE_TYPE_SCHEMA,
"name": { "name": {
"description": "Appliance name", "description": "Appliance name",
"type": "string", "type": "string",
@ -53,7 +54,7 @@ APPLIANCE_OBJECT_SCHEMA = {
}, },
}, },
"additionalProperties": True, #TODO: validate all properties "additionalProperties": True, #TODO: validate all properties
"required": ["appliance_id", "compute_id", "node_type", "name", "default_name_format", "symbol"] "required": ["appliance_id", "compute_id", "appliance_type", "name", "default_name_format", "symbol"]
} }
APPLIANCE_CREATE_SCHEMA = copy.deepcopy(APPLIANCE_OBJECT_SCHEMA) APPLIANCE_CREATE_SCHEMA = copy.deepcopy(APPLIANCE_OBJECT_SCHEMA)
@ -62,7 +63,9 @@ APPLIANCE_CREATE_SCHEMA["required"].remove("appliance_id")
APPLIANCE_CREATE_SCHEMA["required"].remove("compute_id") APPLIANCE_CREATE_SCHEMA["required"].remove("compute_id")
APPLIANCE_CREATE_SCHEMA["required"].remove("default_name_format") APPLIANCE_CREATE_SCHEMA["required"].remove("default_name_format")
APPLIANCE_CREATE_SCHEMA["required"].remove("symbol") APPLIANCE_CREATE_SCHEMA["required"].remove("symbol")
APPLIANCE_UPDATE_SCHEMA = copy.deepcopy(APPLIANCE_OBJECT_SCHEMA) APPLIANCE_UPDATE_SCHEMA = copy.deepcopy(APPLIANCE_CREATE_SCHEMA)
#APPLIANCE_UPDATE_SCHEMA["additionalProperties"] = False
del APPLIANCE_UPDATE_SCHEMA["required"]
APPLIANCE_USAGE_SCHEMA = { APPLIANCE_USAGE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",