From 34ae32e0c24777f9ac9cf265447688a3e21680c7 Mon Sep 17 00:00:00 2001 From: Toshiaki Hatano Date: Wed, 21 Aug 2013 02:48:15 +0000 Subject: [PATCH] CLOUDSTACK-2328: Linux native VXLAN support on KVM hypervisor Initial patch for VXLAN support. Fully functional, hopefully, for GuestNetwork - AdvancedZone. Patch Note: in cloudstack-server - Add isolation method VXLAN - Add VxlanGuestNetworkGuru as plugin for VXLAN isolation - Modify NetworkServiceImpl to handle extended vNet range for VXLAN isolation - Add VXLAN isolation option in zoneWizard UI in cloudstack-agent (kvm) - Add modifyvxlan.sh script that handle bridge/vxlan interface manipulation script -- Usage is exactly same to modifyvlan.sh - BridgeVifDriver will call modifyvxlan.sh instead of modifyvlan.sh when VXLAN is used for isolation Database changes: - No change in database structure. - VXLAN isolation uses same tables that VLAN uses to store vNet allocation status. Known Issue: - Some resource still says 'VLAN' in log even if VXLAN is used - in UI, "Network - GuestNetworks" dosen't display VNI -- VLAN ID field displays "N/A" --- api/src/com/cloud/network/Networks.java | 1 + .../com/cloud/network/PhysicalNetwork.java | 3 +- client/pom.xml | 5 + client/tomcatconf/componentContext.xml.in | 8 + .../kvm/resource/BridgeVifDriver.java | 64 ++-- .../resource/LibvirtComputingResource.java | 10 +- .../hypervisor/kvm/resource/OvsVifDriver.java | 6 +- plugins/network-elements/vxlan/pom.xml | 29 ++ .../network/guru/VxlanGuestNetworkGuru.java | 180 ++++++++++++ .../guru/VxlanGuestNetworkGuruTest.java | 273 ++++++++++++++++++ plugins/pom.xml | 1 + scripts/vm/network/vnet/modifyvlan.sh | 32 +- scripts/vm/network/vnet/modifyvxlan.sh | 230 +++++++++++++++ .../com/cloud/network/NetworkManagerImpl.java | 21 +- .../com/cloud/network/NetworkServiceImpl.java | 6 +- 15 files changed, 812 insertions(+), 57 deletions(-) create mode 100644 plugins/network-elements/vxlan/pom.xml create mode 100644 plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java create mode 100644 plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java create mode 100755 scripts/vm/network/vnet/modifyvxlan.sh diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java index f8166c6881a..cdc0f17783d 100755 --- a/api/src/com/cloud/network/Networks.java +++ b/api/src/com/cloud/network/Networks.java @@ -108,6 +108,7 @@ public class Networks { }, Mido("mido", String.class), Pvlan("pvlan", String.class), + Vxlan("vxlan", Long.class), UnDecided(null, null); private final String scheme; diff --git a/api/src/com/cloud/network/PhysicalNetwork.java b/api/src/com/cloud/network/PhysicalNetwork.java index f6cb1a6e0b6..55b18e67ba9 100644 --- a/api/src/com/cloud/network/PhysicalNetwork.java +++ b/api/src/com/cloud/network/PhysicalNetwork.java @@ -39,7 +39,8 @@ public interface PhysicalNetwork extends Identity, InternalIdentity { STT, VNS, MIDO, - SSP; + SSP, + VXLAN; } public enum BroadcastDomainRange { diff --git a/client/pom.xml b/client/pom.xml index 1afe5b898d3..eb27fd2f681 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -104,6 +104,11 @@ cloud-plugin-network-internallb ${project.version} + + org.apache.cloudstack + cloud-plugin-network-vxlan + ${project.version} + org.apache.cloudstack cloud-plugin-hypervisor-xen diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in index f36d0ee02a2..6e4ac662ec1 100644 --- a/client/tomcatconf/componentContext.xml.in +++ b/client/tomcatconf/componentContext.xml.in @@ -91,6 +91,13 @@ + + + + + + + 4.0.0 + cloud-plugin-network-vxlan + Apache CloudStack Plugin - Network VXLAN + + org.apache.cloudstack + cloudstack-plugins + 4.3.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java b/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java new file mode 100644 index 00000000000..69e04e1899c --- /dev/null +++ b/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java @@ -0,0 +1,180 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.guru; + +import javax.ejb.Local; + +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; +import com.cloud.event.EventVO; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.Network; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.State; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetwork.IsolationMethod; +import com.cloud.network.dao.NetworkVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Component +@Local(value=NetworkGuru.class) +public class VxlanGuestNetworkGuru extends GuestNetworkGuru { + private static final Logger s_logger = Logger.getLogger(VxlanGuestNetworkGuru.class); + + public VxlanGuestNetworkGuru() { + super(); + _isolationMethods = new IsolationMethod[] { IsolationMethod.VXLAN }; + } + + @Override + protected boolean canHandle(NetworkOffering offering, final NetworkType networkType, final PhysicalNetwork physicalNetwork) { + // This guru handles only Guest Isolated network that supports Source nat service + if (networkType == NetworkType.Advanced + && isMyTrafficType(offering.getTrafficType()) + && offering.getGuestType() == Network.GuestType.Isolated + && isMyIsolationMethod(physicalNetwork)) { + return true; + } else { + s_logger.trace("We only take care of Guest networks of type " + GuestType.Isolated + " in zone of type " + NetworkType.Advanced); + return false; + } + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + + NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, owner); + if (network == null) { + return null; + } + + network.setBroadcastDomainType(BroadcastDomainType.Vxlan); + + return network; + } + + protected void allocateVnet(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId) throws InsufficientVirtualNetworkCapcityException { + if (network.getBroadcastUri() == null) { + String vnet = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), reservationId, + canUseSystemGuestVlan(network.getAccountId())); + if (vnet == null) { + throw new InsufficientVirtualNetworkCapcityException("Unable to allocate vnet as a " + + "part of network " + network + " implement ", DataCenter.class, dcId); + } + implemented.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(vnet)); + allocateVnetComplete(network, implemented, dcId, physicalNetworkId, reservationId, vnet); + } else { + implemented.setBroadcastUri(network.getBroadcastUri()); + } + } + + // For Test: Mockit cannot mock static method, wrap it + protected void allocateVnetComplete(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId, String vnet) { + //TODO(VXLAN): Add new event type for vxlan? + ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), + EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone vNet: " + vnet + " Network Id: " + network.getId(), 0); + } + + @Override + public Network implement(Network network, NetworkOffering offering, + DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException { + assert (network.getState() == State.Implementing) : "Why are we implementing " + network; + + long dcId = dest.getDataCenter().getId(); + + //get physical network id + Long physicalNetworkId = network.getPhysicalNetworkId(); + + // physical network id can be null in Guest Network in Basic zone, so locate the physical network + if (physicalNetworkId == null) { + physicalNetworkId = _networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType()); + } + + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), network.getBroadcastDomainType(), network.getNetworkOfferingId(), State.Allocated, + network.getDataCenterId(), physicalNetworkId); + + allocateVnet(network, implemented, dcId, physicalNetworkId, context.getReservationId()); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + return implemented; + } + + @Override + public void reserve(NicProfile nic, Network network, + VirtualMachineProfile vm, + DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, + InsufficientAddressCapacityException { + super.reserve(nic, network, vm, dest, context); + } + + @Override + public boolean release(NicProfile nic, + VirtualMachineProfile vm, + String reservationId) { + return super.release(nic, vm, reservationId); + } + + @Override + public void shutdown(NetworkProfile profile, NetworkOffering offering) { + NetworkVO networkObject = _networkDao.findById(profile.getId()); + if (networkObject.getBroadcastDomainType() != BroadcastDomainType.Vxlan || + networkObject.getBroadcastUri() == null) { + s_logger.warn("BroadcastUri is empty or incorrect for guestnetwork " + networkObject.getDisplayText()); + return; + } + + super.shutdown(profile, offering); + } + + @Override + public boolean trash(Network network, NetworkOffering offering, + Account owner) { + return super.trash(network, offering, owner); + } + + + + + +} diff --git a/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java b/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java new file mode 100644 index 00000000000..1172a36d9eb --- /dev/null +++ b/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java @@ -0,0 +1,273 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.guru; + +import java.net.URI; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Command; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.domain.Domain; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.Network; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Service; +import com.cloud.network.Network.State; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.server.ConfigurationServer; +import com.cloud.user.Account; +import com.cloud.vm.ReservationContext; + +import java.util.Arrays; + +public class VxlanGuestNetworkGuruTest { + PhysicalNetworkDao physnetdao = mock (PhysicalNetworkDao.class); + DataCenterDao dcdao = mock(DataCenterDao.class); + AgentManager agentmgr = mock (AgentManager.class); + NetworkManager netmgr = mock (NetworkManager.class); + NetworkModel netmodel = mock (NetworkModel.class); + ConfigurationServer confsvr = mock(ConfigurationServer.class); + + NetworkDao netdao = mock(NetworkDao.class); + VxlanGuestNetworkGuru guru; + + @Before + public void setUp() { + guru = spy( new VxlanGuestNetworkGuru() ); + ((GuestNetworkGuru) guru)._physicalNetworkDao = physnetdao; + guru._physicalNetworkDao = physnetdao; + guru._dcDao = dcdao; + guru._networkModel = netmodel; + guru._networkDao = netdao; + ((GuestNetworkGuru) guru)._configServer = confsvr; + + DataCenterVO dc = mock(DataCenterVO.class); + when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); + when(dc.getGuestNetworkCidr()).thenReturn("10.1.1.1/24"); + + when(dcdao.findById(anyLong())).thenReturn((DataCenterVO) dc); + } + + @Test + public void testCanHandle() { + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + assertTrue(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported TrafficType != Guest + when(offering.getTrafficType()).thenReturn(TrafficType.Management); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported: GuestType Shared + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Shared); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported: Basic networking + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + assertFalse(guru.canHandle(offering, NetworkType.Basic, physnet) == true); + + // Not supported: IsolationMethod != VXLAN + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VLAN" })); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + } + + @Test + public void testDesign() { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + DeploymentPlan plan = mock(DeploymentPlan.class); + Network network = mock(Network.class); + Account account = mock(Account.class); + + Network designednetwork = guru.design(offering, plan, network, account); + assertTrue(designednetwork != null); + assertTrue(designednetwork.getBroadcastDomainType() == BroadcastDomainType.Vxlan); + } + + @Test + public void testImplement() throws InsufficientVirtualNetworkCapcityException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getPhysicalNetworkId()).thenReturn(42L); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + //TODO(VXLAN): doesn't support VNI specified + when(confsvr.getConfigValue((String) any(), (String) any(), anyLong())).thenReturn("true"); + when(dcdao.allocateVnet(anyLong(), anyLong(), anyLong(), (String) any(), eq(true))).thenReturn("42"); + doNothing().when(guru).allocateVnetComplete((Network) any(), (NetworkVO) any(), anyLong(), anyLong(), (String) any(), eq("42")); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + Network implementednetwork = guru.implement(network, offering, dest, res); + assertTrue(implementednetwork != null); + } + + @Test + public void testImplementWithCidr() throws InsufficientVirtualNetworkCapcityException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getGateway()).thenReturn("10.1.1.1"); + when(network.getCidr()).thenReturn("10.1.1.0/24"); + when(network.getPhysicalNetworkId()).thenReturn(42L); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + + //TODO(VXLAN): doesn't support VNI specified + when(confsvr.getConfigValue((String) any(), (String) any(), anyLong())).thenReturn("true"); + when(dcdao.allocateVnet(anyLong(), anyLong(), anyLong(), (String) any(), eq(true))).thenReturn("42"); + doNothing().when(guru).allocateVnetComplete((Network) any(), (NetworkVO) any(), anyLong(), anyLong(), (String) any(), eq("42")); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + Network implementednetwork = guru.implement(network, offering, dest, res); + assertTrue(implementednetwork != null); + assertTrue(implementednetwork.getCidr().equals("10.1.1.0/24")); + assertTrue(implementednetwork.getGateway().equals("10.1.1.1")); + } + + @Test + public void testShutdown() throws InsufficientVirtualNetworkCapcityException, URISyntaxException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vxlan); + when(network.getBroadcastUri()).thenReturn(new URI("vxlan:12345")); + when(network.getPhysicalNetworkId()).thenReturn(42L); + when(netdao.findById(42L)).thenReturn(network); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + NetworkProfile implementednetwork = mock(NetworkProfile.class); + when(implementednetwork.getId()).thenReturn(42L); + when(implementednetwork.getBroadcastUri()).thenReturn(new URI("vxlan:12345")); + when(offering.getSpecifyVlan()).thenReturn(false); + + guru.shutdown(implementednetwork, offering); + verify(implementednetwork, times(1)).setBroadcastUri(null); + } +} diff --git a/plugins/pom.xml b/plugins/pom.xml index 0812642b7c0..032f0e9a942 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -62,6 +62,7 @@ alert-handlers/snmp-alerts alert-handlers/syslog-alerts network-elements/internal-loadbalancer + network-elements/vxlan diff --git a/scripts/vm/network/vnet/modifyvlan.sh b/scripts/vm/network/vnet/modifyvlan.sh index 8ed3905a579..8463d469632 100755 --- a/scripts/vm/network/vnet/modifyvlan.sh +++ b/scripts/vm/network/vnet/modifyvlan.sh @@ -30,8 +30,8 @@ addVlan() { local pif=$2 local vlanDev=$pif.$vlanId local vlanBr=$3 - - vconfig set_name_type DEV_PLUS_VID_NO_PAD + + vconfig set_name_type DEV_PLUS_VID_NO_PAD if [ ! -d /sys/class/net/$vlanDev ] then @@ -39,17 +39,17 @@ addVlan() { if [ $? -gt 0 ] then - # race condition that someone already creates the vlan + # race condition that someone already creates the vlan if [ ! -d /sys/class/net/$vlanDev ] - then - printf "Failed to create vlan $vlanId on pif: $pif." - return 1 - fi + then + printf "Failed to create vlan $vlanId on pif: $pif." + return 1 + fi fi fi # is up? - ifconfig |grep -w $vlanDev > /dev/null + ifconfig |grep -w $vlanDev > /dev/null if [ $? -gt 0 ] then ifconfig $vlanDev up > /dev/null @@ -67,7 +67,7 @@ addVlan() { return 2 fi fi - + brctl setfd $vlanBr 0 fi @@ -92,7 +92,7 @@ addVlan() { then ifconfig $vlanBr up fi - + return 0 } @@ -100,7 +100,7 @@ deleteVlan() { local vlanId=$1 local pif=$2 local vlanDev=$pif.$vlanId - local vlanBr=$3 + local vlanBr=$3 vconfig rem $vlanDev > /dev/null @@ -142,12 +142,12 @@ do v) vflag=1 vlanId="$OPTARG" ;; - p) pflag=1 + p) pflag=1 pif="$OPTARG" ;; - b) bflag=1 - brName="$OPTARG" - ;; + b) bflag=1 + brName="$OPTARG" + ;; ?) usage exit 2 ;; @@ -183,7 +183,7 @@ else then # Delete the vlan deleteVlan $vlanId $pif $brName - + # Always exit with success exit 0 fi diff --git a/scripts/vm/network/vnet/modifyvxlan.sh b/scripts/vm/network/vnet/modifyvxlan.sh new file mode 100755 index 00000000000..45d141a70db --- /dev/null +++ b/scripts/vm/network/vnet/modifyvxlan.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# modifyvnet.sh -- adds and deletes VXLANs from a Routing Server +# set -x + +## TODO(VXLAN): MTU, IPv6 underlying + +usage() { + printf "Usage: %s: -o (add | delete) -v -p -b \n" +} + +addVxlan() { + local vxlanId=$1 + local pif=$2 + local vxlanDev=vxlan$vxlanId + local vxlanBr=$3 + local mcastGrp="239.$(( $vxlanId >> 16 % 256 )).$(( $vxlanId >> 8 % 256 )).$(( $vxlanId % 256 ))" + + ## TODO(VXLAN): $brif (trafficlabel) should be passed from caller because we cannot assume 1:1 mapping between pif and brif. + # lookup bridge interface + local sysfs_dir=/sys/devices/virtual/net/ + local brif=`find ${sysfs_dir}*/brif/ -name $pif | sed -e "s,$sysfs_dir,," | sed -e 's,/brif/.*$,,'` + + if [ "$brif " == " " ] + then + printf "Failed to lookup bridge interface which includes pif: $pif." + return 1 + fi + + # confirm ip address of $brif + ip addr show $brif | grep -w inet + if [ $? -gt 0 ] + then + printf "Failed to find vxlan multicast source ip address on brif: $brif." + return 1 + fi + + # mcast route + ## TODO(VXLAN): Can we assume there're only one IP address which can be multicast src IP on the IF? + ip route get $mcastGrp | grep -w "dev $brif" + if [ $? -gt 0 ] + then + ip route add $mcastGrp/32 dev $brif + if [ $? -gt 0 ] + then + printf "Failed to add vxlan multicast route on brif: $brif." + return 1 + fi + fi + + if [ ! -d /sys/class/net/$vxlanDev ] + then + ip link add $vxlanDev type vxlan id $vxlanId group $mcastGrp ttl 10 dev $brif + + if [ $? -gt 0 ] + then + # race condition that someone already creates the vxlan + if [ ! -d /sys/class/net/$vxlanDev ] + then + printf "Failed to create vxlan $vxlanId on brif: $brif." + return 1 + fi + fi + fi + + # is up? + ip link show $vxlanDev | grep -w UP > /dev/null + if [ $? -gt 0 ] + then + ip link set $vxlanDev up > /dev/null + fi + + if [ ! -d /sys/class/net/$vxlanBr ] + then + brctl addbr $vxlanBr > /dev/null + + if [ $? -gt 0 ] + then + if [ ! -d /sys/class/net/$vxlanBr ] + then + printf "Failed to create br: $vxlanBr" + return 2 + fi + fi + + brctl setfd $vxlanBr 0 + fi + + #pif is eslaved into vxlanBr? + ls /sys/class/net/$vxlanBr/brif/ | grep -w "$vxlanDev" > /dev/null + if [ $? -gt 0 ] + then + brctl addif $vxlanBr $vxlanDev > /dev/null + if [ $? -gt 0 ] + then + ls /sys/class/net/$vxlanBr/brif/ | grep -w "$vxlanDev" > /dev/null + if [ $? -gt 0 ] + then + printf "Failed to add vxlan: $vxlanDev to $vxlanBr" + return 3 + fi + fi + fi + + # is vxlanBr up? + ip link show $vxlanBr | grep -w UP > /dev/null + if [ $? -gt 0 ] + then + ip link set $vxlanBr up + fi + + return 0 +} + +deleteVxlan() { + local vxlanId=$1 + local pif=$2 + local vxlanDev=vxlan$vxlanId + local vxlanBr=$3 + local mcastGrp="239.$(( $vxlanId >> 16 % 256 )).$(( $vxlanId >> 8 % 256 )).$(( $vxlanId % 256 ))" + + ip route del $mcastGrp/32 dev $brif + + ip link delete $vxlanDev + + if [ $? -gt 0 ] + then + printf "Failed to del vxlan: $vxlanId" + printf "Continue..." + fi + + ip link set $vxlanBr down + + if [ $? -gt 0 ] + then + return 1 + fi + + brctl delbr $vxlanBr + + if [ $? -gt 0 ] + then + printf "Failed to del bridge $vxlanBr" + return 1 + fi + + return 0 +} + +op= +vxlanId= +option=$@ + +while getopts 'o:v:p:b:' OPTION +do + case $OPTION in + o) oflag=1 + op="$OPTARG" + ;; + v) vflag=1 + vxlanId="$OPTARG" + ;; + p) pflag=1 + pif="$OPTARG" + ;; + b) bflag=1 + brName="$OPTARG" + ;; + ?) usage + exit 2 + ;; + esac +done + +# Check that all arguments were passed in +if [ "$oflag$vflag$pflag$bflag" != "1111" ] +then + usage + exit 2 +fi + +# Do we support Vxlan? +lsmod|grep ^vxlan >& /dev/null +if [ $? -gt 0 ] +then + modprobe=`modprobe vxlan 2>&1` + if [ $? -gt 0 ] + then + printf "Failed to load vxlan kernel module: $modprobe" + exit 1 + fi +fi + +if [ "$op" == "add" ] +then + # Add the vxlan + addVxlan $vxlanId $pif $brName + + # If the add fails then return failure + if [ $? -gt 0 ] + then + exit 1 + fi +else + if [ "$op" == "delete" ] + then + # Delete the vxlan + deleteVxlan $vxlanId $pif $brName + + # Always exit with success + exit 0 + fi +fi + diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 00103e32618..15f413b30ee 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -327,7 +327,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L UsageEventDao _usageEventDao; @Inject NetworkModel _networkModel; - @Inject + @Inject NicSecondaryIpDao _nicSecondaryIpDao; @Inject UserIpv6AddressDao _ipv6Dao; @@ -631,7 +631,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L try { if (predefined == null || (offering.getTrafficType() != TrafficType.Guest && predefined.getCidr() == null && predefined.getBroadcastUri() == null && - !(predefined.getBroadcastDomainType() == BroadcastDomainType.Vlan || predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch))) { + !(predefined.getBroadcastDomainType() == BroadcastDomainType.Vlan || predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch || + predefined.getBroadcastDomainType() == BroadcastDomainType.Vxlan))) { List configs = _networksDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId()); if (configs.size() > 0) { if (s_logger.isDebugEnabled()) { @@ -1729,12 +1730,12 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } else if (zone.getNetworkType() == NetworkType.Advanced) { if (zone.isSecurityGroupEnabled()) { - if (ipv6) { - throw new InvalidParameterValueException("IPv6 is not supported with security group!"); - } - if (isolatedPvlan != null) { - throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); - } + if (ipv6) { + throw new InvalidParameterValueException("IPv6 is not supported with security group!"); + } + if (isolatedPvlan != null) { + throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); + } // Only Account specific Isolated network with sourceNat service disabled are allowed in security group // enabled zone if ( ntwkOff.getGuestType() != GuestType.Shared ){ @@ -1754,6 +1755,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } } + //TODO(VXLAN): Support VNI specified // VlanId can be specified only when network offering supports it boolean vlanSpecified = (vlanId != null); if (vlanSpecified != ntwkOff.getSpecifyVlan()) { @@ -1812,9 +1814,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L vlanId + " already exists " + "in zone " + zoneId); } } - - - } // If networkDomain is not specified, take it from the global configuration diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index eadd7bc757f..27f1b088d46 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -187,7 +187,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { private static final long MAX_VLAN_ID = 4095L; // 2^12 - 1 private static final long MIN_GRE_KEY = 0L; private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1 - + private static final long MIN_VXLAN_VNI = 0L; + private static final long MAX_VXLAN_VNI = 16777215L; // 2^24 -1 @Inject DataCenterDao _dcDao = null; @@ -2645,6 +2646,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (network.getIsolationMethods().contains("GRE")) { minVnet = MIN_GRE_KEY; maxVnet = MAX_GRE_KEY; + } else if (network.getIsolationMethods().contains("VXLAN")) { + minVnet = MIN_VXLAN_VNI; + maxVnet = MAX_VXLAN_VNI; } String rangeMessage = " between " + minVnet + " and " + maxVnet; if (VnetRange.length == 1 && VnetRange[0].equals("")) {