
Signed-off-by: Lucio Correia <luciojhc@linux.vnet.ibm.com> --- API.json | 32 ++++++++++++++++++++++++++++++++ control/networks.py | 1 + docs/API.md | 9 +++++++++ i18n.py | 8 +++++--- model/networks.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/API.json b/API.json index ff505b1..86cd406 100644 --- a/API.json +++ b/API.json @@ -384,6 +384,38 @@ "interfaces": { "description": "An array of network interfaces of the host", "type": "array", + "minItems": 1, + "error": "KCHNET0014E" + }, + "vlan_id": { + "description": "Network's VLAN ID", + "type": "integer", + "maximum": 4094, + "minimum": 1, + "error": "KCHNET0015E" + } + } + }, + "network_update": { + "type": "object", + "additionalProperties": false, + "error": "KCHAPI0001E", + "properties": { + "name": { + "description": "The new name of the network", + "type": "string", + "minLength": 1, + "pattern": "^[^/\"]*$", + "error": "KCHNET0011E" + }, + "subnet": { + "description": "Network segment in slash-separated format with ip address and prefix or netmask", + "type": "string", + "error": "KCHNET0013E" + }, + "interfaces": { + "description": "An array of network interfaces of the host", + "type": "array", "error": "KCHNET0014E" }, "vlan_id": { diff --git a/control/networks.py b/control/networks.py index 8ba2206..c87b5a6 100644 --- a/control/networks.py +++ b/control/networks.py @@ -27,6 +27,7 @@ NETWORKS_REQUESTS = { NETWORK_REQUESTS = { 'DELETE': {'default': "Remove virtual network '%(ident)s'"}, + 'PUT': {'default': "Update virtual network '%(ident)s'"}, 'POST': { 'activate': "Activate virtual network '%(ident)s'", 'deactivate': "Deactivate virtual network '%(ident)s'", diff --git a/docs/API.md b/docs/API.md index f1a13b4..a94f0f9 100644 --- a/docs/API.md +++ b/docs/API.md @@ -736,6 +736,15 @@ A interface represents available interface on host. * **DELETE**: Remove the Network * **POST**: *See Network Actions* +* **PUT**: Update the Network. The network type cannot be changed. + * name *(optional)*: The new name of the Network + * subnet *(optional)*: Network segment in slash-separated format with ip address and prefix. + Applies only to NAT and isolated networks + * interfaces *(optional)*: An array of network interfaces that belongs to this network. + Applies only to bridge, macvtap and VEPA networks. For bridge and macvtap, + only one interface is allowed. For VEPA, you can specify multiple interfaces. + * vlan_id *(optional)*: VLAN tagging ID for the bridge network. + **Actions (POST):** * activate: Activate an inactive Network diff --git a/i18n.py b/i18n.py index 7cce796..3712a02 100644 --- a/i18n.py +++ b/i18n.py @@ -253,10 +253,10 @@ messages = { "KCHNET0002E": _("Network %(name)s does not exist"), "KCHNET0003E": _("Subnet %(subnet)s specified for network %(network)s is not valid."), "KCHNET0004E": _("Specify a network interface to create bridged or macvtap networks."), - "KCHNET0005E": _("Unable to delete active network %(name)s"), + "KCHNET0005E": _("Unable to delete or update active network %(name)s"), "KCHNET0006E": _("Interface %(iface)s specified for network %(network)s is already in use"), "KCHNET0007E": _("Interface should be bare NIC, bonding or bridge device."), - "KCHNET0008E": _("Unable to create network %(name)s. Details: %(err)s"), + "KCHNET0008E": _("Unable to create or update network %(name)s. Details: %(err)s"), "KCHNET0009E": _("Unable to find a free IP address for network '%(name)s'"), "KCHNET0010E": _("The interface %(iface)s already exists."), "KCHNET0011E": _("Network name must be a string without slashes (/) or quotes (\")"), @@ -265,7 +265,7 @@ messages = { "KCHNET0014E": _("Network interfaces must be an array."), "KCHNET0015E": _("Network VLAN ID must be an integer between 1 and 4094"), "KCHNET0016E": _("Specify name and type to create a Network"), - "KCHNET0017E": _("Unable to delete network %(name)s. There are some virtual machines %(vms)s and/or templates linked to this network."), + "KCHNET0017E": _("Unable to delete or update network %(name)s. There are some virtual machines %(vms)s and/or templates linked to this network."), "KCHNET0018E": _("Unable to deactivate network %(name)s. There are some virtual machines %(vms)s and/or templates linked to this network."), "KCHNET0019E": _("Bridge device %(name)s can not be the trunk device of a VLAN."), "KCHNET0020E": _("Failed to activate interface %(iface)s: %(err)s."), @@ -277,6 +277,8 @@ messages = { "KCHNET0028E": _("Interface should be bare NIC or bonding."), "KCHNET0029E": _("Network interfaces parameter must contain at least one interface."), "KCHNET0030E": _("Only one interface is allowed for 'bridge' and 'macvtap' networks."), + "KCHNET0031E": _("Subnet is not a valid parameter for this type of virtual network."), + "KCHNET0032E": _("VLAN ID and interfaces are not valid parameters for this type of virtual network."), "KCHSR0001E": _("Storage server %(server)s was not used by Kimchi"), diff --git a/model/networks.py b/model/networks.py index 2664af3..08b2690 100644 --- a/model/networks.py +++ b/model/networks.py @@ -17,6 +17,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import copy import ipaddr import libvirt import sys @@ -33,6 +34,8 @@ from wok.plugins.kimchi import netinfo from wok.plugins.kimchi import network as knetwork from wok.plugins.kimchi.config import kimchiPaths from wok.plugins.kimchi.model.config import CapabilitiesModel +from wok.plugins.kimchi.netinfo import get_vlan_device, is_bridge, is_vlan +from wok.plugins.kimchi.netinfo import ports from wok.plugins.kimchi.osinfo import defaults as tmpl_defaults from wok.plugins.kimchi.xmlutils.interface import get_iface_xml from wok.plugins.kimchi.xmlutils.network import create_linux_bridge_xml @@ -109,7 +112,7 @@ class NetworksModel(object): try: network = conn.networkDefineXML(xml.encode("utf-8")) - network.setAutostart(True) + network.setAutostart(params.get('autostart', True)) except libvirt.libvirtError as e: raise OperationFailed("KCHNET0008E", {'name': name, 'err': e.get_error_message()}) @@ -339,6 +342,7 @@ class NetworkModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] self.objstore = kargs['objstore'] + self.collection = NetworksModel(**kargs) def lookup(self, name): network = self.get_network(self.conn.get(), name) @@ -499,3 +503,45 @@ class NetworkModel(object): iface = conn.interfaceLookupByName(bridge) iface.isActive() and iface.destroy(0) iface.undefine() + + def update(self, name, params): + info = self.lookup(name) + original = copy.deepcopy(info) + original['name'] = name + + # validate update parameters + connection = info['connection'] + if connection in ['bridge', 'macvtap', 'vepa']: + if params.get('subnet'): + raise InvalidParameter("KCHNET0031E") + elif connection in ['nat', 'isolated']: + if params.get('vlan_id') or params.get('interfaces'): + raise InvalidParameter("KCHNET0032E") + + # get target device if bridge was created by Kimchi + if connection == 'bridge': + iface = info['interfaces'][0] + if is_bridge(iface) and iface.startswith(KIMCHI_BRIDGE_PREFIX): + port = ports(iface)[0] + if is_vlan(port): + dev = get_vlan_device(port) + info['interfaces'] = original['interfaces'] = [dev] + # nic + else: + info['interfaces'] = original['interfaces'] = [port] + + # merge parameters + info.update(params) + + # delete original network + self.delete(name) + + try: + # create new network + network = self.collection.create(info) + except: + # restore original network + self.collection.create(original) + raise + + return network -- 1.9.1