First step towards the cloud node re-factoring.

This commit is contained in:
grossmj 2016-05-22 19:24:14 -06:00
parent 6d5085910c
commit 7d055968e5
8 changed files with 468 additions and 10 deletions

View File

@ -17,13 +17,15 @@
from ..node_error import NodeError
from .nodes.cloud import Cloud
from .nodes.ethernet_hub import EthernetHub
from .nodes.ethernet_switch import EthernetSwitch
import logging
log = logging.getLogger(__name__)
BUILTIN_NODES = {'ethernet_hub': EthernetHub,
BUILTIN_NODES = {'cloud': Cloud,
'ethernet_hub': EthernetHub,
'ethernet_switch': EthernetSwitch}

View File

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
from ...node_error import NodeError
from ...base_node import BaseNode
import logging
log = logging.getLogger(__name__)
class Cloud(BaseNode):
"""
Cloud.
:param name: name for this cloud
:param node_id: Node identifier
:param project: Project instance
:param manager: Parent VM Manager
"""
def __init__(self, name, node_id, project, manager):
super().__init__(name, node_id, project, manager)
def __json__(self):
return {"name": self.name,
"node_id": self.id,
"project_id": self.project.id}
@asyncio.coroutine
def create(self):
"""
Creates this cloud.
"""
super().create()
log.info('Cloud "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@asyncio.coroutine
def delete(self):
"""
Deletes this cloud.
"""
raise NotImplementedError()
@asyncio.coroutine
def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on this cloud.
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
raise NotImplementedError()
@asyncio.coroutine
def remove_nio(self, port_number):
"""
Removes the specified NIO as member of cloud.
:param port_number: allocated port number
:returns: the NIO that was bound to the allocated port
"""
raise NotImplementedError()
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
:param port_number: allocated port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
"""
raise NotImplementedError()
@asyncio.coroutine
def stop_capture(self, port_number):
"""
Stops a packet capture.
:param port_number: allocated port number
"""
raise NotImplementedError()

View File

@ -15,10 +15,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Hub object that uses the Bridge interface to create a hub with ports.
"""
import asyncio
from ...node_error import NodeError
@ -51,6 +47,9 @@ class EthernetHub(BaseNode):
@asyncio.coroutine
def create(self):
"""
Creates this hub.
"""
super().create()
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))

View File

@ -15,10 +15,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Hub object that uses the Bridge interface to create a hub with ports.
"""
import asyncio
from ...node_error import NodeError
@ -51,6 +47,9 @@ class EthernetSwitch(BaseNode):
@asyncio.coroutine
def create(self):
"""
Creates this switch.
"""
super().create()
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))

View File

@ -28,6 +28,7 @@ from .vmware_handler import VMwareHandler
from .config_handler import ConfigHandler
from .version_handler import VersionHandler
from .notification_handler import NotificationHandler
from .cloud_handler import CloudHandler
from .ethernet_hub_handler import EthernetHubHandler
from .ethernet_switch_handler import EthernetSwitchHandler
from .frame_relay_switch_handler import FrameRelaySwitchHandler

View File

@ -0,0 +1,215 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA
from gns3server.compute.builtin import Builtin
from gns3server.schemas.cloud import (
CLOUD_CREATE_SCHEMA,
CLOUD_OBJECT_SCHEMA,
CLOUD_UPDATE_SCHEMA
)
class CloudHandler:
"""
API entry points for cloud
"""
@Route.post(
r"/projects/{project_id}/cloud/nodes",
parameters={
"project_id": "Project UUID"
},
status_codes={
201: "Instance created",
400: "Invalid request",
409: "Conflict"
},
description="Create a new cloud instance",
input=CLOUD_CREATE_SCHEMA,
output=CLOUD_OBJECT_SCHEMA)
def create(request, response):
builtin_manager = Builtin.instance()
node = yield from builtin_manager.create_node(request.json.pop("name"),
request.match_info["project_id"],
request.json.get("node_id"),
node_type="cloud")
response.set_status(201)
response.json(node)
@Route.get(
r"/projects/{project_id}/cloud/nodes/{node_id}",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Success",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Get a cloud instance",
output=CLOUD_OBJECT_SCHEMA)
def show(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.json(node)
@Route.put(
r"/projects/{project_id}/cloud/nodes/{node_id}",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
200: "Instance updated",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Conflict"
},
description="Update a cloud instance",
input=CLOUD_UPDATE_SCHEMA,
output=CLOUD_OBJECT_SCHEMA)
def update(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
node.updated()
response.json(node)
@Route.delete(
r"/projects/{project_id}/cloud/nodes/{node_id}",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Delete a cloud instance")
def delete(request, response):
builtin_manager = Builtin.instance()
yield from builtin_manager.delete_node(request.match_info["node_id"])
response.set_status(204)
@Route.post(
r"/projects/{project_id}/cloud/nodes/{node_id}/start",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance started",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Start a cloud")
def start(request, response):
Builtin.instance().get_device(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.set_status(204)
@Route.post(
r"/projects/{project_id}/cloud/nodes/{node_id}/stop",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance stopped",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Stop a cloud")
def stop(request, response):
Builtin.instance().get_device(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.set_status(204)
@Route.post(
r"/projects/{project_id}/cloud/nodes/{node_id}/suspend",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance suspended",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Suspend a cloud")
def suspend(request, response):
Builtin.instance().get_device(request.match_info["node_id"], project_id=request.match_info["project_id"])
response.set_status(204)
@Route.post(
r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter on the cloud (always 0)",
"port_number": "Port on the cloud"
},
status_codes={
201: "NIO created",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Add a NIO to a cloud instance",
input=NIO_SCHEMA,
output=NIO_SCHEMA)
def create_nio(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
nio = yield from builtin_manager.create_nio(node, request.json["nio"])
port_number = int(request.match_info["port_number"])
yield from node.add_nio(nio, port_number)
response.set_status(201)
response.json(nio)
@Route.delete(
r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
"adapter_number": "Adapter on the cloud (always 0)",
"port_number": "Port on the cloud"
},
status_codes={
204: "NIO deleted",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Remove a NIO from a cloud instance")
def delete_nio(request, response):
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
port_number = int(request.match_info["port_number"])
nio = yield from node.remove_nio(port_number)
yield from nio.delete()
response.set_status(204)

133
gns3server/schemas/cloud.py Normal file
View File

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2016 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
CLOUD_CREATE_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Request validation to create a new cloud instance",
"type": "object",
"definitions": {
"EthernetHubPort": {
"description": "Ethernet port",
"properties": {
"name": {
"description": "Port name",
"type": "string",
"minLength": 1,
},
"port_number": {
"description": "Port number",
"type": "integer",
"minimum": 1
},
},
"required": ["name", "port_number"],
"additionalProperties": False
},
},
"properties": {
"name": {
"description": "Cloud name",
"type": "string",
"minLength": 1,
},
"node_id": {
"description": "Node UUID",
"oneOf": [
{"type": "string",
"minLength": 36,
"maxLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"}
]
},
# "ports": {
# "type": "array",
# "items": [
# {"type": "object",
# "oneOf": [
# {"$ref": "#/definitions/EthernetHubPort"}
# ]},
# ]
# },
},
"additionalProperties": False,
"required": ["name"]
}
CLOUD_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Cloud instance",
"type": "object",
"definitions": {
"EthernetHubPort": {
"description": "Ethernet port",
"properties": {
"name": {
"description": "Port name",
"type": "string",
"minLength": 1,
},
"port_number": {
"description": "Port number",
"type": "integer",
"minimum": 1
},
},
"required": ["name", "port_number"],
"additionalProperties": False
},
},
"properties": {
"name": {
"description": "Cloud name",
"type": "string",
"minLength": 1,
},
"node_id": {
"description": "Node UUID",
"type": "string",
"minLength": 36,
"maxLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
},
"project_id": {
"description": "Project UUID",
"type": "string",
"minLength": 36,
"maxLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
},
# "ports": {
# "type": "array",
# "items": [
# {"type": "object",
# "oneOf": [
# {"$ref": "#/definitions/EthernetHubPort"}
# ]},
# ]
# },
"status": {
"description": "Node status",
"enum": ["started", "stopped", "suspended"]
},
},
"additionalProperties": False,
"required": ["name", "node_id", "project_id"] #, "ports"]
}
CLOUD_UPDATE_SCHEMA = CLOUD_OBJECT_SCHEMA
del CLOUD_UPDATE_SCHEMA["required"]

View File

@ -88,7 +88,8 @@ NODE_OBJECT_SCHEMA = {
},
"node_type": {
"description": "Type of node",
"enum": ["ethernet_hub",
"enum": ["cloud",
"ethernet_hub",
"ethernet_switch",
"frame_relay_switch",
"atm_switch",