From 16ddc3414ae0269ecb8903f670496e45f68d6ef6 Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Fri, 14 Mar 2014 11:04:42 +0530 Subject: [PATCH 01/31] CLOUDSTACK-6052: List VM enhancement to support querying with multiple VM IDs Annotated the 'ids' parameter with "since = 4.4" --- .../org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index e5576e74cab..6c95a1215ed 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -68,8 +68,7 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the virtual machine") private Long id; - @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=UserVmResponse.class, - description="the IDs of the virtual machines, mutually exclusive with id") + @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=UserVmResponse.class, description="the IDs of the virtual machines, mutually exclusive with id", since = "4.4") private List ids; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the virtual machine") From 286974488eab03efddfea35962161a224e997ab0 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Mon, 10 Mar 2014 03:44:43 -0400 Subject: [PATCH 02/31] CLOUDSTACK-6215: Updating test case to accomodate condition for VMware Signed-off-by: SrikanteswaraRao Talluri --- .../component/test_base_image_updation.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/integration/component/test_base_image_updation.py b/test/integration/component/test_base_image_updation.py index af968cc1de1..e31aacfa7e6 100644 --- a/test/integration/component/test_base_image_updation.py +++ b/test/integration/component/test_base_image_updation.py @@ -25,11 +25,10 @@ """ #Import Local Modules -import marvin from marvin.codes import (PASS, RECURRING) from nose.plugins.attrib import attr -from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.cloudstackTestCase import cloudstackTestCase from marvin.integration.lib.base import (ServiceOffering, Account, @@ -51,7 +50,6 @@ from marvin.integration.lib.utils import (validateList, cleanup_resources) import time -from datetime import datetime, timedelta class Services: """Test Base Image Updation @@ -117,6 +115,18 @@ class Services: "ispublic": True, "isextractable": True, + }, + "VMware": { + "displaytext": "Public Template - VMware", + "name": "Public template -VMware", + "ostype": "CentOS 5.3 (64-bit)", + "url": "http://download.cloud.com/releases/2.2.0/CentOS5.3-x86_64.ova", + "hypervisor": "vmware", + "format": "ova", + "isfeatured": True, + "ispublic": True, + "isextractable": True, + } }, "template": { @@ -503,8 +513,7 @@ class TestBaseImageUpdate(cloudstackTestCase): "VM created with IsVolatile=False doesn't have same ip after restore. Got : %s Expected : %s" %(vm_without_reset.nic[0].ipaddress, self.vm_without_reset.nic[0].ipaddress) ) - - return + return @attr(tags=["advanced", "basic"]) def test_04_reoccuring_snapshot_rules(self): From 6a5d3e96c96c0cd5ec9e1ec1e1c4ed42dc73f84f Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Fri, 14 Mar 2014 13:35:22 +0530 Subject: [PATCH 03/31] CLOUDSTACK-6238: NPE in HostStatsCollector. --- server/src/com/cloud/server/StatsCollector.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java index 067ed003f39..50aa93cd5ee 100755 --- a/server/src/com/cloud/server/StatsCollector.java +++ b/server/src/com/cloud/server/StatsCollector.java @@ -343,7 +343,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc } for (HostVO host : gpuEnabledHosts) { HashMap> groupDetails = _resourceMgr.getGPUStatistics(host); - _resourceMgr.updateGPUDetails(host.getId(), groupDetails); + if (groupDetails != null) { + _resourceMgr.updateGPUDetails(host.getId(), groupDetails); + } } hostIds = _hostGpuGroupsDao.listHostIds(); } catch (Throwable t) { From f7337527cf9dbb35ec4c60621d4b37173635660c Mon Sep 17 00:00:00 2001 From: Harikrishna Patnala Date: Fri, 14 Mar 2014 15:00:38 +0530 Subject: [PATCH 04/31] CLOUDSTACK-6090: Virtual Router Service Failure Alerting Signed-off-by: Koushik Das --- .../agent/api/GetRouterAlertsAnswer.java | 62 +++++ .../api/routing/GetRouterAlertsCommand.java | 46 ++++ .../VirtualRoutingResource.java | 45 +++- ...spring-engine-schema-core-daos-context.xml | 1 + .../dao/OpRouterMonitorServiceDao.java | 25 ++ .../dao/OpRouterMonitorServiceDaoImpl.java | 28 ++ .../network/dao/OpRouterMonitorServiceVO.java | 52 ++++ .../VirtualNetworkApplianceManagerImpl.java | 67 +++++ setup/db/db/schema-430to440.sql | 8 + .../config/opt/cloud/bin/getRouterAlerts.sh | 70 +++++ .../debian/config/root/monitorServices.py | 3 + .../smoke/test_VirtualRouter_alerts.py | 244 ++++++++++++++++++ 12 files changed, 650 insertions(+), 1 deletion(-) create mode 100644 core/src/com/cloud/agent/api/GetRouterAlertsAnswer.java create mode 100644 core/src/com/cloud/agent/api/routing/GetRouterAlertsCommand.java create mode 100644 engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDao.java create mode 100644 engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java create mode 100644 engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java create mode 100644 systemvm/patches/debian/config/opt/cloud/bin/getRouterAlerts.sh create mode 100644 test/integration/smoke/test_VirtualRouter_alerts.py diff --git a/core/src/com/cloud/agent/api/GetRouterAlertsAnswer.java b/core/src/com/cloud/agent/api/GetRouterAlertsAnswer.java new file mode 100644 index 00000000000..06a7a7a84eb --- /dev/null +++ b/core/src/com/cloud/agent/api/GetRouterAlertsAnswer.java @@ -0,0 +1,62 @@ +// 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.agent.api; + + +import com.cloud.agent.api.routing.GetRouterAlertsCommand; + +public class GetRouterAlertsAnswer extends Answer { + + String routerName; + String[] alerts; + String timeStamp; + + protected GetRouterAlertsAnswer() { + } + + public GetRouterAlertsAnswer(GetRouterAlertsCommand cmd, String alerts[], String timeStamp) { + super(cmd, true, null); + this.alerts = alerts; + this.timeStamp = timeStamp; + } + + + public GetRouterAlertsAnswer(GetRouterAlertsCommand cmd, Exception ex) { + super(cmd, ex); + } + + public void setAlerts(String[] alerts) { + this.alerts = alerts; + } + + public String[] getAlerts() { + return alerts; + } + + public void setTimeStamp(String timeStamp) { + this.timeStamp = timeStamp; + } + + public String getTimeStamp() { + return timeStamp; + } + + public String getRouterName() { + return routerName; + } +} diff --git a/core/src/com/cloud/agent/api/routing/GetRouterAlertsCommand.java b/core/src/com/cloud/agent/api/routing/GetRouterAlertsCommand.java new file mode 100644 index 00000000000..a6769efa4ae --- /dev/null +++ b/core/src/com/cloud/agent/api/routing/GetRouterAlertsCommand.java @@ -0,0 +1,46 @@ +// 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.agent.api.routing; + + +public class GetRouterAlertsCommand extends NetworkElementCommand { + + private String previousAlertTimeStamp; + + protected GetRouterAlertsCommand() { + + } + + @Override + public boolean executeInSequence() { + return false; + } + + public GetRouterAlertsCommand(String timeStamp) { + this.previousAlertTimeStamp = timeStamp; + } + + public String getPreviousAlertTimeStamp() { + return previousAlertTimeStamp; + } + + @Override + public boolean isQuery() { + return true; + } +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 3712abad7d8..e13fbf651cb 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -32,6 +32,8 @@ import com.cloud.agent.api.routing.DeleteIpAliasCommand; import com.cloud.agent.api.routing.DhcpEntryCommand; import com.cloud.agent.api.routing.DnsMasqConfigCommand; import com.cloud.agent.api.routing.GroupAnswer; +import com.cloud.agent.api.routing.GetRouterAlertsCommand; +import com.cloud.agent.api.GetRouterAlertsAnswer; import com.cloud.agent.api.routing.IpAliasTO; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.IpAssocVpcCommand; @@ -102,6 +104,7 @@ public class VirtualRoutingResource { protected static final String IPASSOC = "ipassoc.sh"; protected static final String LB = "loadbalancer.sh"; protected static final String MONITOR_SERVICE = "monitor_service.sh"; + protected static final String ROUTER_ALERTS = "getRouterAlerts.sh"; protected static final String PASSWORD = "savepassword.sh"; protected static final String RVR_CHECK = "checkrouter.sh"; protected static final String RVR_BUMPUP_PRI = "bumpup_priority.sh"; @@ -275,7 +278,9 @@ public class VirtualRoutingResource { } else if (cmd instanceof GetDomRVersionCmd) { return execute((GetDomRVersionCmd)cmd); } else if (cmd instanceof CheckS2SVpnConnectionsCommand) { - return execute((CheckS2SVpnConnectionsCommand)cmd); + return execute((CheckS2SVpnConnectionsCommand) cmd); + } else if (cmd instanceof GetRouterAlertsCommand) { + return execute((GetRouterAlertsCommand)cmd); } else { s_logger.error("Unknown query command in VirtualRoutingResource!"); return Answer.createUnsupportedCommandAnswer(cmd); @@ -642,6 +647,29 @@ public class VirtualRoutingResource { return new CheckS2SVpnConnectionsAnswer(cmd, result.isSuccess(), result.getDetails()); } + private GetRouterAlertsAnswer execute(GetRouterAlertsCommand cmd) { + + String args = null; + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + if (cmd.getPreviousAlertTimeStamp() != null) { + args = cmd.getPreviousAlertTimeStamp(); + } + + ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.ROUTER_ALERTS, args); + String alerts[] = null; + String lastAlertTimestamp = null; + // CallHostPlugin results "success" when there are no alerts on virtual router + if (result.isSuccess()) { + if (!result.getDetails().isEmpty() && !result.getDetails().equals("No Alerts")) { + alerts = result.getDetails().split("\\\\n"); + String[] lastAlert = alerts[alerts.length - 1].split(" "); + lastAlertTimestamp = lastAlert[0] + " " + lastAlert[1]; + } + } + + return new GetRouterAlertsAnswer(cmd, alerts, lastAlertTimestamp); + } + protected Answer execute(CheckRouterCommand cmd) { final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.RVR_CHECK, null); if (!result.isSuccess()) { @@ -732,6 +760,21 @@ public class VirtualRoutingResource { return cfg; } + protected List generateConfig(GetRouterAlertsCommand cmd) { + LinkedList cfg = new LinkedList<>(); + + String args = null; + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + if (cmd.getPreviousAlertTimeStamp() != null) { + args = "getRouterAlerts.sh " + routerIp + " " + cmd.getPreviousAlertTimeStamp(); + } else { + args = "getRouterAlerts.sh " + routerIp; + } + + cfg.add(new ConfigItem(VRScripts.ROUTER_ALERTS, args)); + return cfg; + } + protected List generateConfig(SetupGuestNetworkCommand cmd) { LinkedList cfg = new LinkedList<>(); diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 765d86c34ad..489b37d04b7 100644 --- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -157,6 +157,7 @@ + diff --git a/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDao.java b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDao.java new file mode 100644 index 00000000000..ebc0f1af227 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDao.java @@ -0,0 +1,25 @@ +// 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.dao; + + +import com.cloud.utils.db.GenericDao; + +public interface OpRouterMonitorServiceDao extends GenericDao { + +} diff --git a/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java new file mode 100644 index 00000000000..b92512e6d91 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java @@ -0,0 +1,28 @@ +// 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.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; + +@Component +@Local(value=OpRouterMonitorServiceDao.class) +public class OpRouterMonitorServiceDaoImpl extends GenericDaoBase implements OpRouterMonitorServiceDao { +} diff --git a/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java new file mode 100644 index 00000000000..c70bba357c1 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java @@ -0,0 +1,52 @@ +package com.cloud.network.dao; + +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Id; +import javax.persistence.Column; + + +@Entity +@Table(name = "op_router_monitoring_services") +public class OpRouterMonitorServiceVO implements InternalIdentity { + + @Id + @Column(name="vm_id") + Long id; + + @Column(name="router_name") + private String name; + + @Column(name="last_alert_timestamp") + private String lastAlertTimestamp; + + + public OpRouterMonitorServiceVO() {} + + public OpRouterMonitorServiceVO(long vmId, String name, String lastAlertTimestamp) { + this.id = vmId; + this.name = name; + this.lastAlertTimestamp = lastAlertTimestamp; + } + + + @Override + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getLastAlertTimestamp() { + return lastAlertTimestamp; + } + + public void setLastAlertTimestamp (String timestamp) { + this.lastAlertTimestamp = timestamp; + } + +} diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index eeab91d81ab..c692491a34b 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -55,6 +55,7 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.cloudstack.alert.AlertService.AlertType; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -73,6 +74,8 @@ import com.cloud.agent.api.NetworkUsageAnswer; import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.routing.GetRouterAlertsCommand; +import com.cloud.agent.api.GetRouterAlertsAnswer; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.routing.AggregationControlCommand; @@ -179,6 +182,7 @@ import com.cloud.network.dao.MonitoringServiceDao; import com.cloud.network.dao.MonitoringServiceVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.OpRouterMonitorServiceDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.RemoteAccessVpnDao; import com.cloud.network.dao.Site2SiteCustomerGatewayDao; @@ -188,6 +192,7 @@ import com.cloud.network.dao.Site2SiteVpnGatewayDao; import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.dao.OpRouterMonitorServiceVO; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; @@ -394,6 +399,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V AsyncJobManager _asyncMgr; @Inject protected ApiAsyncJobDispatcher _asyncDispatcher; + @Inject + OpRouterMonitorServiceDao _opRouterMonitorServiceDao; int _routerRamSize; int _routerCpuMHz; @@ -1348,6 +1355,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V updateSite2SiteVpnConnectionState(routers); + getRouterAlerts(); + final List networks = _networkDao.listRedundantNetworks(); s_logger.debug("Found " + networks.size() + " networks to update RvR status. "); for (final NetworkVO network : networks) { @@ -1362,6 +1371,64 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V } } + private void getRouterAlerts() { + try{ + List routersInIsolatedNetwork = _routerDao.listByStateAndNetworkType(State.Running, GuestType.Isolated, mgmtSrvrId); + List routersInSharedNetwork = _routerDao.listByStateAndNetworkType(State.Running, GuestType.Shared, mgmtSrvrId); + + List routers = new ArrayList(); + routers.addAll(routersInIsolatedNetwork); + routers.addAll(routersInSharedNetwork); + s_logger.debug("Found " + routers.size() + " running routers. "); + + for (final DomainRouterVO router : routers) { + if (router.getVpcId() != null) { + continue; + } + String privateIP = router.getPrivateIpAddress(); + + if (privateIP != null) { + OpRouterMonitorServiceVO opRouterMonitorServiceVO = _opRouterMonitorServiceDao.findById(router.getId()); + + GetRouterAlertsCommand command = null; + if (opRouterMonitorServiceVO == null) { + command = new GetRouterAlertsCommand(null); + } else { + command = new GetRouterAlertsCommand(opRouterMonitorServiceVO.getLastAlertTimestamp()); + } + + command.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + GetRouterAlertsAnswer answer = null; + try { + answer = (GetRouterAlertsAnswer) _agentMgr.easySend(router.getHostId(), command); + String alerts[] = answer.getAlerts(); + if (alerts != null ) { + for (String alert: alerts) { + _alertMgr.sendAlert(AlertType.ALERT_TYPE_DOMAIN_ROUTER, router.getDataCenterId(), router.getPodIdToDeployIn(), "Monitoring Service on VR " + router.getInstanceName(), alert); + } + String lastAlertTimeStamp = answer.getTimeStamp(); + if (opRouterMonitorServiceVO == null) { + opRouterMonitorServiceVO = new OpRouterMonitorServiceVO(router.getId(), router.getHostName(), lastAlertTimeStamp); + _opRouterMonitorServiceDao.persist(opRouterMonitorServiceVO); + } else { + opRouterMonitorServiceVO.setLastAlertTimestamp(lastAlertTimeStamp); + _opRouterMonitorServiceDao.update(opRouterMonitorServiceVO.getId(), opRouterMonitorServiceVO); + } + } + } catch (Exception e) { + s_logger.warn("Error while collecting alerts from router: " + router.getInstanceName() + " from host: " + router.getHostId(), e); + continue; + } + } + } + } catch (Exception e) { + s_logger.warn("Error while collecting alerts from router", e); + } + } + + private final static int DEFAULT_PRIORITY = 100; private final static int DEFAULT_DELTA = 2; diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 056c5f84f53..9d41fe92071 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -657,6 +657,14 @@ ALTER TABLE `cloud`.`s2s_vpn_gateway` ADD COLUMN `display` tinyint(1) NOT NULL D INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (225, UUID(), 9, 'FreeBSD 10 (32-bit)'); INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (226, UUID(), 9, 'FreeBSD 10 (64-bit)'); +CREATE TABLE `cloud`.`op_router_monitoring_services` ( + `vm_id` bigint unsigned UNIQUE NOT NULL COMMENT 'Primary Key', + `router_name` varchar(255) NOT NULL COMMENT 'Name of the Virtual Router', + `last_alert_timestamp` varchar(255) NOT NULL COMMENT 'Timestamp of the last alert received from Virtual Router', + PRIMARY KEY (`vm_id`), + CONSTRAINT `fk_virtual_router__id` FOREIGN KEY `fk_virtual_router__id` (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET=utf8 + ALTER TABLE `cloud`.`event` ADD COLUMN `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user'; DROP VIEW IF EXISTS `cloud`.`event_view`; diff --git a/systemvm/patches/debian/config/opt/cloud/bin/getRouterAlerts.sh b/systemvm/patches/debian/config/opt/cloud/bin/getRouterAlerts.sh new file mode 100644 index 00000000000..e5e8abeffda --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/getRouterAlerts.sh @@ -0,0 +1,70 @@ +#!/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. + +# getRouterAlerts.sh --- Send the alerts from routerServiceMonitor.log to Management Server + +source /root/func.sh + +lock="biglock" +locked=$(getLockFile $lock) +if [ "$locked" != "1" ] +then + exit 1 +fi + +#set -x + +filename=/var/log/routerServiceMonitor.log #Monitor service log file +if [ -n "$1" -a -n "$2" ] +then + reqdateval=$(date -d $1 +"%Y%m%d"); + reqtimeval=$(date -d $2 +"%H%M%S"); +else + reqdateval=0 + reqtimeval=0 +fi +if [ -f $filename ] +then + while read line + do + if [ -n "$line" ]; then + dateval=`echo $line |awk '{print $1}'` + timeval=`echo $line |awk '{print $2}'` + + todate=$(date -d "$dateval" +"%Y%m%d") > /dev/null + totime=$(date -d "$timeval" +"%H%M%S") > /dev/null + if [ "$todate" -gt "$reqdateval" ] > /dev/null + then + if [ -n "$alerts" ]; then alerts="$alerts\n$line"; else alerts="$line"; fi #>> $outputfile + elif [ "$todate" -eq "$reqdateval" ] > /dev/null + then + if [ "$totime" -gt "$reqtimeval" ] > /dev/null + then + if [ -n "$alerts" ]; then alerts="$alerts\n$line"; else alerts="$line"; fi #>> $outputfile + fi + fi + fi + done < $filename +fi +if [ -n "$alerts" ]; then + echo $alerts +else + echo "No Alerts" +fi + +unlock_exit 0 $lock $locked \ No newline at end of file diff --git a/systemvm/patches/debian/config/root/monitorServices.py b/systemvm/patches/debian/config/root/monitorServices.py index 0319ece6b87..c1dfba21a46 100755 --- a/systemvm/patches/debian/config/root/monitorServices.py +++ b/systemvm/patches/debian/config/root/monitorServices.py @@ -25,6 +25,7 @@ from subprocess import * from os import path import time import os +import logging class StatusCodes: SUCCESS = 0 @@ -92,6 +93,8 @@ def raisealert(severity, msg, process_name=None): else: log = '['+severity+']' + " " + msg +"\n" + logging.basicConfig(level=logging.INFO,filename='/var/log/routerServiceMonitor.log',format='%(asctime)s %(message)s') + logging.info(log) msg = 'logger -t monit '+ log pout = Popen(msg, shell=True, stdout=PIPE) diff --git a/test/integration/smoke/test_VirtualRouter_alerts.py b/test/integration/smoke/test_VirtualRouter_alerts.py new file mode 100644 index 00000000000..2333d846f14 --- /dev/null +++ b/test/integration/smoke/test_VirtualRouter_alerts.py @@ -0,0 +1,244 @@ +# 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. +""" P1 tests for alert receiving from VR on service failure in VR +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from nose.plugins.attrib import attr +import time + + +_multiprocess_shared_ = True + +class Services: + """Test VM Life Cycle Services + """ + + def __init__(self): + self.services = { + + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "small": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "SmallInstance", + "displaytext": "SmallInstance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + }, + "big": + { + # Big service offering ID to for change VM + "name": "BigInstance", + "displaytext": "BigInstance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 512, + } + }, + #Change this + "template": { + "displaytext": "xs", + "name": "xs", + "passwordenabled": False, + }, + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostype": 'CentOS 5.3 (64-bit)', + # CentOS 5.3 (64-bit) + } + + +class TestVRServiceFailureAlerting(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVRServiceFailureAlerting, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings ?? + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = template.id + + # Create account, service offerings, vm. + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=domain.id + ) + + cls.small_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["small"] + ) + + #create a virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.small_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.small_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + cls.api_client = super(TestVRServiceFailureAlerting, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created ISOs + cleanup_resources(self.apiclient, self.cleanup) + return + + @attr(hypervisor="xenserver") + @attr(tags=["advanced", "basic"]) + def test_01_VRServiceFailureAlerting(self): + + + if self.zone.networktype == "Basic": + list_router_response = list_routers( + self.apiclient, + listall="true" + ) + else: + list_router_response = list_routers( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + + self.debug("Router ID: %s, state: %s" % (router.id, router.state)) + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + alertSubject = "Monitoring Service on VR " + router.name + + if self.apiclient.hypervisor.lower() == 'vmware': + result = get_process_status( + self.apiclient.connection.mgtSvr, + 22, + self.apiclient.connection.user, + self.apiclient.connection.passwd, + router.linklocalip, + "service dnsmasq status", + hypervisor=self.apiclient.hypervisor + ) + else: + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + router.linklocalip, + "service apache2 stop" + ) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") + + res = str(result) + self.debug("apache process status: %s" % res) + + time.sleep(300) #wait for 5 minutes meanwhile monitor service on VR starts the apache service + + qresultset = self.dbclient.execute( + "select id from alert where subject = '%s' ORDER BY id DESC LIMIT 1;" \ + % str(alertSubject) + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + return From f293c94bc0198f162c80ff807ca81459150354a5 Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Fri, 14 Mar 2014 15:45:54 +0530 Subject: [PATCH 05/31] CLOUDSTACK-6090: Virtual Router Service Failure Alerting Fixed a missing license header --- .../network/dao/OpRouterMonitorServiceVO.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java index c70bba357c1..c2882fc4eaf 100644 --- a/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java +++ b/engine/schema/src/com/cloud/network/dao/OpRouterMonitorServiceVO.java @@ -1,3 +1,20 @@ +// 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.dao; import org.apache.cloudstack.api.InternalIdentity; From 2aff39f8c75d20ea086dbb873030f3e513fc6719 Mon Sep 17 00:00:00 2001 From: Devdeep Singh Date: Wed, 12 Mar 2014 02:22:22 +0530 Subject: [PATCH 06/31] CLOUDSTACK-6143: Storage motion support for hyper-v. With these changes a volume on a shared storage pool (SMB) and attached to a running vm can be live migrated to another shared storage pool. Also a vm and its volumes can be live migrated to another host and storage pool respectively. --- .../agent/api/MigrateWithStorageCommand.java | 16 ++ .../subsystem/api/storage/StorageAction.java | 3 +- ...ine-storage-datamotion-storage-context.xml | 3 +- .../motion/AncientDataMotionStrategy.java | 2 +- .../endpoint/DefaultEndPointSelector.java | 9 + .../storage/volume/VolumeServiceImpl.java | 10 +- .../HypervResource/CloudStackTypes.cs | 51 +++-- .../HypervResourceController.cs | 139 +++++++++++++- .../HypervResource/IWmiCallsV2.cs | 2 + .../HypervResource/WmiCallsV2.cs | 139 ++++++++++++++ ...on.v2.Msvm_StorageAllocationSettingData.cs | 2 +- .../motion/HypervStorageMotionStrategy.java | 179 ++++++++++++++++++ .../src/com/cloud/vm/UserVmManagerImpl.java | 3 +- setup/db/db/schema-430to440.sql | 1 + 14 files changed, 530 insertions(+), 29 deletions(-) create mode 100644 plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java index a94697a279d..43ae8543139 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java @@ -16,26 +16,38 @@ // under the License. package com.cloud.agent.api; +import java.util.List; import java.util.Map; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.utils.Pair; public class MigrateWithStorageCommand extends Command { VirtualMachineTO vm; Map volumeToFiler; + List> volumeToFilerAsList; String tgtHost; public MigrateWithStorageCommand(VirtualMachineTO vm, Map volumeToFiler) { this.vm = vm; this.volumeToFiler = volumeToFiler; + this.volumeToFilerAsList = null; this.tgtHost = null; } public MigrateWithStorageCommand(VirtualMachineTO vm, Map volumeToFiler, String tgtHost) { this.vm = vm; this.volumeToFiler = volumeToFiler; + this.volumeToFilerAsList = null; + this.tgtHost = tgtHost; + } + + public MigrateWithStorageCommand(VirtualMachineTO vm, List> volumeToFilerAsList, String tgtHost) { + this.vm = vm; + this.volumeToFiler = null; + this.volumeToFilerAsList = volumeToFilerAsList; this.tgtHost = tgtHost; } @@ -47,6 +59,10 @@ public class MigrateWithStorageCommand extends Command { return volumeToFiler; } + public List> getVolumeToFilerAsList() { + return volumeToFilerAsList; + } + public String getTargetHost() { return tgtHost; } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java index 4fbb20ed29e..965df84c2dd 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java @@ -21,5 +21,6 @@ package org.apache.cloudstack.engine.subsystem.api.storage; public enum StorageAction { TAKESNAPSHOT, BACKUPSNAPSHOT, - DELETESNAPSHOT + DELETESNAPSHOT, + MIGRATEVOLUME } diff --git a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml index 725f7d35be5..ade22dbc25b 100644 --- a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml +++ b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml @@ -30,5 +30,6 @@ class="org.apache.cloudstack.storage.motion.AncientDataMotionStrategy" /> - + diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index df81199461e..fbdacfa5aa2 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -372,7 +372,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { VolumeInfo volume = (VolumeInfo)srcData; StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary); MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool, volume.getAttachedVmName()); - EndPoint ep = selector.select(volume.getDataStore()); + EndPoint ep = selector.select(srcData, StorageAction.MIGRATEVOLUME); Answer answer = null; if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java index d64f755b4d0..304f9595cbf 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java +++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java @@ -305,6 +305,15 @@ public class DefaultEndPointSelector implements EndPointSelector { return getEndPointFromHostId(hostId); } } + } else if (action == StorageAction.MIGRATEVOLUME) { + VolumeInfo volume = (VolumeInfo)object; + if (volume.getHypervisorType() == Hypervisor.HypervisorType.Hyperv) { + VirtualMachine vm = volume.getAttachedVM(); + if ((vm != null) && (vm.getState() == VirtualMachine.State.Running)) { + Long hostId = vm.getHostId(); + return getEndPointFromHostId(hostId); + } + } } return select(object); } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 1c01401d4ed..68e5a5680e7 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -77,6 +77,7 @@ import com.cloud.host.Host; import com.cloud.host.dao.HostDao; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; @@ -814,9 +815,16 @@ public class VolumeServiceImpl implements VolumeService { protected VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePool pool) { Long lastPoolId = volume.getPoolId(); + String folder = pool.getPath(); + // For SMB, pool credentials are also stored in the uri query string. We trim the query string + // part here to make sure the credentials do not get stored in the db unencrypted. + if (pool.getPoolType() == StoragePoolType.SMB && folder != null && folder.contains("?")) { + folder = folder.substring(0, folder.indexOf("?")); + } + VolumeVO newVol = new VolumeVO(volume); newVol.setPoolId(pool.getId()); - newVol.setFolder(pool.getPath()); + newVol.setFolder(folder); newVol.setPodId(pool.getPodId()); newVol.setPoolId(pool.getId()); newVol.setLastPoolId(lastPoolId); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index 869f1088469..c222102805e 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -173,33 +173,35 @@ namespace HypervResource PrimaryDataStoreTO store = this.primaryDataStore; if (store.isLocal) { - fileName = Path.Combine(store.Path, this.uuid); + String volume = this.path; + if (String.IsNullOrEmpty(volume)) + { + volume = this.uuid; + } + fileName = Path.Combine(store.Path, volume); } else { - fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" + this.uuid; + String volume = this.path; + if (String.IsNullOrEmpty(volume)) + { + volume = this.uuid; + } + fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" + volume; fileName = Utils.NormalizePath(fileName); } } else if (this.nfsDataStore != null) { - if (this.path != null && File.Exists(this.path)) + fileName = this.nfsDataStore.UncPath; + if (this.path != null) { - fileName = this.path; + fileName = Utils.NormalizePath(fileName + @"\" + this.path); } - else - { - fileName = this.nfsDataStore.UncPath; - if (this.path != null) - { - fileName += @"\" + this.path; - } - fileName = Utils.NormalizePath(fileName); - if (Directory.Exists(fileName)) - { - fileName = Utils.NormalizePath(fileName + @"\" + this.uuid); - } + if (fileName != null && !File.Exists(fileName)) + { + fileName = Utils.NormalizePath(fileName + @"\" + this.uuid); } } else @@ -344,8 +346,17 @@ namespace HypervResource } else if (this.nfsDataStoreTO != null) { - NFSTO store = this.nfsDataStoreTO; - fileName = store.UncPath + @"\" + this.path + @"\" + this.uuid; + fileName = this.nfsDataStoreTO.UncPath; + if (this.path != null) + { + fileName = Utils.NormalizePath(fileName + @"\" + this.path); + } + + if (fileName != null && !File.Exists(fileName)) + { + fileName = Utils.NormalizePath(fileName + @"\" + this.uuid); + } + if (!this.format.Equals("RAW")) { fileName = fileName + '.' + this.format.ToLowerInvariant(); @@ -769,6 +780,8 @@ namespace HypervResource public const string ManageSnapshotCommand = "com.cloud.agent.api.ManageSnapshotCommand"; public const string MigrateAnswer = "com.cloud.agent.api.MigrateAnswer"; public const string MigrateCommand = "com.cloud.agent.api.MigrateCommand"; + public const string MigrateWithStorageAnswer = "com.cloud.agent.api.MigrateWithStorageAnswer"; + public const string MigrateWithStorageCommand = "com.cloud.agent.api.MigrateWithStorageCommand"; public const string ModifySshKeysCommand = "com.cloud.agent.api.ModifySshKeysCommand"; public const string ModifyStoragePoolAnswer = "com.cloud.agent.api.ModifyStoragePoolAnswer"; public const string ModifyStoragePoolCommand = "com.cloud.agent.api.ModifyStoragePoolCommand"; @@ -853,6 +866,8 @@ namespace HypervResource public const string CreateCommand = "com.cloud.agent.api.storage.CreateCommand"; public const string CreatePrivateTemplateAnswer = "com.cloud.agent.api.storage.CreatePrivateTemplateAnswer"; public const string DestroyCommand = "com.cloud.agent.api.storage.DestroyCommand"; + public const string MigrateVolumeAnswer = "com.cloud.agent.api.storage.MigrateVolumeAnswer"; + public const string MigrateVolumeCommand = "com.cloud.agent.api.storage.MigrateVolumeCommand"; public const string PrimaryStorageDownloadAnswer = "com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer"; public const string PrimaryStorageDownloadCommand = "com.cloud.agent.api.storage.PrimaryStorageDownloadCommand"; public const string ResizeVolumeAnswer = "com.cloud.agent.api.storage.ResizeVolumeAnswer"; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index e233d0340d8..2d447532d7f 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -1187,7 +1187,7 @@ namespace HypervResource volumePath = Utils.NormalizePath(volumePath); Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); } - volume.path = volumePath; + volume.path = volume.uuid; wmiCallsV2.CreateDynamicVirtualHardDisk(volumeSize, volumePath); if (File.Exists(volumePath)) { @@ -1578,7 +1578,7 @@ namespace HypervResource { // TODO: thin provision instead of copying the full file. File.Copy(srcFile, destFile); - destVolumeObjectTO.path = destFile; + destVolumeObjectTO.path = destVolumeObjectTO.uuid; JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); newData = ansObj; result = true; @@ -1612,8 +1612,25 @@ namespace HypervResource // doesn't do anything if the directory is already present. Directory.CreateDirectory(Path.GetDirectoryName(destFile)); File.Copy(srcFile, destFile); + + if (srcVolumeObjectTO.nfsDataStore != null && srcVolumeObjectTO.primaryDataStore == null) + { + logger.Info("Copied volume from secondary data store to primary. Path: " + destVolumeObjectTO.path); + } + else if (srcVolumeObjectTO.primaryDataStore != null && srcVolumeObjectTO.nfsDataStore == null) + { + destVolumeObjectTO.path = destVolumeObjectTO.path + "/" + destVolumeObjectTO.uuid; + if (destVolumeObjectTO.format != null) + { + destVolumeObjectTO.path += "." + destVolumeObjectTO.format.ToLower(); + } + } + else + { + logger.Error("Destination volume path wasn't set. Unsupported source volume data store."); + } + // Create volumeto object deserialize and send it - destVolumeObjectTO.path = destFile; JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); newData = ansObj; result = true; @@ -1656,7 +1673,11 @@ namespace HypervResource TemplateObjectTO destTemplateObject = new TemplateObjectTO(); destTemplateObject.size = srcVolumeObjectTO.size.ToString(); destTemplateObject.format = srcVolumeObjectTO.format; - destTemplateObject.path = destFile; + destTemplateObject.path = destTemplateObjectTO.path + "/" + destTemplateObjectTO.uuid; + if (destTemplateObject.format != null) + { + destTemplateObject.path += "." + destTemplateObject.format.ToLower(); + } destTemplateObject.nfsDataStoreTO = destTemplateObjectTO.nfsDataStoreTO; destTemplateObject.checksum = destTemplateObjectTO.checksum; newData = destTemplateObject; @@ -1990,6 +2011,113 @@ namespace HypervResource } } + // POST api/HypervResource/MigrateVolumeCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateVolumeCommand)] + public JContainer MigrateVolumeCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateVolumeCommand + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + string vm = (string)cmd.attachedVmName; + string volume = (string)cmd.volumePath; + wmiCallsV2.MigrateVolume(vm, volume, GetStoragePoolPath(cmd.pool)); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateVolumeCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + volumePath = (string)cmd.volumePath, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateVolumeAnswer); + } + } + + // POST api/HypervResource/MigrateWithStorageCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateWithStorageCommand)] + public JContainer MigrateWithStorageCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateWithStorageCommand + cmd.ToString()); + + string details = null; + bool result = false; + List volumeTos = new List(); + + try + { + string vm = (string)cmd.vm.name; + string destination = (string)cmd.tgtHost; + var volumeToPoolList = cmd.volumeToFilerAsList; + var volumeToPool = new Dictionary(); + foreach (var item in volumeToPoolList) + { + volumeTos.Add(item.t); + string poolPath = GetStoragePoolPath(item.u); + volumeToPool.Add((string)item.t.path, poolPath); + } + + wmiCallsV2.MigrateVmWithVolume(vm, destination, volumeToPool); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateWithStorageCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + volumeTos = JArray.FromObject(volumeTos), + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateWithStorageAnswer); + } + } + + private string GetStoragePoolPath(dynamic pool) + { + string poolTypeStr = pool.type; + StoragePoolType poolType; + if (!Enum.TryParse(poolTypeStr, out poolType)) + { + throw new ArgumentException("Invalid pool type " + poolTypeStr); + } + else if (poolType == StoragePoolType.SMB) + { + NFSTO share = new NFSTO(); + String uriStr = "cifs://" + (string)pool.host + (string)pool.path; + share.uri = new Uri(uriStr); + return Utils.NormalizePath(share.UncPath); + } + else if (poolType == StoragePoolType.Filesystem) + { + return pool.path; + } + + throw new ArgumentException("Couldn't parse path for pool type " + poolTypeStr); + } + // POST api/HypervResource/StartupCommand [HttpPost] [ActionName(CloudStackTypes.StartupCommand)] @@ -2016,7 +2144,8 @@ namespace HypervResource strtRouteCmd.storageNetmask = subnet; strtRouteCmd.storageMacAddress = storageNic.GetPhysicalAddress().ToString(); strtRouteCmd.gatewayIpAddress = storageNic.GetPhysicalAddress().ToString(); - + strtRouteCmd.hypervisorVersion = System.Environment.OSVersion.Version.Major.ToString() + "." + + System.Environment.OSVersion.Version.Minor.ToString(); strtRouteCmd.caps = "hvm"; dynamic details = strtRouteCmd.hostDetails; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index da6ad838757..601889646bd 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -40,6 +40,8 @@ namespace HypervResource void DestroyVm(dynamic jsonObj); void DestroyVm(string displayName); void MigrateVm(string vmName, string destination); + void MigrateVolume(string vmName, string volume, string destination); + void MigrateVmWithVolume(string vmName, string destination, Dictionary volumeToPool); void DetachDisk(string displayName, string diskFileName); ComputerSystem GetComputerSystem(string displayName); ComputerSystem.ComputerSystemCollection GetComputerSystemCollection(); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index d2b9ce19760..73156c5e3e7 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -1058,6 +1058,125 @@ namespace HypervResource } } + /// + /// Migrates the volume of a vm to a given destination storage + /// + /// + /// + /// + public void MigrateVolume(string vmName, string volume, string destination) + { + ComputerSystem vm = GetComputerSystem(vmName); + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance(); + VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService(); + StorageAllocationSettingData[] sasd = GetStorageSettings(vm); + + string[] rasds = null; + if (sasd != null) + { + rasds = new string[sasd.Length]; + uint index = 0; + foreach (StorageAllocationSettingData item in sasd) + { + string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]); + if (!String.IsNullOrEmpty(vhdFileName) && vhdFileName.Equals(volume)) + { + string newVhdPath = Path.Combine(destination, Path.GetFileName(item.HostResource[0])); + item.LateBoundObject["HostResource"] = new string[] { newVhdPath }; + item.LateBoundObject["PoolId"] = ""; + } + + rasds[index++] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + } + } + + migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.Storage; + migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP; + string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + + ManagementPath jobPath; + var ret_val = service.MigrateVirtualSystemToHost(vm.Path, null, migrationSettings, rasds, null, out jobPath); + if (ret_val == ReturnCode.Started) + { + MigrationJobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed migrating volume {0} of VM {1} (GUID {2}) due to {3}", + volume, + vm.ElementName, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + /// + /// Migrates the volume of a vm to a given destination storage + /// + /// + /// + /// volume to me migrated to which pool + public void MigrateVmWithVolume(string vmName, string destination, Dictionary volumeToPool) + { + ComputerSystem vm = GetComputerSystem(vmName); + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance(); + VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService(); + StorageAllocationSettingData[] sasd = GetStorageSettings(vm); + + string[] rasds = null; + if (sasd != null) + { + rasds = new string[sasd.Length]; + uint index = 0; + foreach (StorageAllocationSettingData item in sasd) + { + string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]); + if (!String.IsNullOrEmpty(vhdFileName) && volumeToPool.ContainsKey(vhdFileName)) + { + string newVhdPath = Path.Combine(volumeToPool[vhdFileName], Path.GetFileName(item.HostResource[0])); + item.LateBoundObject["HostResource"] = new string[] { newVhdPath }; + item.LateBoundObject["PoolId"] = ""; + } + + rasds[index++] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + } + } + + IPAddress addr = IPAddress.Parse(destination); + IPHostEntry entry = Dns.GetHostEntry(addr); + string[] destinationHost = new string[] { destination }; + + migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.VirtualSystemAndStorage; + migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP; + migrationSettingData.LateBoundObject["DestinationIPAddressList"] = destinationHost; + string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + + ManagementPath jobPath; + var ret_val = service.MigrateVirtualSystemToHost(vm.Path, entry.HostName, migrationSettings, rasds, null, out jobPath); + if (ret_val == ReturnCode.Started) + { + MigrationJobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed migrating VM {0} and its volumes to destination {1} (GUID {2}) due to {3}", + vm.ElementName, + destination, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + /// /// Create new storage media resources, e.g. hard disk images and ISO disk images /// see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx @@ -2081,6 +2200,26 @@ namespace HypervResource return result.ToArray(); } + public StorageAllocationSettingData[] GetStorageSettings(ComputerSystem vm) + { + // ComputerSystem -> VirtualSystemSettingData -> EthernetPortAllocationSettingData + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + + var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, StorageAllocationSettingData.CreatedClassName); + + // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain + // the virtualisation objects. + var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery); + var wmiObjCollection = new StorageAllocationSettingData.StorageAllocationSettingDataCollection(wmiObjectSearch.Get()); + + var result = new List(wmiObjCollection.Count); + foreach (StorageAllocationSettingData item in wmiObjCollection) + { + result.Add(item); + } + return result.ToArray(); + } + public EthernetSwitchPortVlanSettingData GetVlanSettings(EthernetPortAllocationSettingData ethernetConnection) { diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs index 8553fedd525..602347f90dc 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs @@ -36,7 +36,7 @@ namespace CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2 { private static string CreatedWmiNamespace = "ROOT\\virtualization\\v2"; // Private property to hold the name of WMI class which created this class. - private static string CreatedClassName = "Msvm_StorageAllocationSettingData"; + public static string CreatedClassName = "Msvm_StorageAllocationSettingData"; // Private member variable to hold the ManagementScope which is used by the various methods. private static System.Management.ManagementScope statMgmtScope = null; diff --git a/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java b/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java new file mode 100644 index 00000000000..011bd1292c7 --- /dev/null +++ b/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java @@ -0,0 +1,179 @@ +/* + * 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 org.apache.cloudstack.storage.motion; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.MigrateWithStorageAnswer; +import com.cloud.agent.api.MigrateWithStorageCommand; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.host.Host; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.StoragePool; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.VMInstanceDao; + +@Component +public class HypervStorageMotionStrategy implements DataMotionStrategy { + private static final Logger s_logger = Logger.getLogger(HypervStorageMotionStrategy.class); + @Inject AgentManager agentMgr; + @Inject VolumeDao volDao; + @Inject VolumeDataFactory volFactory; + @Inject PrimaryDataStoreDao storagePoolDao; + @Inject VMInstanceDao instanceDao; + + @Override + public StrategyPriority canHandle(DataObject srcData, DataObject destData) { + return StrategyPriority.CANT_HANDLE; + } + + @Override + public StrategyPriority canHandle(Map volumeMap, Host srcHost, Host destHost) { + if (srcHost.getHypervisorType() == HypervisorType.Hyperv && + destHost.getHypervisorType() == HypervisorType.Hyperv) { + return StrategyPriority.HYPERVISOR; + } + + return StrategyPriority.CANT_HANDLE; + } + + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + + @Override + public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + CopyCommandResult result = new CopyCommandResult(null, null); + result.setResult("Unsupported operation requested for copying data."); + callback.complete(result); + + return null; + } + + @Override + public Void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, + AsyncCompletionCallback callback) { + Answer answer = null; + String errMsg = null; + try { + VMInstanceVO instance = instanceDao.findById(vmTo.getId()); + if (instance != null) { + answer = migrateVmWithVolumes(instance, vmTo, srcHost, destHost, volumeMap); + } else { + throw new CloudRuntimeException("Unsupported operation requested for moving data."); + } + } catch (Exception e) { + s_logger.error("copy failed", e); + errMsg = e.toString(); + } + + CopyCommandResult result = new CopyCommandResult(null, answer); + result.setResult(errMsg); + callback.complete(result); + return null; + } + + private Answer migrateVmWithVolumes(VMInstanceVO vm, VirtualMachineTO to, Host srcHost, + Host destHost, Map volumeToPool) throws AgentUnavailableException { + + // Initiate migration of a virtual machine with it's volumes. + try { + List> volumeToFilerto = new ArrayList>(); + for (Map.Entry entry : volumeToPool.entrySet()) { + VolumeInfo volume = entry.getKey(); + VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId())); + StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue()); + volumeToFilerto.add(new Pair(volumeTo, filerTo)); + } + + MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto, destHost.getPrivateIpAddress()); + MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer) agentMgr.send(srcHost.getId(), command); + if (answer == null) { + s_logger.error("Migration with storage of vm " + vm + " failed."); + throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost); + } else if (!answer.getResult()) { + s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + answer.getDetails()); + throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + + ". " + answer.getDetails()); + } else { + // Update the volume details after migration. + updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos()); + } + + return answer; + } catch (OperationTimedoutException e) { + s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e); + throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId()); + } + } + + private void updateVolumePathsAfterMigration(Map volumeToPool, List volumeTos) { + for (Map.Entry entry : volumeToPool.entrySet()) { + boolean updated = false; + VolumeInfo volume = entry.getKey(); + StoragePool pool = (StoragePool)entry.getValue(); + for (VolumeObjectTO volumeTo : volumeTos) { + if (volume.getId() == volumeTo.getId()) { + VolumeVO volumeVO = volDao.findById(volume.getId()); + Long oldPoolId = volumeVO.getPoolId(); + volumeVO.setPath(volumeTo.getPath()); + volumeVO.setFolder(pool.getPath()); + volumeVO.setPodId(pool.getPodId()); + volumeVO.setPoolId(pool.getId()); + volumeVO.setLastPoolId(oldPoolId); + volDao.update(volume.getId(), volumeVO); + updated = true; + break; + } + } + + if (!updated) { + s_logger.error("Volume path wasn't updated for volume " + volume + " after it was migrated."); + } + } + } +} diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 0e4fb5ec56d..393613a140e 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3996,7 +3996,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) - && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { + && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv) + && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" + " XenServer/VMware/KVM only"); } diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 9d41fe92071..51bce2df8f2 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -26,6 +26,7 @@ ALTER TABLE `cloud`.`disk_offering` ADD `cache_mode` VARCHAR( 16 ) DEFAULT 'none UPDATE `cloud`.`hypervisor_capabilities` set max_guests_limit='150' WHERE hypervisor_version='6.1.0'; UPDATE `cloud`.`hypervisor_capabilities` set max_guests_limit='500' WHERE hypervisor_version='6.2.0'; +UPDATE `cloud`.`hypervisor_capabilities` set storage_motion_supported='1' WHERE hypervisor_version='6.2' AND hypervisor_type="Hyperv"; DROP VIEW IF EXISTS `cloud`.`disk_offering_view`; CREATE VIEW `cloud`.`disk_offering_view` AS From 7c4443e233b936820d375b28f348e6613ae5ad93 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 4 Mar 2014 16:42:17 +0530 Subject: [PATCH 07/31] -introduces 'DistributedRouter' as capability to 'Connectivity' service. -create VPC offering to permit 'DistributedRouter' as capability to connectivity service --- api/src/com/cloud/network/Network.java | 3 +- .../com/cloud/network/vpc/VpcOffering.java | 5 ++ .../network/vpc/VpcProvisioningService.java | 6 +- .../apache/cloudstack/api/ApiConstants.java | 1 + .../admin/vpc/CreateVPCOfferingCmd.java | 10 ++- .../api/response/VpcOfferingResponse.java | 8 +++ .../com/cloud/network/vpc/VpcOfferingVO.java | 12 +++- .../management/ContrailManagerImpl.java | 2 +- .../src/com/cloud/api/ApiResponseHelper.java | 1 + .../com/cloud/network/vpc/VpcManagerImpl.java | 65 ++++++++++++++++--- setup/db/db/schema-430to440.sql | 2 +- 11 files changed, 99 insertions(+), 16 deletions(-) diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 6dc67521663..3283a55bfe7 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -57,7 +57,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Service PortForwarding = new Service("PortForwarding"); public static final Service SecurityGroup = new Service("SecurityGroup"); public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols); - public static final Service Connectivity = new Service("Connectivity"); + public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter); private final String name; private final Capability[] caps; @@ -186,6 +186,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Capability SslTermination = new Capability("SslTermination"); public static final Capability LbSchemes = new Capability("LbSchemes"); public static final Capability DhcpAccrossMultipleSubnets = new Capability("DhcpAccrossMultipleSubnets"); + public static final Capability DistributedRouter = new Capability("DistributedRouter"); private final String name; diff --git a/api/src/com/cloud/network/vpc/VpcOffering.java b/api/src/com/cloud/network/vpc/VpcOffering.java index 6e75a2f2500..a0a1b1528cf 100644 --- a/api/src/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/com/cloud/network/vpc/VpcOffering.java @@ -55,4 +55,9 @@ public interface VpcOffering extends InternalIdentity, Identity { */ Long getServiceOfferingId(); + /** + * + * @return true if the offering provides a distributed router capable of one-hop forwarding + */ + boolean supportsDistributedRouter(); } diff --git a/api/src/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/com/cloud/network/vpc/VpcProvisioningService.java index 174b71f34a2..e545275217f 100644 --- a/api/src/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/com/cloud/network/vpc/VpcProvisioningService.java @@ -23,8 +23,10 @@ public interface VpcProvisioningService { public VpcOffering getVpcOffering(long vpcOfferingId); - public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, - Long serviceOfferingId); + public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, + Map> serviceProviders, + Map serviceCapabilitystList, + Long serviceOfferingId); List listVpcOfferings(Long id, String name, String displayText, List supportedServicesStr, Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal); diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 239b8cdeec8..32107edf840 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -588,6 +588,7 @@ public class ApiConstants { public static final String VGPU = "vgpu"; public static final String VGPUTYPE = "vgputype"; public static final String REMAININGCAPACITY = "remainingcapacity"; + public static final String DISTRIBUTED_VPC_ROUTER = "distributedvpcrouter"; public enum HostDetails { diff --git a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index 6b2c4ba5cab..5b3090bd756 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -66,6 +66,9 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { + "If not specified, the provider for the service will be mapped to the default provider on the physical network") private Map serviceProviderList; + @Parameter(name = ApiConstants.SERVICE_CAPABILITY_LIST, type = CommandType.MAP, description = "desired service capabilities as part of vpc offering") + private Map serviceCapabilitystList; + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, @@ -112,13 +115,18 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return serviceProviderMap; } + public Map> getServiceCapabilitystList() { + return serviceCapabilitystList; + } + public Long getServiceOfferingId() { return serviceOfferingId; } @Override public void create() throws ResourceAllocationException { - VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(getVpcOfferingName(), getDisplayText(), getSupportedServices(), getServiceProviders(), getServiceOfferingId()); + VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(getVpcOfferingName(), getDisplayText(), + getSupportedServices(), getServiceProviders(), getServiceCapabilitystList(), getServiceOfferingId()); if (vpcOff != null) { setEntityId(vpcOff.getId()); setEntityUuid(vpcOff.getUuid()); diff --git a/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java index 17e4dfdd0b6..89697f04ad0 100644 --- a/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -59,6 +59,10 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = "the list of supported services", responseObject = ServiceResponse.class) private List services; + @SerializedName(ApiConstants.DISTRIBUTED_VPC_ROUTER) + @Param(description = " indicates if the vpc offering supports distributed router for one-hop forwarding") + private Boolean supportsDistributedRouter; + public void setId(String id) { this.id = id; } @@ -86,4 +90,8 @@ public class VpcOfferingResponse extends BaseResponse { public void setState(String state) { this.state = state; } + + public void setSupportsDistributedRouter(Boolean supportsDistributedRouter) { + this.supportsDistributedRouter = supportsDistributedRouter; + } } diff --git a/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java index 3a676e6af5e..53f6f6055e1 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java @@ -67,6 +67,9 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "service_offering_id") Long serviceOfferingId; + @Column(name = "supports_distributed_router") + boolean supportsDistributedRouter=false; + public VpcOfferingVO() { this.uuid = UUID.randomUUID().toString(); } @@ -80,9 +83,11 @@ public class VpcOfferingVO implements VpcOffering { this.state = State.Disabled; } - public VpcOfferingVO(String name, String displayText, boolean isDefault, Long serviceOfferingId) { + public VpcOfferingVO(String name, String displayText, boolean isDefault, Long serviceOfferingId, + boolean supportsDistributedRouter) { this(name, displayText, serviceOfferingId); this.isDefault = isDefault; + this.supportsDistributedRouter = supportsDistributedRouter; } @Override @@ -145,4 +150,9 @@ public class VpcOfferingVO implements VpcOffering { public Long getServiceOfferingId() { return serviceOfferingId; } + + @Override + public boolean supportsDistributedRouter() { + return supportsDistributedRouter; + } } diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java index 01be7dbd66a..bf083fdfb05 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java @@ -281,7 +281,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager for (String svc: services) { serviceProviderMap.put(svc, providerSet); } - vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null); + vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null); ((VpcOfferingVO)vpcOffer).setState(VpcOffering.State.Enabled); long id = vpcOffer.getId(); _vpcOffDao.update(id, (VpcOfferingVO)vpcOffer); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index f5009097785..e87c3e0fe1f 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2765,6 +2765,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDisplayText(offering.getDisplayText()); response.setIsDefault(offering.isDefault()); response.setState(offering.getState().name()); + response.setSupportsDistributedRouter(offering.supportsDistributedRouter()); Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(offering.getId()); List serviceResponses = new ArrayList(); diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 99dbfdee127..8c7b5fb74c6 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -18,8 +18,10 @@ package com.cloud.network.vpc; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -231,7 +233,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null); + createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null, false); } //configure default vpc offering with Netscaler as LB Provider @@ -250,7 +252,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null); + createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false); } } }); @@ -299,8 +301,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true) - public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, - Long serviceOfferingId) { + public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, + Map> serviceProviders, + Map serviceCapabilitystList, + Long serviceOfferingId) { Map> svcProviderMap = new HashMap>(); Set defaultProviders = new HashSet(); defaultProviders.add(Provider.VPCVirtualRouter); @@ -372,20 +376,25 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } - VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, serviceOfferingId); + boolean supportsDistributedRouter = isVpcOfferingSupportsDistributedRouter(serviceCapabilitystList); + + VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, + serviceOfferingId,supportsDistributedRouter); CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); return offering; } @DB - protected VpcOffering createVpcOffering(final String name, final String displayText, final Map> svcProviderMap, - final boolean isDefault, final State state, final Long serviceOfferingId) { + protected VpcOffering createVpcOffering(final String name, final String displayText, + final Map> svcProviderMap, + final boolean isDefault, final State state, final Long serviceOfferingId, + final boolean supportsDistributedRouter) { return Transaction.execute(new TransactionCallback() { @Override public VpcOffering doInTransaction(TransactionStatus status) { - // create vpc offering object - VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId); + // create vpc offering object + VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId, supportsDistributedRouter); if (state != null) { offering.setState(state); @@ -413,6 +422,44 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis }); } + private boolean isVpcOfferingSupportsDistributedRouter(Map serviceCapabilitystList) { + boolean supportsDistributedRouter = false; + if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { + Collection serviceCapabilityCollection = serviceCapabilitystList.values(); + Iterator iter = serviceCapabilityCollection.iterator(); + Map capabilityMap = null; + + while (iter.hasNext()) { + HashMap svcCapabilityMap = (HashMap)iter.next(); + Network.Capability capability = null; + String svc = svcCapabilityMap.get("service"); + String capabilityName = svcCapabilityMap.get("capabilitytype"); + String capabilityValue = svcCapabilityMap.get("capabilityvalue"); + if (capabilityName != null) { + capability = Network.Capability.getCapability(capabilityName); + } + + if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { + throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); + } + + if (!svc.equalsIgnoreCase(Service.Connectivity.getName())) { + throw new InvalidParameterValueException("Invalid Service:" + svc + " specified. Only for 'Connectivity' service capabilities can be specified"); + } + + if (!capabilityName.equalsIgnoreCase("DistributedRouter")) { + throw new InvalidParameterValueException("Invalid Capability:" + capabilityName + " specified. Only 'DistributedRouter' capability can be specified."); + } + + if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { + throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified."); + } + supportsDistributedRouter = capabilityValue.equalsIgnoreCase("true"); + } + } + return supportsDistributedRouter; + } + @Override public Vpc getActiveVpc(long vpcId) { return _vpcDao.getActiveVpcById(vpcId); diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 51bce2df8f2..ac398e51265 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -739,5 +739,5 @@ UPDATE `cloud`.`guest_os_hypervisor` SET `created` = now(); ALTER TABLE `cloud`.`guest_os` ADD COLUMN `created` datetime COMMENT 'Time when Guest OS was created in system'; ALTER TABLE `cloud`.`guest_os` ADD COLUMN `removed` datetime COMMENT 'Time when Guest OS was removed if deleted, else NULL'; UPDATE `cloud`.`guest_os` SET `created` = now(); - ALTER TABLE `cloud`.`vm_reservation` ADD COLUMN `deployment_planner` varchar(40) DEFAULT NULL COMMENT 'Preferred deployment planner for the vm'; +ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_distributed_router boolean default false; From e3ec12e5d051d7faa6ea12c1bc6f1d1d1d2daf3e Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 4 Mar 2014 17:17:18 +0530 Subject: [PATCH 08/31] -add check to ensure 'Connectivity' service provider specified in createVpcOffering actually supports 'DistributedRouter' capability - enable OVS to support 'DistributedRouter' capability --- .../com/cloud/network/element/OvsElement.java | 5 +- .../com/cloud/network/vpc/VpcManagerImpl.java | 52 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java index 03eeedd7e05..05e81a12a93 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java @@ -246,7 +246,10 @@ StaticNatServiceProvider, IpDeployer { Map> capabilities = new HashMap>(); // L2 Support : SDN provisioning - capabilities.put(Service.Connectivity, null); + Map connectivityCapabilities = new HashMap(); + connectivityCapabilities.put(Capability.DistributedRouter, null); + capabilities.put(Service.Connectivity, connectivityCapabilities); + // L3 Support : Port Forwarding capabilities.put(Service.PortForwarding, null); diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 8c7b5fb74c6..16a839903db 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -65,6 +65,7 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.element.NetworkElement; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; @@ -376,6 +377,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } + validateConnectivtyServiceCapablitlies(svcProviderMap.get(Service.Connectivity), serviceCapabilitystList); boolean supportsDistributedRouter = isVpcOfferingSupportsDistributedRouter(serviceCapabilitystList); VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, @@ -460,6 +462,56 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return supportsDistributedRouter; } + private void validateConnectivtyServiceCapablitlies(Set providers, Map serviceCapabilitystList) { + + if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { + Collection serviceCapabilityCollection = serviceCapabilitystList.values(); + Iterator iter = serviceCapabilityCollection.iterator(); + Map capabilityMap = null; + + while (iter.hasNext()) { + HashMap svcCapabilityMap = (HashMap)iter.next(); + Network.Capability capability = null; + String svc = svcCapabilityMap.get("service"); + String capabilityName = svcCapabilityMap.get("capabilitytype"); + String capabilityValue = svcCapabilityMap.get("capabilityvalue"); + if (capabilityName != null) { + capability = Network.Capability.getCapability(capabilityName); + } + + if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { + throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); + } + + if (!svc.equalsIgnoreCase(Service.Connectivity.getName())) { + throw new InvalidParameterValueException("Invalid Service:" + svc + " specified. Only for 'Connectivity' service capabilities can be specified"); + } + + if (!capabilityName.equalsIgnoreCase("DistributedRouter")) { + throw new InvalidParameterValueException("Invalid Capability:" + capabilityName + " specified. Only 'DistributedRouter' capability can be specified."); + } + + if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { + throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified."); + } + } + + if (providers != null && !providers.isEmpty()) { + for (Provider provider: providers) { + NetworkElement element = _ntwkModel.getElementImplementingProvider(provider.getName()); + Map> capabilities = element.getCapabilities(); + if (capabilities != null && !capabilities.isEmpty()) { + Map connectivityCapabilities = capabilities.get(Service.Connectivity); + if (connectivityCapabilities == null || (connectivityCapabilities != null && !connectivityCapabilities.keySet().contains(Network.Capability.DistributedRouter))) { + throw new InvalidParameterValueException("Provider: " + provider.getName() + " does not support " + + Network.Capability.DistributedRouter.getName() + " capability."); + } + } + } + } + } + } + @Override public Vpc getActiveVpc(long vpcId) { return _vpcDao.getActiveVpcById(vpcId); From 3139b3551809e176eac8111fe0884579cc80d595 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 4 Mar 2014 18:17:02 +0530 Subject: [PATCH 09/31] mark VPC to be using distributed router if VPC offerign supports distributedrouter capability. --- api/src/com/cloud/network/vpc/Vpc.java | 6 ++++++ .../apache/cloudstack/api/response/VpcResponse.java | 9 +++++++++ engine/schema/src/com/cloud/network/vpc/VpcVO.java | 13 +++++++++++-- server/src/com/cloud/api/ApiResponseHelper.java | 1 + .../src/com/cloud/network/vpc/VpcManagerImpl.java | 9 +++++---- server/test/com/cloud/vpc/VpcApiUnitTest.java | 2 +- server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java | 4 ++-- setup/db/db/schema-430to440.sql | 1 + 8 files changed, 36 insertions(+), 9 deletions(-) diff --git a/api/src/com/cloud/network/vpc/Vpc.java b/api/src/com/cloud/network/vpc/Vpc.java index eb7e39134c3..4bc8c98872d 100644 --- a/api/src/com/cloud/network/vpc/Vpc.java +++ b/api/src/com/cloud/network/vpc/Vpc.java @@ -73,4 +73,10 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity { boolean isRestartRequired(); boolean isDisplay(); + + /** + * + * @return true if VPC is configured to use distributed router to provides one-hop forwarding and hypervisor based ACL + */ + boolean usesDistributedRouter(); } diff --git a/api/src/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/org/apache/cloudstack/api/response/VpcResponse.java index eeafb40c142..e3b44f2113f 100644 --- a/api/src/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VpcResponse.java @@ -111,6 +111,11 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons @Param(description = "is vpc for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; + + @SerializedName(ApiConstants.DISTRIBUTED_VPC_ROUTER) + @Param(description = "is VPC uses distributed router for one hop forwarding and host based network ACL's") + private boolean usesDistributedRouter; + public void setId(String id) { this.id = id; } @@ -199,4 +204,8 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } + + public void setUsesDistributedRouter(Boolean usesDistributedRouter) { + this.usesDistributedRouter = usesDistributedRouter; + } } diff --git a/engine/schema/src/com/cloud/network/vpc/VpcVO.java b/engine/schema/src/com/cloud/network/vpc/VpcVO.java index 39bea77e99e..d7aaa95b87c 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcVO.java @@ -81,11 +81,15 @@ public class VpcVO implements Vpc { @Column(name = "display", updatable = true, nullable = false) protected boolean display = true; + @Column(name="uses_distributed_router") + boolean usesDistributedRouter = false; + public VpcVO() { uuid = UUID.randomUUID().toString(); } - public VpcVO(long zoneId, String name, String displayText, long accountId, long domainId, long vpcOffId, String cidr, String networkDomain) { + public VpcVO(long zoneId, String name, String displayText, long accountId, long domainId, long vpcOffId, String cidr, + String networkDomain, boolean useDistributedRouter) { this.zoneId = zoneId; this.name = name; this.displayText = displayText; @@ -95,7 +99,8 @@ public class VpcVO implements Vpc { uuid = UUID.randomUUID().toString(); state = State.Enabled; this.networkDomain = networkDomain; - vpcOfferingId = vpcOffId; + this.vpcOfferingId = vpcOffId; + this.usesDistributedRouter = useDistributedRouter; } @Override @@ -201,5 +206,9 @@ public class VpcVO implements Vpc { @Override public IAMEntityType getEntityType() { return IAMEntityType.Vpc; + + @Override + public boolean usesDistributedRouter() { + return usesDistributedRouter; } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index e87c3e0fe1f..a37bf7b2d4d 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2808,6 +2808,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setRestartRequired(vpc.isRestartRequired()); response.setNetworkDomain(vpc.getNetworkDomain()); response.setForDisplay(vpc.isDisplay()); + response.setUsesDistributedRouter(vpc.usesDistributedRouter()); Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(vpc.getVpcOfferingId()); List serviceResponses = new ArrayList(); diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 16a839903db..6da1a55e65f 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -728,13 +728,13 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis networkDomain = "cs" + Long.toHexString(owner.getId()) + NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId); } } - - return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain, displayVpc); + boolean useDistributedRouter = vpcOff.supportsDistributedRouter(); + return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain, displayVpc, useDistributedRouter); } @DB protected Vpc createVpc(final long zoneId, final long vpcOffId, final Account vpcOwner, final String vpcName, final String displayText, final String cidr, - final String networkDomain, final Boolean displayVpc) { + final String networkDomain, final Boolean displayVpc, final boolean useDistributedRouter) { //Validate CIDR if (!NetUtils.isValidCIDR(cidr)) { @@ -756,7 +756,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return Transaction.execute(new TransactionCallback() { @Override public VpcVO doInTransaction(TransactionStatus status) { - VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, vpcOwner.getId(), vpcOwner.getDomainId(), vpcOffId, cidr, networkDomain); + VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, vpcOwner.getId(), vpcOwner.getDomainId(), vpcOffId, + cidr, networkDomain, useDistributedRouter); if (displayVpc != null) { vpc.setDisplay(displayVpc); } diff --git a/server/test/com/cloud/vpc/VpcApiUnitTest.java b/server/test/com/cloud/vpc/VpcApiUnitTest.java index bc982a9c44e..5e283743b7a 100644 --- a/server/test/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/test/com/cloud/vpc/VpcApiUnitTest.java @@ -85,7 +85,7 @@ public class VpcApiUnitTest extends TestCase { public void validateNtwkOffForVpc() { //validate network offering //1) correct network offering - VpcVO vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain"); + VpcVO vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); boolean result = false; try { _vpcService.validateNtwkOffForNtwkInVpc(2L, 1, "0.0.0.0", "111-", vo, "10.1.1.1", new AccountVO(), null); diff --git a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java index 7a0c7a0e267..e1a6ac2ae46 100644 --- a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java +++ b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java @@ -98,9 +98,9 @@ public class MockVpcDaoImpl extends GenericDaoBase implements VpcDa public VpcVO findById(Long id) { VpcVO vo = null; if (id.longValue() == 1) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain"); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); } else if (id.longValue() == 2) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain"); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); vo.setState(State.Inactive); } diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index ac398e51265..9298f728532 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -741,3 +741,4 @@ ALTER TABLE `cloud`.`guest_os` ADD COLUMN `removed` datetime COMMENT 'Time when UPDATE `cloud`.`guest_os` SET `created` = now(); ALTER TABLE `cloud`.`vm_reservation` ADD COLUMN `deployment_planner` varchar(40) DEFAULT NULL COMMENT 'Preferred deployment planner for the vm'; ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_distributed_router boolean default false; +ALTER TABLE `cloud`.`vpc` ADD COLUMN uses_distributed_router boolean default false; From 32ac021043c82a636d154d244bc4e8d0f18e8b4f Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 5 Mar 2014 01:26:53 +0530 Subject: [PATCH 10/31] make Ovs as VPC provider --- server/src/com/cloud/network/vpc/VpcManagerImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 6da1a55e65f..c5a048c25cc 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -205,7 +205,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker")); private List vpcElements = null; private final List nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall); - private final List supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler, Provider.JuniperContrailVpcRouter); + private final List supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, + Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler, Provider.JuniperContrailVpcRouter, + Provider.Ovs); int _cleanupInterval; int _maxNetworks; From 100df9245599bb53636d955e658f9821566a9bd1 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Mon, 10 Mar 2014 11:58:37 +0530 Subject: [PATCH 11/31] Scripts that use ovs-vsctl and ovs-ofctl to setup a bridge for VPC in distributed routing mode, and setup flows appropriatley script to handle the VPC topology sent from management server in JSOn format. From the JSON file, reqired configuration (tunnel setup and flow rules setup) is setup on the bridge --- .../xenserver/cloudstack_pluginlib.py | 154 ++++++++++++++++++ .../vm/hypervisor/xenserver/ovs-vif-flows.py | 122 +++++++++++--- scripts/vm/hypervisor/xenserver/ovstunnel | 114 ++++++++++++- 3 files changed, 359 insertions(+), 31 deletions(-) diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index 422111f4f41..d2b95dc8930 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -21,6 +21,7 @@ import ConfigParser import logging import os import subprocess +import json from time import localtime, asctime @@ -176,6 +177,7 @@ def _build_flow_expr(**kwargs): dl_dst = 'dl_dst' in kwargs and ",dl_dst=%s" % kwargs['dl_dst'] or '' nw_src = 'nw_src' in kwargs and ",nw_src=%s" % kwargs['nw_src'] or '' nw_dst = 'nw_dst' in kwargs and ",nw_dst=%s" % kwargs['nw_dst'] or '' + table = 'table' in kwargs and ",table=%s" % kwargs['table'] or '' proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or '' ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or '' flow = (flow + in_port + dl_type + dl_src + dl_dst + @@ -219,3 +221,155 @@ def del_all_flows(bridge): def del_port(bridge, port): delPort = [VSCTL_PATH, "del-port", bridge, port] do_cmd(delPort) + +def get_network_id_for_vif(vif_name): + domain_id, device_id = vif_name[3:len(vif_name)].split(".") + dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"]) + vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"]) + vnet = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=other-config", + "param-key=cloudstack-network-id"]) + return vnet + +def get_network_id_for_tunnel_port(tunnelif_name): + vnet = do_cmd([VSCTL_PATH, "get", "interface", tunnelif_name, "options:cloudstack-network-id"]) + return vnet + +def clear_flooding_rules_for_port(bridge, ofport): + del_flows(bridge, in_port=ofport, table=2) + +def add_flooding_rules_for_port(bridge, in_ofport, out_ofports): + action = "".join("output:%s," %ofport for ofport in out_ofports)[:-1] + add_flow(bridge, priority=1100, in_port=in_ofport, table=1, actions=action) + +def get_ofport_for_vif(vif_name): + return do_cmd([VSCTL_PATH, "get", "interface", vif_name, "ofport"]) + +def get_macaddress_of_vif(vif_name): + domain_id, device_id = vif_name[3:len(vif_name)].split(".") + dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"]) + vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"]) + mac = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=MAC"]) + return mac + +def get_vif_name_from_macaddress(macaddress): + vif_uuid = do_cmd([XE_PATH, "vif-list", "MAC=%s" % macaddress, "--minimal"]) + vif_device_id = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=device"]) + vm_uuid = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=vm-uuid"]) + vm_domain_id = do_cmd([XE_PATH, "vm-param-get", "uuid=%s" % vm_uuid, "param-name=dom-id"]) + return "vif"+vm_domain_id+"."+vif_device_id + +def add_mac_lookup_table_entry(bridge, mac_address, out_of_port): + add_flow(bridge, priority=1100, dl_dst=mac_address, table=1, actions="output:%s" % out_of_port) + +def delete_mac_lookup_table_entry(bridge, mac_address): + del_flows(bridge, dl_dst=mac_address, table=1) + +def add_ip_lookup_table_entry(bridge, ip, dst_tier_gateway_mac, dst_vm_mac): + action_str = "mod_dl_sr:%s" % dst_tier_gateway_mac + ",mod_dl_dst:%s" % dst_vm_mac +",resubmit(,5)" + addflow = [OFCTL_PATH, "add-flow", bridge, "table=4", "nw_dst=%s" % ip, "actions=%s" %action_str] + do_cmd(addflow) + +def get_vms_on_host(vpc, host_id): + all_vms = vpc.vms + vms_on_host = [] + for vm in all_vms: + if vm.hostid == host_id: + vms_on_host.append(vm) + return vms_on_host + +def get_network_details(vpc, network_uuid): + tiers = vpc.tiers + for tier in tiers: + if tier.networkuuid == network_uuid: + return tier + return None + +class jsonLoader(object): + def __init__(self, obj): + for k in obj: + v = obj[k] + if isinstance(v, dict): + setattr(self, k, jsonLoader(v)) + elif isinstance(v, (list, tuple)): + if len(v) > 0 and isinstance(v[0], dict): + setattr(self, k, [jsonLoader(elem) for elem in v]) + else: + setattr(self, k, v) + else: + setattr(self, k, v) + + def __getattr__(self, val): + if val in self.__dict__: + return self.__dict__[val] + else: + return None + + def __repr__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) + in self.__dict__.iteritems())) + + def __str__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) + in self.__dict__.iteritems())) + +def configure_bridge_for_topology(bridge, this_host_id, json_config): + vpconfig = jsonLoader(json.loads(json_config)).vpc + + if vpconfig is None: + logging.debug("WARNING:Can't find VPC info in json config file") + return "FAILURE:IMPROPER_JSON_CONFG_FILE" + + # get the list of Vm's in the VPC from the JSON config + this_host_vms = get_vms_on_host(vpconfig, this_host_id) + + for vm in this_host_vms: + for nic in vm.nics: + mac_addr = nic.macaddress + ip = nic.ipaddress + vif_name = get_vif_name_from_macaddress(mac_addr) + of_port = get_ofport_for_vif(vif_name) + network = get_network_details(vpconfig, nic.networkuuid) + + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet on the found OFPORT + add_mac_lookup_table_entry(bridge, mac_addr, of_port) + + # Add flow rule in L3 look up table: if the destination IP = VM's IP then modify the packet + # to set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) + + # Add flow entry to send with intra tier traffic from the NIC to L2 lookup path) + addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, + "nw_dst=%s" %network.cidr, "actions=resubmit(,1)"] + do_cmd(addflow) + + #add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path) + addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, + "dl_dst=%s" %network.gatewaymac, "nw_dst=%s" %vpconfig.cidr, "actions=resubmit(,3)"] + do_cmd(addflow) + + # get the list of hosts on which VPC spans from the JSON config + vpc_spanning_hosts = vpconfig.hosts + + for host in vpc_spanning_hosts: + if this_host_id == host.hostid: + continue + other_host_vms = get_vms_on_host(vpconfig, host.hostid) + for vm in other_host_vms: + for nic in vm.nics: + mac_addr = nic.macaddress + ip = nic.ipaddress + network = get_network_details(vpconfig, nic.networkuuid) + gre_key = network.grekey + + # generate tunnel name from tunnel naming convention + tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid) + of_port = get_ofport_for_vif(tunnel_name) + + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet tunnel port + add_mac_lookup_table_entry(bridge, mac_addr, of_port) + + # Add flow tule in L3 look up table: if the destination IP = VM's IP then modify the packet + # set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) + + return "SUCCESS: successfully configured bridge as per the VPC toplogy" \ No newline at end of file diff --git a/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py b/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py index 8452daef147..ae375252e22 100644 --- a/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py +++ b/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py @@ -18,6 +18,7 @@ # A simple script for enabling and disabling per-vif rules for explicitly # allowing broadcast/multicast traffic on the port where the VIF is attached +import copy import os import sys @@ -65,7 +66,6 @@ def clear_rules(vif): except: pass - def main(command, vif_raw): if command not in ('online', 'offline'): return @@ -86,39 +86,111 @@ def main(command, vif_raw): # find xs network for this bridge, verify is used for ovs tunnel network xs_nw_uuid = pluginlib.do_cmd([pluginlib.XE_PATH, "network-list", "bridge=%s" % bridge, "--minimal"]) - result = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", + ovs_tunnel_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", "uuid=%s" % xs_nw_uuid, "param-name=other-config", "param-key=is-ovs-tun-network", "--minimal"]) - if result != 'True': - return - - vlan = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'br-to-vlan', bridge]) - if vlan != '0': - # We need the REAL bridge name - bridge = pluginlib.do_cmd([pluginlib.VSCTL_PATH, - 'br-to-parent', bridge]) + ovs_vpc_distributed_vr_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=is-ovs_vpc_distributed_vr_network", "--minimal"]) - vsctl_output = pluginlib.do_cmd([pluginlib.VSCTL_PATH, - 'list-ports', bridge]) - vifs = vsctl_output.split('\n') - vif_ofports = [] - for vif in vifs: - vif_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', - 'Interface', vif, 'ofport']) - if this_vif == vif: - this_vif_ofport = vif_ofport - if vif.startswith('vif'): - vif_ofports.append(vif_ofport) + if ovs_tunnel_network == 'True': + vlan = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'br-to-vlan', bridge]) + if vlan != '0': + # We need the REAL bridge name + bridge = pluginlib.do_cmd([pluginlib.VSCTL_PATH, + 'br-to-parent', bridge]) + vsctl_output = pluginlib.do_cmd([pluginlib.VSCTL_PATH, + 'list-ports', bridge]) + vifs = vsctl_output.split('\n') + vif_ofports = [] + for vif in vifs: + vif_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', + 'Interface', vif, 'ofport']) + if this_vif == vif: + this_vif_ofport = vif_ofport + if vif.startswith('vif'): + vif_ofports.append(vif_ofport) - if command == 'offline': - clear_flows(bridge, this_vif_ofport, vif_ofports) + if command == 'offline': + clear_flows(bridge, this_vif_ofport, vif_ofports) - if command == 'online': - apply_flows(bridge, this_vif_ofport, vif_ofports) + if command == 'online': + apply_flows(bridge, this_vif_ofport, vif_ofports) + if ovs_vpc_distributed_vr_network == 'True': + vlan = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'br-to-vlan', bridge]) + if vlan != '0': + # We need the REAL bridge name + bridge = pluginlib.do_cmd([pluginlib.VSCTL_PATH, + 'br-to-parent', bridge]) + vsctl_output = pluginlib.do_cmd([pluginlib.VSCTL_PATH, + 'list-ports', bridge]) + vif_network_id = pluginlib.get_network_id_for_vif(this_vif) + vnet_vif_ofports = [] + vnet_tunnelif_ofports = [] + vnet_all_ofports = [] + + ports = vsctl_output.split('\n') + for port in ports: + if_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', 'Interface', vif, 'ofport']) + if vif.startswith('vif'): + # check VIF is in same network as that of plugged vif + if vif_network_id != pluginlib.get_network_id_for_vif(port): + continue + vnet_vif_ofports.append(if_ofport) + vnet_all_ofports.append(if_ofport) + + if vif.startswith('t'): + # check tunnel port is in same network as that of plugged vif + if vif_network_id != pluginlib.get_network_id_for_tunnel_port(port): + continue + vnet_tunnelif_ofports.append(if_ofport) + vnet_all_ofports.append(if_ofport) + + if command == 'online': + for port in vnet_all_ofports: + pluginlib.clear_flooding_rules_for_port(bridge, port) + + # for a packet arrived from tunnel port, flood only on VIF ports + for port in vnet_tunnelif_ofports: + pluginlib.add_flooding_rules_for_port(bridge, port, vnet_vif_ofports) + + # send on all VIF and tunnel port excluding the port on which packet arrived + for port in vnet_vif_ofports: + vnet_all_ofports_copy = copy.copy(vnet_all_ofports) + vnet_all_ofports_copy.remove(port) + pluginlib.add_flooding_rules_for_port(bridge, port, vnet_all_ofports_copy) + + #learn that MAC is reachable through the VIF port + mac = pluginlib.get_macaddress_of_vif(this_vif) + pluginlib.add_mac_lookup_table_entry(bridge, mac, this_vif_ofport) + + if command == 'offline': + for port in vnet_all_ofports: + pluginlib.clear_flooding_rules_for_port(bridge, port) + vnet_all_ofports.remove(this_vif_ofport) + vnet_vif_ofports.remove(this_vif_ofport) + + # for a packet arrived from tunnel port, flood only on VIF ports + for port in vnet_tunnelif_ofports: + pluginlib.add_flooding_rules_for_port(bridge, port, vnet_vif_ofports) + + # for a packet from VIF port send on all VIF's and tunnel ports excluding the port on which packet arrived + for port in vnet_vif_ofports: + vnet_all_ofports_copy = copy.copy(vnet_all_ofports) + vnet_all_ofports_copy.remove(port) + pluginlib.add_flooding_rules_for_port(bridge, port, vnet_all_ofports_copy) + + #un-learn that MAC is reachable through the VIF port + mac = pluginlib.get_macaddress_of_vif(this_vif) + pluginlib.delete_mac_lookup_table_entry(bridge, mac) + + return + if __name__ == "__main__": if len(sys.argv) != 3: print "usage: %s [online|offline] vif-domid-idx" % \ diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel index 106be0441ce..d558e972cda 100755 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -124,6 +124,75 @@ def setup_ovs_bridge(session, args): logging.debug("Setup_ovs_bridge completed with result:%s" % result) return result +@echo +def setup_ovs_bridge_for_distributed_routing(session, args): + bridge = args.pop("bridge") + key = args.pop("key") + xs_nw_uuid = args.pop("xs_nw_uuid") + cs_host_id = args.pop("cs_host_id") + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + logging.debug("About to manually create the bridge:%s" % bridge) + # create a bridge with the same name as the xapi network + res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge, + "--", "set", "bridge", bridge]) + logging.debug("Bridge has been manually created:%s" % res) + # TODO: Make sure xs-network-uuid is set into external_ids + lib.do_cmd([lib.VSCTL_PATH, "set", "Bridge", bridge, + "external_ids:xs-network-uuid=%s" % xs_nw_uuid]) + + # Non empty result means something went wrong + if res: + result = "FAILURE:%s" % res + else: + # Verify the bridge actually exists, with the gre_key properly set + res = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", + bridge, "other_config:gre_key"]) + if key in res: + result = "SUCCESS:%s" % bridge + else: + result = "FAILURE:%s" % res + # Finally note in the xenapi network object that the network has + # been configured + xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list", + "bridge=%s" % bridge, "--minimal"]) + lib.do_cmd([lib.XE_PATH, "network-param-set", "uuid=%s" % xs_nw_uuid, + "other-config:is-ovs_vpc_distributed_vr_network=True"]) + conf_hosts = lib.do_cmd([lib.XE_PATH, "network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=ovs-host-setup", "--minimal"]) + conf_hosts = cs_host_id + (conf_hosts and ',%s' % conf_hosts or '') + lib.do_cmd([lib.XE_PATH, "network-param-set", "uuid=%s" % xs_nw_uuid, + "other-config:ovs-host-setup=%s" % conf_hosts]) + + # add a default flow rule to send broadcast and multi-cast packets to L2 flooding table + lib.add_flow(bridge, priority=1000, dl_dst='ff:ff:ff:ff:ff:ff', table=0, actions='resubmit(,2)') + lib.add_flow(bridge, priority=1000, nw_dst='224.0.0.0/24', table=0, actions='resubmit(,2)') + + # add a default flow rule to send uni-cast traffic to L2 lookup table + lib.add_flow(bridge, priority=0, table=0, actions='resubmit(,1)') + + # add a default rule to send unknown mac address to L2 flooding table + lib.add_flow(bridge, priority=0, table=1, actions='resubmit(,2)') + + # add a default rule in L2 flood table to drop packet + lib.add_flow(bridge, priority=0, table=2, actions='drop') + + # add a default rule in egress table to forward packet to L3 lookup table + lib.add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + + # add a default rule in L3 lookup table to forward packet to L2 lookup table + lib.add_flow(bridge, priority=0, table=4, actions='resubmit(,1)') + + # add a default rule in egress table to forward packet to L3 lookup table + lib.add_flow(bridge, priority=0, table=5, actions='drop') + + logging.debug("Setup_ovs_bridge completed with result:%s" % result) + return result @echo def destroy_ovs_bridge(session, args): @@ -220,13 +289,36 @@ def create_tunnel(session, args): # Ensure no trailing LF if tun_ofport.endswith('\n'): tun_ofport = tun_ofport[:-1] - # add flow entryies for dropping broadcast coming in from gre tunnel - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + # find xs network for this bridge, verify is used for ovs tunnel network + xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list", + "bridge=%s" % bridge, "--minimal"]) + ovs_tunnel_network = lib.do_cmd([lib.XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=is-ovs-tun-network", "--minimal"]) + ovs_vpc_distributed_vr_network = lib.do_cmd([lib.XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=is-ovs_vpc_distributed_vr_network", "--minimal"]) + if ovs_tunnel_network == 'True': + # add flow entryies for dropping broadcast coming in from gre tunnel + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, nw_dst='224.0.0.0/24', actions='drop') - drop_flow_setup = True - logging.debug("Broadcast drop rules added") + drop_flow_setup = True + logging.debug("Broadcast drop rules added") + + if ovs_vpc_distributed_vr_network == 'True': + # add flow rules for dropping broadcast coming in from tunnel ports + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + nw_dst='224.0.0.0/24', actions='drop') + + # add flow rule to send the traffic from tunnel ports to L2 switching table only + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, actions='resubmit(,1)') + return "SUCCESS:%s" % name except: logging.debug("An unexpected error occured. Rolling back") @@ -293,10 +385,20 @@ def getLabel(session, args): return label return False +@echo +def configure_ovs_bridge_for_network_topology(session, args): + bridge = args.pop("bridge") + json_config = args.pop("config") + this_host_id = args.pop("host-id") + + return lib.configure_bridge_for_topology(bridge, this_host_id, json_config) + if __name__ == "__main__": XenAPIPlugin.dispatch({"create_tunnel": create_tunnel, "destroy_tunnel": destroy_tunnel, "setup_ovs_bridge": setup_ovs_bridge, "destroy_ovs_bridge": destroy_ovs_bridge, "is_xcp": is_xcp, - "getLabel": getLabel}) + "getLabel": getLabel, + "setup_ovs_bridge_for_distributed_routing": setup_ovs_bridge_for_distributed_routing, + "configure_ovs_bridge_for_network_topology": configure_ovs_bridge_for_network_topology}) From e045883c52240a0d960d4d8496910ed189c80b59 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Mon, 10 Mar 2014 12:52:30 +0530 Subject: [PATCH 12/31] introduce OvsNetworkTopologyGuru that has convinenace functions to - get the hosts on which VPC spans given vpc id - get the VM's in the VPC - get the hosts on which a network spans - get the VPC's to which a hosts is part of - get VM's of a VPC on a hosts introduces capability to build a physical toplogy representation of a VPC. This json file is encapsulated in OvsVpcPhysicalTopologyConfigCommand, and is used to send full topology to hypervisor hosts. On hypervisor this json config can be used to setup tunnels, configure bridge, add flow rules etc Ovs GURU, to use different broasdcast scheme VS://vpcid.gerkey for the networks in VPC that use distributed routing each VIF and tunnel interface to carry the network UUID in other/options config --- api/src/com/cloud/agent/api/to/NicTO.java | 8 + .../resource/LibvirtComputingResource.java | 34 +- .../xen/resource/CitrixResourceBase.java | 95 +++-- .../cloudstack/ovs/spring-ovs-context.xml | 2 +- .../agent/api/OvsCreateTunnelCommand.java | 19 +- .../agent/api/OvsDestroyBridgeCommand.java | 10 +- .../agent/api/OvsDestroyTunnelCommand.java | 10 +- .../agent/api/OvsSetupBridgeCommand.java | 10 +- .../OvsVpcPhysicalTopologyConfigCommand.java | 127 +++++++ .../com/cloud/network/element/OvsElement.java | 35 +- .../network/guru/OvsGuestNetworkGuru.java | 17 +- .../network/ovs/OvsNetworkTopologyGuru.java | 49 +++ .../ovs/OvsNetworkTopologyGuruImpl.java | 74 ++++ .../cloud/network/ovs/OvsTunnelManager.java | 16 +- .../network/ovs/OvsTunnelManagerImpl.java | 356 +++++++++++++----- .../com/cloud/network/ovs/dao/OvsTunnel.java | 24 ++ .../network/ovs/dao/OvsTunnelNetworkVO.java | 2 +- scripts/vm/hypervisor/xenserver/ovstunnel | 2 + .../cloud/hypervisor/HypervisorGuruBase.java | 7 + 19 files changed, 737 insertions(+), 160 deletions(-) create mode 100644 plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java create mode 100644 plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java create mode 100644 plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java create mode 100644 plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnel.java diff --git a/api/src/com/cloud/agent/api/to/NicTO.java b/api/src/com/cloud/agent/api/to/NicTO.java index bd8f24ec563..67660c85307 100644 --- a/api/src/com/cloud/agent/api/to/NicTO.java +++ b/api/src/com/cloud/agent/api/to/NicTO.java @@ -80,4 +80,12 @@ public class NicTO extends NetworkTO { public List getNicSecIps() { return nicSecIps; } + + public String getNetworkUuid() { + return super.getUuid(); + } + + public void setNetworkUuid(String uuid) { + super.setUuid(uuid); + } } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index cdeedfea684..c1a77214bec 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1388,23 +1388,22 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private Answer execute(OvsSetupBridgeCommand cmd) { - findOrCreateTunnelNetwork(cmd.getKey()); + findOrCreateTunnelNetwork(cmd.getBridgeName()); configureTunnelNetwork(cmd.getNetworkId(), cmd.getHostId(), - cmd.getKey()); + cmd.getBridgeName()); s_logger.debug("OVS Bridge configured"); return new Answer(cmd, true, null); } private Answer execute(OvsDestroyBridgeCommand cmd) { - destroyTunnelNetwork(cmd.getKey()); + destroyTunnelNetwork(cmd.getBridgeName()); s_logger.debug("OVS Bridge destroyed"); return new Answer(cmd, true, null); } - private synchronized void destroyTunnelNetwork(int key) { + private synchronized void destroyTunnelNetwork(String bridge) { try { - findOrCreateTunnelNetwork(key); - String bridge = "OVSTunnel" + key; + findOrCreateTunnelNetwork(bridge); Script cmd = new Script(_ovsTunnelPath, _timeout, s_logger); cmd.add("destroy_ovs_bridge"); cmd.add("--bridge", bridge); @@ -1423,9 +1422,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } - private synchronized boolean findOrCreateTunnelNetwork(long key) { + private synchronized boolean findOrCreateTunnelNetwork(String nwName) { try { - String nwName = "OVSTunnel" + key; if (checkNetwork(nwName)) { return true; } @@ -1443,10 +1441,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private synchronized boolean configureTunnelNetwork(long networkId, - long hostId, int key) { + long hostId, String nwName) { try { - findOrCreateTunnelNetwork(key); - String nwName = "OVSTunnel" + key; + findOrCreateTunnelNetwork(nwName); String configuredHosts = Script .runSimpleBashScript("ovs-vsctl get bridge " + nwName + " other_config:ovs_host_setup"); @@ -1463,7 +1460,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (!configured) { Script cmd = new Script(_ovsTunnelPath, _timeout, s_logger); cmd.add("setup_ovs_bridge"); - cmd.add("--key", String.valueOf(key)); + cmd.add("--key", nwName); cmd.add("--cs_host_id", ((Long)hostId).toString()); cmd.add("--bridge", nwName); String result = cmd.execute(); @@ -1481,16 +1478,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private OvsCreateTunnelAnswer execute(OvsCreateTunnelCommand cmd) { - String bridge = "OVSTunnel" + cmd.getKey(); + String bridge = cmd.getNetworkName(); try { - if (!findOrCreateTunnelNetwork(cmd.getKey())) { + if (!findOrCreateTunnelNetwork(bridge)) { s_logger.debug("Error during bridge setup"); return new OvsCreateTunnelAnswer(cmd, false, "Cannot create network", bridge); } configureTunnelNetwork(cmd.getNetworkId(), cmd.getFrom(), - cmd.getKey()); + cmd.getNetworkName()); Script command = new Script(_ovsTunnelPath, _timeout, s_logger); command.add("create_tunnel"); command.add("--bridge", bridge); @@ -1515,16 +1512,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private Answer execute(OvsDestroyTunnelCommand cmd) { try { - if (!findOrCreateTunnelNetwork(cmd.getKey())) { + if (!findOrCreateTunnelNetwork(cmd.getBridgeName())) { s_logger.warn("Unable to find tunnel network for GRE key:" - + cmd.getKey()); + + cmd.getBridgeName()); return new Answer(cmd, false, "No network found"); } - String bridge = "OVSTunnel" + cmd.getKey(); Script command = new Script(_ovsTunnelPath, _timeout, s_logger); command.add("destroy_tunnel"); - command.add("--bridge", bridge); + command.add("--bridge", cmd.getBridgeName()); command.add("--iface_name", cmd.getInPortName()); String result = command.execute(); if (result == null) { diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 9103b594081..3e7dfafd903 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -148,6 +148,7 @@ import com.cloud.agent.api.OvsFetchInterfaceCommand; import com.cloud.agent.api.OvsSetTagAndFlowAnswer; import com.cloud.agent.api.OvsSetTagAndFlowCommand; import com.cloud.agent.api.OvsSetupBridgeCommand; +import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; import com.cloud.agent.api.PerformanceMonitorAnswer; import com.cloud.agent.api.PerformanceMonitorCommand; import com.cloud.agent.api.PingCommand; @@ -507,6 +508,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((OvsSetTagAndFlowCommand)cmd); } else if (clazz == OvsDeleteFlowCommand.class) { return execute((OvsDeleteFlowCommand)cmd); + } else if (clazz == OvsVpcPhysicalTopologyConfigCommand.class) { + return execute((OvsVpcPhysicalTopologyConfigCommand) cmd); } else if (clazz == CleanupNetworkRulesCmd.class) { return execute((CleanupNetworkRulesCmd)cmd); } else if (clazz == NetworkRulesSystemVmCommand.class) { @@ -964,15 +967,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe /** * This method just creates a XenServer network following the tunnel network naming convention */ - private synchronized Network findOrCreateTunnelNetwork(Connection conn, long key) { + private synchronized Network findOrCreateTunnelNetwork(Connection conn, String nwName) { try { - String nwName = "OVSTunnel" + key; Network nw = null; Network.Record rec = new Network.Record(); Set networks = Network.getByNameLabel(conn, nwName); if (networks.size() == 0) { - rec.nameDescription = "tunnel network id# " + key; + rec.nameDescription = "tunnel network id# " + nwName; rec.nameLabel = nwName; //Initialize the ovs-host-setup to avoid error when doing get-param in plugin Map otherConfig = new HashMap(); @@ -981,7 +983,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe nw = Network.create(conn, rec); // Plug dom0 vif only when creating network if (!is_xcp()) - enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + key); + enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + nwName); s_logger.debug("### Xen Server network for tunnels created:" + nwName); } else { nw = networks.iterator().next(); @@ -997,10 +999,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe /** * This method creates a XenServer network and configures it for being used as a L2-in-L3 tunneled network */ - private synchronized Network configureTunnelNetwork(Connection conn, long networkId, long hostId, int key) { + private synchronized Network configureTunnelNetwork(Connection conn, long networkId, long hostId, String bridgeName) { try { - Network nw = findOrCreateTunnelNetwork(conn, key); - String nwName = "OVSTunnel" + key; + Network nw = findOrCreateTunnelNetwork(conn, bridgeName); + String nwName = bridgeName; //Invoke plugin to setup the bridge which will be used by this network String bridge = nw.getBridge(conn); Map nwOtherConfig = nw.getOtherConfig(conn); @@ -1018,11 +1020,20 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (!configured) { // Plug dom0 vif only if not done before for network and host if (!is_xcp()) - enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + key); - String result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge", "bridge", bridge, - "key", String.valueOf(key), - "xs_nw_uuid", nw.getUuid(conn), - "cs_host_id", ((Long)hostId).toString()); + enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + bridgeName); + String result; + if (bridgeName.startsWith("OVS-DR-VPC-Bridge")) { + result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge_for_distributed_routing", "bridge", bridge, + "key", bridgeName, + "xs_nw_uuid", nw.getUuid(conn), + "cs_host_id", ((Long)hostId).toString()); + } else { + result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge", "bridge", bridge, + "key", bridgeName, + "xs_nw_uuid", nw.getUuid(conn), + "cs_host_id", ((Long)hostId).toString()); + } + //Note down the fact that the ovs bridge has been setup String[] res = result.split(":"); if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) { @@ -1037,9 +1048,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - private synchronized void destroyTunnelNetwork(Connection conn, int key) { + private synchronized void destroyTunnelNetwork(Connection conn, String bridgeName) { try { - Network nw = findOrCreateTunnelNetwork(conn, key); + Network nw = findOrCreateTunnelNetwork(conn, bridgeName); String bridge = nw.getBridge(conn); String result = callHostPlugin(conn, "ovstunnel", "destroy_ovs_bridge", "bridge", bridge); String[] res = result.split(":"); @@ -1079,8 +1090,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe _isOvs = true; return setupvSwitchNetwork(conn); } else { - long vnetId = Long.parseLong(BroadcastDomainType.getValue(uri)); - return findOrCreateTunnelNetwork(conn, vnetId); + return findOrCreateTunnelNetwork(conn, getOvsTunnelNetworkName(BroadcastDomainType.getValue(uri))); } } else if (type == BroadcastDomainType.Storage) { if (uri == null) { @@ -1102,6 +1112,19 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe throw new CloudRuntimeException("Unable to support this type of network broadcast domain: " + nic.getBroadcastUri()); } + private String getOvsTunnelNetworkName(String broadcastUri) { + if (broadcastUri.contains(".")) { + String[] parts = broadcastUri.split("."); + return "OVS-DR-VPC-Bridge"+parts[0]; + } else { + try { + return "OVSTunnel" + broadcastUri; + } catch (Exception e) { + return null; + } + } + } + protected VIF createVif(Connection conn, String vmName, VM vm, VirtualMachineTO vmSpec, NicTO nic) throws XmlRpcException, XenAPIException { assert (nic.getUuid() != null) : "Nic should have a uuid value"; @@ -1122,7 +1145,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (vmSpec != null) { vifr.otherConfig.put("cloudstack-vm-id", vmSpec.getUuid()); } - + vifr.otherConfig.put("cloudstack-network-id", nic.getNetworkUuid()); vifr.network = getNetwork(conn, nic); if (nic.getNetworkRateMbps() != null && nic.getNetworkRateMbps().intValue() != -1) { @@ -5220,15 +5243,15 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe private Answer execute(OvsSetupBridgeCommand cmd) { Connection conn = getConnection(); - findOrCreateTunnelNetwork(conn, cmd.getKey()); - configureTunnelNetwork(conn, cmd.getNetworkId(), cmd.getHostId(), cmd.getKey()); + findOrCreateTunnelNetwork(conn, cmd.getBridgeName()); + configureTunnelNetwork(conn, cmd.getNetworkId(), cmd.getHostId(), cmd.getBridgeName()); s_logger.debug("OVS Bridge configured"); return new Answer(cmd, true, null); } private Answer execute(OvsDestroyBridgeCommand cmd) { Connection conn = getConnection(); - destroyTunnelNetwork(conn, cmd.getKey()); + destroyTunnelNetwork(conn, cmd.getBridgeName()); s_logger.debug("OVS Bridge destroyed"); return new Answer(cmd, true, null); } @@ -5236,14 +5259,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe private Answer execute(OvsDestroyTunnelCommand cmd) { Connection conn = getConnection(); try { - Network nw = findOrCreateTunnelNetwork(conn, cmd.getNetworkId()); + Network nw = findOrCreateTunnelNetwork(conn, cmd.getBridgeName()); if (nw == null) { - s_logger.warn("Unable to find tunnel network for GRE key:" + cmd.getKey()); + s_logger.warn("Unable to find tunnel network for GRE key:" + cmd.getBridgeName()); return new Answer(cmd, false, "No network found"); } String bridge = nw.getBridge(conn); - String result = callHostPlugin(conn, "ovstunnel", "destroy_tunnel", "bridge", bridge, "in_port", cmd.getInPortName()); + String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", bridge, "in_port", cmd.getInPortName()); if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, result); } else { @@ -5255,6 +5278,22 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } + public Answer execute(OvsVpcPhysicalTopologyConfigCommand cmd) { + Connection conn = getConnection(); + try { + String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", + cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString()); + if (result.equalsIgnoreCase("SUCCESS")) { + return new Answer(cmd, true, result); + } else { + return new Answer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught exception while updating host with latest VPC topology", e); + return new Answer(cmd, false, e.getMessage()); + } + } + private Answer execute(UpdateHostPasswordCommand cmd) { _password.add(cmd.getNewPassword()); return new Answer(cmd, true, null); @@ -5264,17 +5303,19 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); String bridge = "unknown"; try { - Network nw = findOrCreateTunnelNetwork(conn, cmd.getKey()); + Network nw = findOrCreateTunnelNetwork(conn, cmd.getNetworkName()); if (nw == null) { s_logger.debug("Error during bridge setup"); return new OvsCreateTunnelAnswer(cmd, false, "Cannot create network", bridge); } - configureTunnelNetwork(conn, cmd.getNetworkId(), cmd.getFrom(), cmd.getKey()); + configureTunnelNetwork(conn, cmd.getNetworkId(), cmd.getFrom(), cmd.getNetworkName()); bridge = nw.getBridge(conn); String result = - callHostPlugin(conn, "ovstunnel", "create_tunnel", "bridge", bridge, "remote_ip", cmd.getRemoteIp(), "key", cmd.getKey().toString(), "from", - cmd.getFrom().toString(), "to", cmd.getTo().toString()); + callHostPlugin(conn, "ovstunnel", "create_tunnel", "bridge", bridge, "remote_ip", cmd.getRemoteIp(), + "key", cmd.getKey().toString(), "from", + cmd.getFrom().toString(), "to", cmd.getTo().toString(), "cloudstack-network-id", + cmd.getNetworkUuid()); String[] res = result.split(":"); if (res.length == 2 && res[0].equalsIgnoreCase("SUCCESS")) { return new OvsCreateTunnelAnswer(cmd, true, result, res[1], bridge); diff --git a/plugins/network-elements/ovs/resources/META-INF/cloudstack/ovs/spring-ovs-context.xml b/plugins/network-elements/ovs/resources/META-INF/cloudstack/ovs/spring-ovs-context.xml index e60d93e08a1..fa56d5d1e63 100644 --- a/plugins/network-elements/ovs/resources/META-INF/cloudstack/ovs/spring-ovs-context.xml +++ b/plugins/network-elements/ovs/resources/META-INF/cloudstack/ovs/spring-ovs-context.xml @@ -38,5 +38,5 @@ - + diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsCreateTunnelCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsCreateTunnelCommand.java index 7f151fbb680..3e08531f06a 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsCreateTunnelCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsCreateTunnelCommand.java @@ -20,10 +20,13 @@ package com.cloud.agent.api; public class OvsCreateTunnelCommand extends Command { Integer key; String remoteIp; + String networkName; Long from; Long to; long networkId; + String networkUuid; + // for debug info String fromIp; @@ -33,13 +36,15 @@ public class OvsCreateTunnelCommand extends Command { } public OvsCreateTunnelCommand(String remoteIp, Integer key, Long from, - Long to, long networkId, String fromIp) { + Long to, long networkId, String fromIp, String networkName, String networkUuid) { this.remoteIp = remoteIp; this.key = key; this.from = from; this.to = to; this.networkId = networkId; this.fromIp = fromIp; + this.networkName = networkName; + this.networkUuid = networkUuid; } public Integer getKey() { @@ -66,4 +71,16 @@ public class OvsCreateTunnelCommand extends Command { return fromIp; } + public String getNetworkName() { + return networkName; + } + + + public String getNetworkUuid() { + return networkUuid; + } + + public void setNetworkUuid(String networkUuid) { + this.networkUuid = networkUuid; + } } diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyBridgeCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyBridgeCommand.java index 59b50beb9a7..f9205c5e9c9 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyBridgeCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyBridgeCommand.java @@ -21,19 +21,19 @@ package com.cloud.agent.api; public class OvsDestroyBridgeCommand extends Command { Long networkId; - Integer key; + String name; - public OvsDestroyBridgeCommand(Long networkId, Integer key) { + public OvsDestroyBridgeCommand(Long networkId, String name) { this.networkId = networkId; - this.key = key; + this.name = name; } public Long getNetworkId() { return networkId; } - public Integer getKey() { - return key; + public String getBridgeName() { + return name; } @Override diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyTunnelCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyTunnelCommand.java index 4776a07eddd..9fc6c376034 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyTunnelCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsDestroyTunnelCommand.java @@ -20,14 +20,14 @@ package com.cloud.agent.api; public class OvsDestroyTunnelCommand extends Command { Long networkId; - Integer key; + String networkName; String inPortName; - public OvsDestroyTunnelCommand(Long networkId, Integer key, + public OvsDestroyTunnelCommand(Long networkId, String networkName, String inPortName) { this.networkId = networkId; this.inPortName = inPortName; - this.key = key; + this.networkName = networkName; } public Long getNetworkId() { @@ -38,8 +38,8 @@ public class OvsDestroyTunnelCommand extends Command { return inPortName; } - public Integer getKey() { - return key; + public String getBridgeName() { + return networkName; } @Override diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsSetupBridgeCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsSetupBridgeCommand.java index e3390f3c6f7..a1045980fc0 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsSetupBridgeCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsSetupBridgeCommand.java @@ -19,7 +19,7 @@ package com.cloud.agent.api; public class OvsSetupBridgeCommand extends Command { - Integer key; + String name; Long hostId; Long networkId; @@ -28,14 +28,14 @@ public class OvsSetupBridgeCommand extends Command { return true; } - public OvsSetupBridgeCommand(Integer key, Long hostId, Long networkId) { - this.key = key; + public OvsSetupBridgeCommand(String name, Long hostId, Long networkId) { + this.name = name; this.hostId = hostId; this.networkId = networkId; } - public Integer getKey() { - return key; + public String getBridgeName() { + return name; } public Long getHostId() { diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java new file mode 100644 index 00000000000..35d4c6e7050 --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java @@ -0,0 +1,127 @@ +// 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.agent.api; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * This command represents view of how a VPC is laid out (on which hosts, which VM is on which host etc) + * on the physical infrastructure. + */ +public class OvsVpcPhysicalTopologyConfigCommand extends Command { + + VpcConfig vpcConfig =null; + long hostId; + String bridgeName; + + public static class Host { + long hostId; + String ipAddress; + + public Host (long hostId, String ipAddress) { + this.hostId = hostId; + this.ipAddress = ipAddress; + } + } + + public static class Nic { + String ipAddress; + String macAddress; + String networkUuid; + public Nic (String ipAddress, String macAddress, String networkUuid) { + this.ipAddress = ipAddress; + this.macAddress = macAddress; + this.networkUuid = networkUuid; + } + } + + public static class Tier { + long greKey; + String networkUuid; + String gatewayIp; + String gatewayMac; + String cidr; + public Tier(long greKey, String networkUuid, String gatewayIp, String gatewayMac, String cidr) { + this.greKey = greKey; + this.networkUuid = networkUuid; + this.gatewayIp = gatewayIp; + this.gatewayMac = gatewayMac; + this.cidr = cidr; + } + } + + public static class Vm { + long hostId; + Nic[] nics; + public Vm(long hostId, Nic[] nics) { + this.hostId = hostId; + this.nics = nics; + } + } + + public static class Vpc { + String cidr; + Host[] hosts; + Tier[] tiers; + Vm[] vms; + public Vpc(Host[] hosts, Tier[] tiers, Vm[] vms, String cidr) { + this.hosts = hosts; + this.tiers = tiers; + this.vms = vms; + this.cidr = cidr; + } + } + + public static class VpcConfig { + Vpc vpc; + public VpcConfig(Vpc vpc) { + this.vpc = vpc; + } + } + + public OvsVpcPhysicalTopologyConfigCommand(Host[] hosts, Tier[] tiers, Vm[] vms, String cidr) { + Vpc vpc = new Vpc(hosts, tiers, vms, cidr); + vpcConfig = new VpcConfig(vpc); + } + + public String getjsonVpcConfig() { + Gson gson = new GsonBuilder().create(); + return gson.toJson(vpcConfig).toLowerCase(); + } + + @Override + public boolean executeInSequence() { + return false; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getHostId() { + return hostId; + } + + public String getBridgeName() { + return bridgeName; + } + + public void setBridgeName(String bridgeName) { + this.bridgeName = bridgeName; + } +} \ No newline at end of file diff --git a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java index 05e81a12a93..c28d90808ed 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.element; +import com.cloud.host.dao.HostDao; +import com.cloud.vm.dao.UserVmDao; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -73,6 +75,8 @@ import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; @Local(value = {NetworkElement.class, ConnectivityProvider.class, SourceNatServiceProvider.class, StaticNatServiceProvider.class, @@ -93,6 +97,10 @@ StaticNatServiceProvider, IpDeployer { DomainRouterDao _routerDao; @Inject VpcVirtualNetworkApplianceManager _routerMgr; + @Inject + UserVmDao _userVmDao; + @Inject + HostDao _hostDao; private static final Logger s_logger = Logger.getLogger(OvsElement.class); private static final Map> capabilities = setCapabilities(); @@ -171,7 +179,12 @@ StaticNatServiceProvider, IpDeployer { return false; } - _ovsTunnelMgr.vmCheckAndCreateTunnel(vm, network, dest); + if (vm.getType() != VirtualMachine.Type.User && vm.getType() != VirtualMachine.Type.DomainRouter) { + return false; + } + + // prepare the tunnel network on the host, in order for VM to get launched + _ovsTunnelMgr.checkAndPrepareHostForTunnelNetwork(network, dest.getHost()); return true; } @@ -192,7 +205,25 @@ StaticNatServiceProvider, IpDeployer { return false; } - _ovsTunnelMgr.checkAndDestroyTunnel(vm.getVirtualMachine(), network); + List userVms = _userVmDao.listByAccountIdAndHostId(vm.getVirtualMachine().getAccountId(), + vm.getVirtualMachine().getHostId()); + if (vm.getType() == VirtualMachine.Type.User) { + if (userVms.size() > 1) { + return true; + } + + List routers = _routerDao.findByNetwork(network.getId()); + for (DomainRouterVO router : routers) { + if (router.getHostId().equals(vm.getVirtualMachine().getHostId())) { + return true; + } + } + } else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) { + return true; + } + + HostVO host = _hostDao.findById(vm.getVirtualMachine().getHostId()); + _ovsTunnelMgr.checkAndRemoveHostFromTunnelNetwork(network, host); return true; } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java index 8fa636da593..2814c2a0837 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.guru; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; import javax.ejb.Local; import javax.inject.Inject; @@ -61,6 +63,8 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { OvsTunnelManager _ovsTunnelMgr; @Inject NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao; + @Inject + VpcDao _vpcDao; OvsGuestNetworkGuru() { super(); @@ -145,10 +149,14 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { implemented.setCidr(network.getCidr()); } - // do we need to create switch right now? - implemented.setBroadcastDomainType(BroadcastDomainType.Vswitch); + if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { + String keyStr = BroadcastDomainType.getValue(implemented.getBroadcastUri()); + Long vpcid= network.getVpcId(); + implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(vpcid.toString()+keyStr)); + } + return implemented; } @@ -215,4 +223,9 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { implemented.setBroadcastUri(network.getBroadcastUri()); } } + + boolean isVpcEnabledForDistributedRouter(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + return vpc.usesDistributedRouter(); + } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java new file mode 100644 index 00000000000..c410d10c22d --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java @@ -0,0 +1,49 @@ +// 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.ovs; + +import com.cloud.utils.component.Manager; + +import java.util.List; + +public interface OvsNetworkTopologyGuru extends Manager { + + /** + * get the list of hypervisor hosts id's on which VM's belonging to the network currently spans + */ + public List getNetworkSpanedHosts(long networkId); + + /** + * get the list of hypervisor hosts id's on which VM's belonging to a VPC spans + */ + public List getVpcSpannedHosts(long vpId); + + /** + * get the list of VPC id's of the vpc's for which one or more VM's from the VPC are running on the host + */ + public List getVpcOnHost(long hostId); + + /** + * get the list of all active Vm id's in the VPC for all ther tiers + */ + public List getAllActiveVmsInVpc(long vpcId); + + /** + * get the list of all Vm id's in the VPC for all the tiers that are running on the host + */ + public List getActiveVmsInVpcOnHost(long vpcId, long hostId); +} diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java new file mode 100644 index 00000000000..7560e35c207 --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java @@ -0,0 +1,74 @@ +package com.cloud.network.ovs; + +import com.cloud.utils.component.ManagerBase; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.UserVmDao; +import java.util.ArrayList; +import java.util.List; +import javax.ejb.Local; +import javax.inject.Inject; +import org.springframework.stereotype.Component; + +@Component +@Local(value = {OvsNetworkTopologyGuru.class}) +public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetworkTopologyGuru { + + @Inject + UserVmDao _userVmDao; + @Inject + DomainRouterDao _routerDao; + + /** + * get the list of hypervisor hosts on which VM's belonging to a network currently spans + */ + public List getNetworkSpanedHosts(long networkId) { + List hostIds = new ArrayList(); + // Find active VMs with a NIC on the target network + List vms = _userVmDao.listByNetworkIdAndStates(networkId, + VirtualMachine.State.Running, VirtualMachine.State.Starting, VirtualMachine.State.Stopping, VirtualMachine.State.Unknown, + VirtualMachine.State.Migrating); + // Find routers for the network + List routers = _routerDao.findByNetwork(networkId); + List ins = new ArrayList(); + if (vms != null) { + ins.addAll(vms); + } + if (routers.size() != 0) { + ins.addAll(routers); + } + for (VMInstanceVO v : ins) { + Long rh = v.getHostId(); + if (rh == null) { + continue; + } + if (!hostIds.contains(rh)) { + hostIds.add(rh); + } + } + return hostIds; + } + + @Override + public List getVpcSpannedHosts(long vpId) { + return null; + } + + @Override + public List getVpcOnHost(long hostId) { + return null; + } + + @Override + public List getAllActiveVmsInVpc(long vpcId) { + return null; + } + + @Override + public List getActiveVmsInVpcOnHost(long vpcId, long hostId) { + return null; + } +} diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManager.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManager.java index 118beeb58cb..cd881363ec3 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManager.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManager.java @@ -16,18 +16,24 @@ // under the License. package com.cloud.network.ovs; -import com.cloud.deploy.DeployDestination; +import com.cloud.host.Host; import com.cloud.network.Network; import com.cloud.utils.component.Manager; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineProfile; public interface OvsTunnelManager extends Manager { boolean isOvsTunnelEnabled(); - public void vmCheckAndCreateTunnel(VirtualMachineProfile vm, Network nw, DeployDestination dest); + /** + * create a bridge on the host if not already created for the network and establish full tunnel mesh with + * the rest of the hosts on which network spans + */ + public void checkAndPrepareHostForTunnelNetwork(Network nw, Host host); - public void checkAndDestroyTunnel(VirtualMachine vm, Network nw); + /** + * remove the bridge and tunnels to the hosts on which network spans if there are no other VM's + * belonging to the network are running on the host + */ + public void checkAndRemoveHostFromTunnelNetwork(Network nw, Host host); } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 320568ba084..ae37095360f 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -16,6 +16,12 @@ // under the License. package com.cloud.network.ovs; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.VpcManager; +import com.cloud.vm.Nic; +import com.cloud.vm.NicVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -42,9 +48,9 @@ import com.cloud.agent.api.OvsDestroyTunnelCommand; import com.cloud.agent.api.OvsFetchInterfaceAnswer; import com.cloud.agent.api.OvsFetchInterfaceCommand; import com.cloud.agent.api.OvsSetupBridgeCommand; +import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; import com.cloud.agent.manager.Commands; import com.cloud.configuration.Config; -import com.cloud.deploy.DeployDestination; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.host.Host; @@ -60,19 +66,15 @@ import com.cloud.network.ovs.dao.OvsTunnelInterfaceDao; import com.cloud.network.ovs.dao.OvsTunnelInterfaceVO; import com.cloud.network.ovs.dao.OvsTunnelNetworkDao; import com.cloud.network.ovs.dao.OvsTunnelNetworkVO; +import com.cloud.network.ovs.dao.OvsTunnel; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.VpcVO; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.vm.DomainRouterVO; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.UserVmDao; @Component @Local(value = {OvsTunnelManager.class}) @@ -91,8 +93,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage HostDao _hostDao; @Inject PhysicalNetworkTrafficTypeDao _physNetTTDao; - @Inject - UserVmDao _userVmDao; + @Inject DomainRouterDao _routerDao; @Inject @@ -101,6 +102,16 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage OvsTunnelInterfaceDao _tunnelInterfaceDao; @Inject AgentManager _agentMgr; + @Inject + OvsNetworkTopologyGuru _ovsNetworkToplogyGuru; + @Inject + VpcDao _vpcDao; + @Inject + VpcManager _vpcMgr; + @Inject + protected VMInstanceDao _vmInstanceDao; + @Inject + NetworkDao _networkDao; @Override public boolean configure(String name, Map params) @@ -182,13 +193,13 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage from, to, networkId)); } if (!r.getResult()) { - tunnel.setState("FAILED"); - s_logger.warn("Create GRE tunnel failed due to " + r.getDetails() + tunnel.setState(OvsTunnel.State.Failed.name()); + s_logger.warn("Create GRE tunnel from " + from + " to " + to + " failed due to " + r.getDetails() + s); } else { - tunnel.setState("SUCCESS"); + tunnel.setState(OvsTunnel.State.Established.name()); tunnel.setPortName(r.getInPortName()); - s_logger.warn("Create GRE tunnel " + r.getDetails() + s); + s_logger.info("Create GRE tunnel from " + from + " to " + to + " succeeded." + r.getDetails() + s); } _tunnelNetworkDao.update(tunnel.getId(), tunnel); } @@ -249,13 +260,14 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage int key = 0; try { //The GRE key is actually in the host part of the URI - // this is not true for lswitch/NiciraNvp! String keyStr = BroadcastDomainType.getValue(network.getBroadcastUri()); - // The key is most certainly and int if network is a vlan. - // !! not in the case of lswitch/pvlan/(possibly)vswitch - // So we now feel quite safe in converting it into a string - // by calling the appropriate BroadcastDomainType method - key = Integer.parseInt(keyStr); + if (keyStr.contains(".")) { + String[] parts = keyStr.split("."); + key = Integer.parseInt(parts[1]); + } else { + key = Integer.parseInt(keyStr); + } + return key; } catch (NumberFormatException e) { s_logger.debug("Well well, how did '" + key @@ -268,41 +280,23 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } @DB - protected void checkAndCreateTunnel(VirtualMachine instance, Network nw, DeployDestination dest) { + protected void checkAndCreateTunnel(Network nw, Host host) { s_logger.debug("Creating tunnels with OVS tunnel manager"); - if (instance.getType() != VirtualMachine.Type.User - && instance.getType() != VirtualMachine.Type.DomainRouter) { - s_logger.debug("Will not work if you're not" - + "an instance or a virtual router"); - return; - } - long hostId = dest.getHost().getId(); + long hostId = host.getId(); int key = getGreKey(nw); - // Find active VMs with a NIC on the target network - List vms = _userVmDao.listByNetworkIdAndStates(nw.getId(), - State.Running, State.Starting, State.Stopping, State.Unknown, - State.Migrating); - // Find routers for the network - List routers = _routerDao.findByNetwork(nw.getId()); - List ins = new ArrayList(); - if (vms != null) { - ins.addAll(vms); - } - if (routers.size() != 0) { - ins.addAll(routers); - } + String bridgeName = generateBridgeName(nw, key); List toHostIds = new ArrayList(); List fromHostIds = new ArrayList(); - for (VMInstanceVO v : ins) { - Long rh = v.getHostId(); - if (rh == null || rh.longValue() == hostId) { + List networkSpannedHosts = _ovsNetworkToplogyGuru.getNetworkSpanedHosts(nw.getId()); + for (Long rh : networkSpannedHosts) { + if (rh == hostId) { continue; } OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(hostId, rh.longValue(), nw.getId()); // Try and create the tunnel even if a previous attempt failed - if (ta == null || ta.getState().equals("FAILED")) { + if (ta == null || ta.getState().equals(OvsTunnel.State.Failed.name())) { s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + rh.longValue()); if (ta == null) { createTunnelRecord(hostId, rh.longValue(), nw.getId(), key); @@ -315,7 +309,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage ta = _tunnelNetworkDao.getByFromToNetwork(rh.longValue(), hostId, nw.getId()); // Try and create the tunnel even if a previous attempt failed - if (ta == null || ta.getState().equals("FAILED")) { + if (ta == null || ta.getState().equals(OvsTunnel.State.Failed.name())) { s_logger.debug("Attempting to create tunnel from:" + rh.longValue() + " to:" + hostId); if (ta == null) { @@ -329,9 +323,9 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } //TODO: Should we propagate the exception here? try { - String myIp = getGreEndpointIP(dest.getHost(), nw); + String myIp = getGreEndpointIP(host, nw); if (myIp == null) - throw new GreTunnelException("Unable to retrieve the source " + "endpoint for the GRE tunnel." + "Failure is on host:" + dest.getHost().getId()); + throw new GreTunnelException("Unable to retrieve the source " + "endpoint for the GRE tunnel." + "Failure is on host:" + host.getId()); boolean noHost = true; for (Long i : toHostIds) { HostVO rHost = _hostDao.findById(i); @@ -343,7 +337,8 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage + "Failure is on host:" + rHost.getId()); Commands cmds = new Commands( new OvsCreateTunnelCommand(otherIp, key, - Long.valueOf(hostId), i, nw.getId(), myIp)); + Long.valueOf(hostId), i, nw.getId(), myIp, bridgeName, nw.getUuid())); + s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + i + " for the network " + nw.getId()); s_logger.debug("Ask host " + hostId + " to create gre tunnel to " + i); Answer[] answers = _agentMgr.send(hostId, cmds); @@ -355,21 +350,19 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage HostVO rHost = _hostDao.findById(i); String otherIp = getGreEndpointIP(rHost, nw); Commands cmds = new Commands(new OvsCreateTunnelCommand(myIp, - key, i, Long.valueOf(hostId), nw.getId(), otherIp)); + key, i, Long.valueOf(hostId), nw.getId(), otherIp, bridgeName, nw.getUuid())); s_logger.debug("Ask host " + i + " to create gre tunnel to " + hostId); Answer[] answers = _agentMgr.send(i, cmds); handleCreateTunnelAnswer(answers); noHost = false; } + // If no tunnels have been configured, perform the bridge setup - // anyway - // This will ensure VIF rules will be triggered + // anyway. This will ensure VIF rules will be triggered if (noHost) { - Commands cmds = new Commands(new OvsSetupBridgeCommand(key, - hostId, nw.getId())); - s_logger.debug("Ask host " + hostId - + " to configure bridge for network:" + nw.getId()); + Commands cmds = new Commands(new OvsSetupBridgeCommand(bridgeName, hostId, nw.getId())); + s_logger.debug("Ask host " + hostId + " to configure bridge for network:" + nw.getId()); Answer[] answers = _agentMgr.send(hostId, cmds); handleSetupBridgeAnswer(answers); } @@ -384,10 +377,20 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage return true; } + boolean isVpcEnabledForDistributedRouter(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + return vpc.usesDistributedRouter(); + } + @Override - public void vmCheckAndCreateTunnel(VirtualMachineProfile vm, - Network nw, DeployDestination dest) { - checkAndCreateTunnel(vm.getVirtualMachine(), nw, dest); + public void checkAndPrepareHostForTunnelNetwork(Network nw, Host host) { + if (nw.getVpcId() != null && isVpcEnabledForDistributedRouter(nw.getVpcId())) { + // check and setup host to be in full tunnel mesh with each of the network in the VPC + checkAndCreateVpcTunnelNetworks(host, nw.getVpcId()); + } else { + // check and setup host to be in full tunnel mesh with the network + checkAndCreateTunnel(nw, host); + } } @DB @@ -440,45 +443,28 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } @Override - public void checkAndDestroyTunnel(VirtualMachine vm, Network nw) { - // if (!_isEnabled) { - // return; - // } + public void checkAndRemoveHostFromTunnelNetwork(Network nw, Host host) { - List userVms = _userVmDao.listByAccountIdAndHostId(vm.getAccountId(), vm.getHostId()); - if (vm.getType() == VirtualMachine.Type.User) { - if (userVms.size() > 1) { - return; - } - - List routers = _routerDao.findByNetwork(nw.getId()); - for (DomainRouterVO router : routers) { - if (router.getHostId().equals(vm.getHostId())) { - return; - } - } - } else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) { - return; - } try { /* Now we are last one on host, destroy the bridge with all * the tunnels for this network */ int key = getGreKey(nw); - Command cmd = new OvsDestroyBridgeCommand(nw.getId(), key); - s_logger.debug("Destroying bridge for network " + nw.getId() + " on host:" + vm.getHostId()); - Answer ans = _agentMgr.send(vm.getHostId(), cmd); - handleDestroyBridgeAnswer(ans, vm.getHostId(), nw.getId()); + String bridgeName = generateBridgeName(nw, key); + Command cmd = new OvsDestroyBridgeCommand(nw.getId(), bridgeName); + s_logger.debug("Destroying bridge for network " + nw.getId() + " on host:" + host.getId()); + Answer ans = _agentMgr.send(host.getId(), cmd); + handleDestroyBridgeAnswer(ans, host.getId(), nw.getId()); /* Then ask hosts have peer tunnel with me to destroy them */ List peers = - _tunnelNetworkDao.listByToNetwork(vm.getHostId(), + _tunnelNetworkDao.listByToNetwork(host.getId(), nw.getId()); for (OvsTunnelNetworkVO p : peers) { // If the tunnel was not successfully created don't bother to remove it - if (p.getState().equals("SUCCESS")) { - cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), key, + if (p.getState().equals(OvsTunnel.State.Established.name())) { + cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), bridgeName, p.getPortName()); - s_logger.debug("Destroying tunnel to " + vm.getHostId() + + s_logger.debug("Destroying tunnel to " + host.getId() + " from " + p.getFrom()); ans = _agentMgr.send(p.getFrom(), cmd); handleDestroyTunnelAnswer(ans, p.getFrom(), @@ -486,8 +472,204 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } } } catch (Exception e) { - s_logger.warn(String.format("Destroy tunnel(account:%1$s," + "hostId:%2$s) failed", vm.getAccountId(), vm.getHostId()), e); + s_logger.warn(String.format("Destroy tunnel failed", e)); } } + private String generateBridgeName(Network nw, int key) { + if (nw.getVpcId() != null && isVpcEnabledForDistributedRouter(nw.getVpcId())) { + return "OVS-DR-VPC-Bridge" + nw.getVpcId(); + } else { + return "OVSTunnel"+key; + } + } + private String generateBridgeNameForVpc(long vpcId) { + return "OVS-DR-VPC-Bridge" + vpcId; + } + + public boolean sendVpcTopologyChangeUpdate(OvsVpcPhysicalTopologyConfigCommand updateCmd, long hostId, String bridgeName) { + try { + s_logger.debug("Sending VPC topology update to the host " + hostId); + updateCmd.setHostId(hostId); + updateCmd.setBridgeName(bridgeName); + Answer ans = _agentMgr.send(hostId, updateCmd); + if (ans.getResult()) { + s_logger.debug("Successfully updated the host " + hostId + " with latest VPC topology." ); + return true; + } else { + s_logger.debug("Failed to update the host " + hostId + " with latest VPC topology." ); + return false; + } + } catch (Exception e) { + s_logger.debug("Failed to updated the host " + hostId + " with latest VPC topology." ); + return false; + } + } + + OvsVpcPhysicalTopologyConfigCommand prepareVpcTopologyUpdate(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + assert (vpc != null): "invalid vpc id"; + + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + List hostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + List vmIds = _ovsNetworkToplogyGuru.getAllActiveVmsInVpc(vpcId); + + List hosts = new ArrayList(); + List tiers = new ArrayList(); + List vms = new ArrayList(); + + for (Long hostId : hostIds) { + HostVO hostDetails = _hostDao.findById(hostId); + String remoteIp = null; + for (Network network: vpcNetworks) { + try { + remoteIp = getGreEndpointIP(hostDetails, network); + } catch (Exception e) { + + } + } + OvsVpcPhysicalTopologyConfigCommand.Host host = new OvsVpcPhysicalTopologyConfigCommand.Host(hostId, remoteIp); + hosts.add(host); + } + + for (Network network: vpcNetworks) { + String key = BroadcastDomainType.getValue(network.getBroadcastUri()); + long gre_key; + if (key.contains(".")) { + String[] parts = key.split("."); + gre_key = Long.parseLong(parts[1]); + } else { + try { + gre_key = Long.parseLong(BroadcastDomainType.getValue(key)); + } catch (Exception e) { + return null; + } + } + NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId()); + OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(gre_key, + network.getUuid(), network.getGateway(), nic.getMacAddress(), network.getCidr()); + tiers.add(tier); + } + + for (long vmId: vmIds) { + VirtualMachine vmInstance = _vmInstanceDao.findById(vmId); + List vmNics = new ArrayList(); + for (Nic vmNic :_nicDao.listByVmId(vmId)) { + Network network = _networkDao.findById(vmNic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest) { + OvsVpcPhysicalTopologyConfigCommand.Nic nic = new OvsVpcPhysicalTopologyConfigCommand.Nic( + vmNic.getIp4Address(), vmNic.getMacAddress(), ((Long)vmNic.getNetworkId()).toString()); + vmNics.add(nic); + } + } + OvsVpcPhysicalTopologyConfigCommand.Vm vm = new OvsVpcPhysicalTopologyConfigCommand.Vm( + vmInstance.getHostId(), vmNics.toArray(new OvsVpcPhysicalTopologyConfigCommand.Nic[vmNics.size()])); + vms.add(vm); + } + return new OvsVpcPhysicalTopologyConfigCommand( + hosts.toArray(new OvsVpcPhysicalTopologyConfigCommand.Host[hosts.size()]), + tiers.toArray(new OvsVpcPhysicalTopologyConfigCommand.Tier[tiers.size()]), + vms.toArray(new OvsVpcPhysicalTopologyConfigCommand.Vm[vms.size()]), + vpc.getCidr()); + } + + @DB + protected void checkAndCreateVpcTunnelNetworks(Host host, long vpcId) { + + long hostId = host.getId(); + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + String bridgeName=generateBridgeNameForVpc(vpcId); + + for (Network vpcNetwork: vpcNetworks) { + int key = getGreKey(vpcNetwork); + List toHostIds = new ArrayList(); + List fromHostIds = new ArrayList(); + + for (Long rh : vpcSpannedHostIds) { + if (rh == hostId) { + continue; + } + OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(hostId, rh.longValue(), vpcNetwork.getId()); + // Try and create the tunnel even if a previous attempt failed + if (ta == null || ta.getState().equals(OvsTunnel.State.Failed.name())) { + s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + rh.longValue()); + if (ta == null) { + createTunnelRecord(hostId, rh.longValue(), vpcNetwork.getId(), key); + } + if (!toHostIds.contains(rh)) { + toHostIds.add(rh); + } + } + + ta = _tunnelNetworkDao.getByFromToNetwork(rh.longValue(), + hostId, vpcNetwork.getId()); + // Try and create the tunnel even if a previous attempt failed + if (ta == null || ta.getState().equals(OvsTunnel.State.Failed.name())) { + s_logger.debug("Attempting to create tunnel from:" + + rh.longValue() + " to:" + hostId); + if (ta == null) { + createTunnelRecord(rh.longValue(), hostId, + vpcNetwork.getId(), key); + } + if (!fromHostIds.contains(rh)) { + fromHostIds.add(rh); + } + } + } + + try { + String myIp = getGreEndpointIP(host, vpcNetwork); + if (myIp == null) + throw new GreTunnelException("Unable to retrieve the source " + "endpoint for the GRE tunnel." + + "Failure is on host:" + host.getId()); + boolean noHost = true; + + for (Long i : toHostIds) { + HostVO rHost = _hostDao.findById(i); + String otherIp = getGreEndpointIP(rHost, vpcNetwork); + if (otherIp == null) + throw new GreTunnelException( + "Unable to retrieve the remote " + + "endpoint for the GRE tunnel." + + "Failure is on host:" + rHost.getId()); + Commands cmds = new Commands( + new OvsCreateTunnelCommand(otherIp, key, + Long.valueOf(hostId), i, vpcNetwork.getId(), myIp, bridgeName, + vpcNetwork.getUuid())); + s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + i + " for the network " + + vpcNetwork.getId()); + s_logger.debug("Ask host " + hostId + + " to create gre tunnel to " + i); + Answer[] answers = _agentMgr.send(hostId, cmds); + handleCreateTunnelAnswer(answers); + noHost = false; + } + + for (Long i : fromHostIds) { + HostVO rHost = _hostDao.findById(i); + String otherIp = getGreEndpointIP(rHost, vpcNetwork); + Commands cmds = new Commands(new OvsCreateTunnelCommand(myIp, + key, i, Long.valueOf(hostId), vpcNetwork.getId(), otherIp, bridgeName, + vpcNetwork.getUuid())); + s_logger.debug("Ask host " + i + " to create gre tunnel to " + + hostId); + Answer[] answers = _agentMgr.send(i, cmds); + handleCreateTunnelAnswer(answers); + noHost = false; + } + } catch (GreTunnelException | OperationTimedoutException | AgentUnavailableException e) { + // I really thing we should do a better handling of these exceptions + s_logger.warn("Ovs Tunnel network created tunnel failed", e); + } + } + + OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); + for (Long id: vpcSpannedHostIds) { + if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { + s_logger.debug("Failed to send VPC topology change update to host : " + id + ". Moving on with rest of" + + "the host update."); + } + } + } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnel.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnel.java new file mode 100644 index 00000000000..3216ac74214 --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnel.java @@ -0,0 +1,24 @@ +// 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.ovs.dao; + +public interface OvsTunnel { + public enum State { + Created, Established, Failed + } +} diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnelNetworkVO.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnelNetworkVO.java index 2826912d229..88a75913086 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnelNetworkVO.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/OvsTunnelNetworkVO.java @@ -62,7 +62,7 @@ public class OvsTunnelNetworkVO implements InternalIdentity { this.key = key; this.networkId = networkId; this.portName = "[]"; - this.state = "FAILED"; + this.state = OvsTunnel.State.Created.name(); } public void setKey(int key) { diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel index d558e972cda..64a2d362ccf 100755 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -224,6 +224,7 @@ def create_tunnel(session, args): gre_key = args.pop("key") src_host = args.pop("from") dst_host = args.pop("to") + network_uuid = args.pop("cloudstack-network-id") logging.debug("Entering create_tunnel") @@ -318,6 +319,7 @@ def create_tunnel(session, args): # add flow rule to send the traffic from tunnel ports to L2 switching table only lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, actions='resubmit(,1)') + lib.do_cmd([lib.VSCTL_PATH, "set", "interface", name, "options:cloudstack-network-id=%s" % network_uuid]) return "SUCCESS:%s" % name except: diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index 4af20c6f00d..b643ec41faf 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.hypervisor; +import com.cloud.network.dao.NetworkVO; import java.util.List; import java.util.Map; @@ -41,6 +42,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; +import com.cloud.network.dao.NetworkDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; @@ -52,6 +54,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis @Inject NicDao _nicDao; @Inject + NetworkDao _networkDao; + @Inject VMInstanceDao _virtualMachineDao; @Inject UserVmDetailsDao _userVmDetailsDao; @@ -87,6 +91,9 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis to.setName(profile.getName()); to.setSecurityGroupEnabled(profile.isSecurityGroupEnabled()); + NetworkVO network = _networkDao.findById(profile.getNetworkId()); + to.setNetworkUuid(network.getUuid()); + // Workaround to make sure the TO has the UUID we need for Niciri integration NicVO nicVO = _nicDao.findById(profile.getId()); to.setUuid(nicVO.getUuid()); From 2c7786992f1900daca1ef8c093ceca6e7b735050 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Mon, 10 Mar 2014 22:16:46 +0530 Subject: [PATCH 13/31] some bug fixes --- .../xen/resource/CitrixResourceBase.java | 14 +- .../OvsVpcLogicalTopologyConfigCommand.java | 33 +++++ .../OvsVpcPhysicalTopologyConfigCommand.java | 7 +- .../com/cloud/network/element/OvsElement.java | 18 --- .../network/guru/OvsGuestNetworkGuru.java | 2 +- .../network/ovs/OvsNetworkTopologyGuru.java | 10 ++ .../ovs/OvsNetworkTopologyGuruImpl.java | 126 ++++++++++++++++- .../network/ovs/OvsTunnelManagerImpl.java | 127 +++++++++++++----- .../xenserver/cloudstack_pluginlib.py | 4 +- scripts/vm/hypervisor/xenserver/ovstunnel | 14 +- .../guru/ExternalGuestNetworkGuru.java | 4 + 11 files changed, 280 insertions(+), 79 deletions(-) create mode 100644 plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 3e7dfafd903..87529210f8d 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -999,7 +999,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe /** * This method creates a XenServer network and configures it for being used as a L2-in-L3 tunneled network */ - private synchronized Network configureTunnelNetwork(Connection conn, long networkId, long hostId, String bridgeName) { + private synchronized Network configureTunnelNetwork(Connection conn, Long networkId, long hostId, String bridgeName) { try { Network nw = findOrCreateTunnelNetwork(conn, bridgeName); String nwName = bridgeName; @@ -1038,7 +1038,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe String[] res = result.split(":"); if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) { //TODO: Should make this error not fatal? - throw new CloudRuntimeException("Unable to pre-configure OVS bridge " + bridge + " for network ID:" + networkId + " - " + res); + throw new CloudRuntimeException("Unable to pre-configure OVS bridge " + bridge ); } } return nw; @@ -1090,7 +1090,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe _isOvs = true; return setupvSwitchNetwork(conn); } else { - return findOrCreateTunnelNetwork(conn, getOvsTunnelNetworkName(BroadcastDomainType.getValue(uri))); + return findOrCreateTunnelNetwork(conn, getOvsTunnelNetworkName(uri.getAuthority())); } } else if (type == BroadcastDomainType.Storage) { if (uri == null) { @@ -1114,7 +1114,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe private String getOvsTunnelNetworkName(String broadcastUri) { if (broadcastUri.contains(".")) { - String[] parts = broadcastUri.split("."); + String[] parts = broadcastUri.split("\\."); return "OVS-DR-VPC-Bridge"+parts[0]; } else { try { @@ -5266,7 +5266,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } String bridge = nw.getBridge(conn); - String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", bridge, "in_port", cmd.getInPortName()); + String result = callHostPlugin(conn, "ovstunnel", "destroy_tunnel", "bridge", bridge, "in_port", cmd.getInPortName()); + if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, result); } else { @@ -5282,7 +5283,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); try { String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", - cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString()); + cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString(), "config", + cmd.getjsonVpcConfig()); if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, result); } else { diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java new file mode 100644 index 00000000000..2fafb6e18c6 --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java @@ -0,0 +1,33 @@ +// 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.agent.api; + +/** + * This command represents view of how a VPC is laid out (on which hosts, which VM is on which host etc) + * on the physical infrastructure. + */ +public class OvsVpcLogicalTopologyConfigCommand extends Command { + + public OvsVpcLogicalTopologyConfigCommand() { + + } + + @Override + public boolean executeInSequence() { + return false; + } +} \ No newline at end of file diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java index 35d4c6e7050..e6f4383ce8f 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java @@ -20,8 +20,11 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; /** - * This command represents view of how a VPC is laid out (on which hosts, which VM is on which host etc) - * on the physical infrastructure. + * This command represents physical view of how a VPC is laid out on the physical infrastructure. + * - on which hypervisor hosts VPC spans (host is running in at least one VM from the VPC) + * - information of tiers, so we can figure how one VM can talk to a different VM in same tier or different tier + * - information on all the VM's in the VPC. + * - information of NIC's of each VM in the VPC */ public class OvsVpcPhysicalTopologyConfigCommand extends Command { diff --git a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java index c28d90808ed..036c3194d57 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java @@ -75,7 +75,6 @@ import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; -import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; @Local(value = {NetworkElement.class, ConnectivityProvider.class, @@ -205,23 +204,6 @@ StaticNatServiceProvider, IpDeployer { return false; } - List userVms = _userVmDao.listByAccountIdAndHostId(vm.getVirtualMachine().getAccountId(), - vm.getVirtualMachine().getHostId()); - if (vm.getType() == VirtualMachine.Type.User) { - if (userVms.size() > 1) { - return true; - } - - List routers = _routerDao.findByNetwork(network.getId()); - for (DomainRouterVO router : routers) { - if (router.getHostId().equals(vm.getVirtualMachine().getHostId())) { - return true; - } - } - } else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) { - return true; - } - HostVO host = _hostDao.findById(vm.getVirtualMachine().getHostId()); _ovsTunnelMgr.checkAndRemoveHostFromTunnelNetwork(network, host); return true; diff --git a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java index 2814c2a0837..9d2efe65bd6 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java @@ -154,7 +154,7 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { String keyStr = BroadcastDomainType.getValue(implemented.getBroadcastUri()); Long vpcid= network.getVpcId(); - implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(vpcid.toString()+keyStr)); + implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(vpcid.toString() + "." + keyStr)); } return implemented; diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java index c410d10c22d..122175caffc 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java @@ -37,6 +37,11 @@ public interface OvsNetworkTopologyGuru extends Manager { */ public List getVpcOnHost(long hostId); + /** + * get the list of all active Vm id's in a network + */ + public List getAllActiveVmsInNetwork(long networkId); + /** * get the list of all active Vm id's in the VPC for all ther tiers */ @@ -46,4 +51,9 @@ public interface OvsNetworkTopologyGuru extends Manager { * get the list of all Vm id's in the VPC for all the tiers that are running on the host */ public List getActiveVmsInVpcOnHost(long vpcId, long hostId); + + /** + * get the list of all Vm id's in the network that are running on the host + */ + public List getActiveVmsInNetworkOnHost(long vpcId, long hostId); } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java index 7560e35c207..7715641872f 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java @@ -1,14 +1,24 @@ package com.cloud.network.ovs; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.VpcManager; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.NicVO; +import com.cloud.vm.Nic; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; import org.springframework.stereotype.Component; @@ -21,6 +31,14 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor UserVmDao _userVmDao; @Inject DomainRouterDao _routerDao; + @Inject + VpcManager _vpcMgr; + @Inject + VMInstanceDao _vmInstanceDao; + @Inject + NicDao _nicDao; + @Inject + NetworkDao _networkDao; /** * get the list of hypervisor hosts on which VM's belonging to a network currently spans @@ -52,23 +70,121 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return hostIds; } + /** + * get the list of hypervisor hosts on which VM's belonging to a VPC currently spans + */ @Override - public List getVpcSpannedHosts(long vpId) { - return null; + public List getVpcSpannedHosts(long vpcId) { + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + List vpcHostIds = new ArrayList<>(); + for (Network vpcNetwork : vpcNetworks) { + List networkHostIds = new ArrayList(); + networkHostIds = getNetworkSpanedHosts(vpcNetwork.getId()); + if (networkHostIds != null && !networkHostIds.isEmpty()) { + for (Long hostId : networkHostIds) { + if (!vpcHostIds.contains(hostId)) { + vpcHostIds.add(hostId); + } + } + } + } + return vpcHostIds; } @Override public List getVpcOnHost(long hostId) { - return null; + List vpcIds = new ArrayList<>(); + List vmInstances = _vmInstanceDao.listByHostId(hostId); + for (VMInstanceVO instance : vmInstances) { + List nics = _nicDao.listByVmId(instance.getId()); + for (Nic nic: nics) { + Network network = _networkDao.findById(nic.getNetworkId()); + if (network.getTrafficType() == Networks.TrafficType.Guest && network.getVpcId() != null) { + if (!vpcIds.contains(network.getVpcId())) { + vpcIds.add(network.getVpcId()); + } + } + } + } + return vpcIds; + } + + @Override + public List getAllActiveVmsInNetwork(long networkId) { + List vmIds = new ArrayList<>(); + List vms = _userVmDao.listByNetworkIdAndStates(networkId, + VirtualMachine.State.Running, VirtualMachine.State.Starting, VirtualMachine.State.Stopping, VirtualMachine.State.Unknown, + VirtualMachine.State.Migrating); + // Find routers for the network + List routers = _routerDao.findByNetwork(networkId); + List ins = new ArrayList(); + + if (vms != null) { + for (UserVmVO vm : vms) { + vmIds.add(vm.getId()); + } + } + if (routers.size() != 0) { + for (DomainRouterVO router: routers) { + vmIds.add(router.getId()); + } + } + return vmIds; } @Override public List getAllActiveVmsInVpc(long vpcId) { - return null; + + Set vmIdsSet = new HashSet<>(); + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + for (Network network : vpcNetworks) { + List networkVmIds = getAllActiveVmsInNetwork(network.getId()); + if (networkVmIds != null && !networkVmIds.isEmpty()) { + vmIdsSet.addAll(networkVmIds); + } + } + List vmIds = new ArrayList<>(); + vmIds.addAll(vmIdsSet); + return vmIds; } @Override public List getActiveVmsInVpcOnHost(long vpcId, long hostId) { - return null; + Set vmIdsSet = new HashSet<>(); + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + for (Network network : vpcNetworks) { + List networkVmIds = getActiveVmsInNetworkOnHost(network.getId(), hostId); + if (networkVmIds != null && !networkVmIds.isEmpty()) { + vmIdsSet.addAll(networkVmIds); + } + } + List vmIds = new ArrayList<>(); + vmIds.addAll(vmIdsSet); + return vmIds; + } + + @Override + public List getActiveVmsInNetworkOnHost(long networkId, long hostId) { + List vmIds = new ArrayList<>(); + List vms = _userVmDao.listByNetworkIdAndStates(networkId, + VirtualMachine.State.Running, VirtualMachine.State.Starting, VirtualMachine.State.Stopping, VirtualMachine.State.Unknown, + VirtualMachine.State.Migrating); + // Find routers for the network + List routers = _routerDao.findByNetwork(networkId); + List ins = new ArrayList(); + + if (vms != null) { + for (UserVmVO vm : vms) { + if (vm.getHostId() == hostId) + vmIds.add(vm.getId()); + } + } + if (routers.size() != 0) { + for (DomainRouterVO router: routers) { + if (router.getHostId() == hostId) + vmIds.add(router.getId()); + } + } + return vmIds; } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index ae37095360f..82dbfed2fa1 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -18,10 +18,10 @@ package com.cloud.network.ovs; import com.cloud.network.dao.NetworkDao; import com.cloud.network.vpc.VpcManager; +import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.Nic; import com.cloud.vm.NicVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.dao.VMInstanceDao; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -260,9 +260,9 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage int key = 0; try { //The GRE key is actually in the host part of the URI - String keyStr = BroadcastDomainType.getValue(network.getBroadcastUri()); + String keyStr = network.getBroadcastUri().getAuthority(); if (keyStr.contains(".")) { - String[] parts = keyStr.split("."); + String[] parts = keyStr.split("\\."); key = Integer.parseInt(parts[1]); } else { key = Integer.parseInt(keyStr); @@ -445,34 +445,72 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage @Override public void checkAndRemoveHostFromTunnelNetwork(Network nw, Host host) { - try { - /* Now we are last one on host, destroy the bridge with all - * the tunnels for this network */ - int key = getGreKey(nw); - String bridgeName = generateBridgeName(nw, key); - Command cmd = new OvsDestroyBridgeCommand(nw.getId(), bridgeName); - s_logger.debug("Destroying bridge for network " + nw.getId() + " on host:" + host.getId()); - Answer ans = _agentMgr.send(host.getId(), cmd); - handleDestroyBridgeAnswer(ans, host.getId(), nw.getId()); - - /* Then ask hosts have peer tunnel with me to destroy them */ - List peers = - _tunnelNetworkDao.listByToNetwork(host.getId(), - nw.getId()); - for (OvsTunnelNetworkVO p : peers) { - // If the tunnel was not successfully created don't bother to remove it - if (p.getState().equals(OvsTunnel.State.Established.name())) { - cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), bridgeName, - p.getPortName()); - s_logger.debug("Destroying tunnel to " + host.getId() + - " from " + p.getFrom()); - ans = _agentMgr.send(p.getFrom(), cmd); - handleDestroyTunnelAnswer(ans, p.getFrom(), - p.getTo(), p.getNetworkId()); - } + if (nw.getVpcId() != null && isVpcEnabledForDistributedRouter(nw.getVpcId())) { + List vmIds = _ovsNetworkToplogyGuru.getActiveVmsInVpcOnHost(nw.getVpcId(), host.getId()); + if (vmIds != null && !vmIds.isEmpty()) { + return; + } + List vpcNetworks = _vpcMgr.getVpcNetworks(nw.getVpcId()); + try { + for (Network network: vpcNetworks) { + int key = getGreKey(nw); + String bridgeName = generateBridgeName(nw, key); + /* Then ask hosts have peer tunnel with me to destroy them */ + List peers = _tunnelNetworkDao.listByToNetwork(host.getId(),nw.getId()); + for (OvsTunnelNetworkVO p : peers) { + // If the tunnel was not successfully created don't bother to remove it + if (p.getState().equals(OvsTunnel.State.Established.name())) { + Command cmd= new OvsDestroyTunnelCommand(p.getNetworkId(), bridgeName, + p.getPortName()); + s_logger.debug("Destroying tunnel to " + host.getId() + + " from " + p.getFrom()); + Answer ans = _agentMgr.send(p.getFrom(), cmd); + handleDestroyTunnelAnswer(ans, p.getFrom(), p.getTo(), p.getNetworkId()); + } + } + } + + Command cmd = new OvsDestroyBridgeCommand(nw.getId(), generateBridgeNameForVpc(nw.getVpcId())); + s_logger.debug("Destroying bridge for network " + nw.getId() + " on host:" + host.getId()); + Answer ans = _agentMgr.send(host.getId(), cmd); + handleDestroyBridgeAnswer(ans, host.getId(), nw.getId()); + } catch (Exception e) { + + } + } else { + List vmIds = _ovsNetworkToplogyGuru.getActiveVmsInNetworkOnHost(nw.getId(), host.getId()); + if (vmIds != null && !vmIds.isEmpty()) { + return; + } + try { + /* Now we are last one on host, destroy the bridge with all + * the tunnels for this network */ + int key = getGreKey(nw); + String bridgeName = generateBridgeName(nw, key); + Command cmd = new OvsDestroyBridgeCommand(nw.getId(), bridgeName); + s_logger.debug("Destroying bridge for network " + nw.getId() + " on host:" + host.getId()); + Answer ans = _agentMgr.send(host.getId(), cmd); + handleDestroyBridgeAnswer(ans, host.getId(), nw.getId()); + + /* Then ask hosts have peer tunnel with me to destroy them */ + List peers = + _tunnelNetworkDao.listByToNetwork(host.getId(), + nw.getId()); + for (OvsTunnelNetworkVO p : peers) { + // If the tunnel was not successfully created don't bother to remove it + if (p.getState().equals(OvsTunnel.State.Established.name())) { + cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), bridgeName, + p.getPortName()); + s_logger.debug("Destroying tunnel to " + host.getId() + + " from " + p.getFrom()); + ans = _agentMgr.send(p.getFrom(), cmd); + handleDestroyTunnelAnswer(ans, p.getFrom(), + p.getTo(), p.getNetworkId()); + } + } + } catch (Exception e) { + s_logger.warn(String.format("Destroy tunnel failed", e)); } - } catch (Exception e) { - s_logger.warn(String.format("Destroy tunnel failed", e)); } } @@ -514,9 +552,9 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage List hostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); List vmIds = _ovsNetworkToplogyGuru.getAllActiveVmsInVpc(vpcId); - List hosts = new ArrayList(); - List tiers = new ArrayList(); - List vms = new ArrayList(); + List hosts = new ArrayList<>(); + List tiers = new ArrayList<>(); + List vms = new ArrayList<>(); for (Long hostId : hostIds) { HostVO hostDetails = _hostDao.findById(hostId); @@ -533,10 +571,10 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } for (Network network: vpcNetworks) { - String key = BroadcastDomainType.getValue(network.getBroadcastUri()); + String key = network.getBroadcastUri().getAuthority(); long gre_key; if (key.contains(".")) { - String[] parts = key.split("."); + String[] parts = key.split("\\."); gre_key = Long.parseLong(parts[1]); } else { try { @@ -580,6 +618,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); String bridgeName=generateBridgeNameForVpc(vpcId); + boolean bridgeNotSetup = true; for (Network vpcNetwork: vpcNetworks) { int key = getGreKey(vpcNetwork); @@ -643,7 +682,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage + " to create gre tunnel to " + i); Answer[] answers = _agentMgr.send(hostId, cmds); handleCreateTunnelAnswer(answers); - noHost = false; + bridgeNotSetup = false; } for (Long i : fromHostIds) { @@ -656,7 +695,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage + hostId); Answer[] answers = _agentMgr.send(i, cmds); handleCreateTunnelAnswer(answers); - noHost = false; + bridgeNotSetup = false; } } catch (GreTunnelException | OperationTimedoutException | AgentUnavailableException e) { // I really thing we should do a better handling of these exceptions @@ -664,6 +703,20 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } } + // If no tunnels have been configured, perform the bridge setup + // anyway. This will ensure VIF rules will be triggered + if (bridgeNotSetup) { + try { + Commands cmds = new Commands(new OvsSetupBridgeCommand(bridgeName, hostId, null)); + s_logger.debug("Ask host " + hostId + " to configure bridge for vpc"); + Answer[] answers = _agentMgr.send(hostId, cmds); + handleSetupBridgeAnswer(answers); + } catch (OperationTimedoutException | AgentUnavailableException e) { + // I really thing we should do a better handling of these exceptions + s_logger.warn("Ovs Tunnel network created tunnel failed", e); + } + } + OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); for (Long id: vpcSpannedHostIds) { if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index d2b95dc8930..dbcc2886a5d 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -21,7 +21,7 @@ import ConfigParser import logging import os import subprocess -import json +import simplejson as json from time import localtime, asctime @@ -181,7 +181,7 @@ def _build_flow_expr(**kwargs): proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or '' ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or '' flow = (flow + in_port + dl_type + dl_src + dl_dst + - (ip or proto) + nw_src + nw_dst) + (ip or proto) + nw_src + nw_dst + table) return flow diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel index 64a2d362ccf..9ef0f7ba4c8 100755 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -137,8 +137,8 @@ def setup_ovs_bridge_for_distributed_routing(session, args): logging.debug("About to manually create the bridge:%s" % bridge) # create a bridge with the same name as the xapi network - res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge, - "--", "set", "bridge", bridge]) + res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge]) + logging.debug("Bridge has been manually created:%s" % res) # TODO: Make sure xs-network-uuid is set into external_ids lib.do_cmd([lib.VSCTL_PATH, "set", "Bridge", bridge, @@ -149,12 +149,8 @@ def setup_ovs_bridge_for_distributed_routing(session, args): result = "FAILURE:%s" % res else: # Verify the bridge actually exists, with the gre_key properly set - res = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", - bridge, "other_config:gre_key"]) - if key in res: - result = "SUCCESS:%s" % bridge - else: - result = "FAILURE:%s" % res + res = lib.do_cmd([lib.VSCTL_PATH, "list", "bridge", bridge]) + # Finally note in the xenapi network object that the network has # been configured xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list", @@ -191,6 +187,8 @@ def setup_ovs_bridge_for_distributed_routing(session, args): # add a default rule in egress table to forward packet to L3 lookup table lib.add_flow(bridge, priority=0, table=5, actions='drop') + result = "SUCCESS: successfully setup bridge with flow rules" + logging.debug("Setup_ovs_bridge completed with result:%s" % result) return result diff --git a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java index 414eb7b6543..13246a74a5d 100644 --- a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -107,6 +107,10 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { @Override public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + if (_networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.Connectivity)) { + return null; + } + NetworkVO config = (NetworkVO)super.design(offering, plan, userSpecified, owner); if (config == null) { return null; From 423a7488075398524d513ac2b22654b393b63a66 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 11 Mar 2014 04:07:13 +0530 Subject: [PATCH 14/31] adds hypervisor script to convert JSON routing polcies (ACL) config in to flow rules and applies them on the bridge add event subscriber in OvsTunnelManager, that listens to replaceNetworkAcl events. On event sends the updated policy info to all the hosts in the VPC --- .../xen/resource/CitrixResourceBase.java | 24 +++- .../OvsVpcLogicalTopologyConfigCommand.java | 33 ----- .../OvsVpcPhysicalTopologyConfigCommand.java | 6 +- .../api/OvsVpcRoutingPolicyConfigCommand.java | 124 ++++++++++++++++++ .../network/ovs/OvsTunnelManagerImpl.java | 104 +++++++++++++-- .../xenserver/cloudstack_pluginlib.py | 76 ++++++++++- scripts/vm/hypervisor/xenserver/ovstunnel | 14 +- .../network/vpc/NetworkACLManagerImpl.java | 12 +- 8 files changed, 336 insertions(+), 57 deletions(-) delete mode 100644 plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java create mode 100644 plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 87529210f8d..a281002c02a 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.hypervisor.xen.resource; - import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -89,6 +88,7 @@ import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import com.cloud.agent.IAgentControl; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachVolumeAnswer; @@ -510,6 +510,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((OvsDeleteFlowCommand)cmd); } else if (clazz == OvsVpcPhysicalTopologyConfigCommand.class) { return execute((OvsVpcPhysicalTopologyConfigCommand) cmd); + } else if (clazz == OvsVpcRoutingPolicyConfigCommand.class) { + return execute((OvsVpcRoutingPolicyConfigCommand) cmd); } else if (clazz == CleanupNetworkRulesCmd.class) { return execute((CleanupNetworkRulesCmd)cmd); } else if (clazz == NetworkRulesSystemVmCommand.class) { @@ -533,7 +535,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } else if (clazz == PlugNicCommand.class) { return execute((PlugNicCommand)cmd); } else if (clazz == UnPlugNicCommand.class) { - return execute((UnPlugNicCommand)cmd); + return execute((UnPlugNicCommand) cmd); } else if (cmd instanceof StorageSubSystemCommand) { return storageHandler.handleStorageCommands((StorageSubSystemCommand) cmd); } else if (clazz == CreateVMSnapshotCommand.class) { @@ -5283,8 +5285,24 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); try { String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", + cmd.getBridgeName(), "config", cmd.getVpcConfigInJson()); + if (result.equalsIgnoreCase("SUCCESS")) { + return new Answer(cmd, true, result); + } else { + return new Answer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught exception while updating host with latest routing polcies", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + public Answer execute(OvsVpcRoutingPolicyConfigCommand cmd) { + Connection conn = getConnection(); + try { + String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_routing_policies", "bridge", cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString(), "config", - cmd.getjsonVpcConfig()); + cmd.getVpcConfigInJson()); if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, result); } else { diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java deleted file mode 100644 index 2fafb6e18c6..00000000000 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcLogicalTopologyConfigCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -// 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.agent.api; - -/** - * This command represents view of how a VPC is laid out (on which hosts, which VM is on which host etc) - * on the physical infrastructure. - */ -public class OvsVpcLogicalTopologyConfigCommand extends Command { - - public OvsVpcLogicalTopologyConfigCommand() { - - } - - @Override - public boolean executeInSequence() { - return false; - } -} \ No newline at end of file diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java index e6f4383ce8f..17299d3f842 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcPhysicalTopologyConfigCommand.java @@ -20,8 +20,8 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; /** - * This command represents physical view of how a VPC is laid out on the physical infrastructure. - * - on which hypervisor hosts VPC spans (host is running in at least one VM from the VPC) + * This command represents physical view of how a VPC is laid out on the physical infrastructure. Contains information: + * - on which hypervisor hosts VPC spans (host running at least one VM from the VPC) * - information of tiers, so we can figure how one VM can talk to a different VM in same tier or different tier * - information on all the VM's in the VPC. * - information of NIC's of each VM in the VPC @@ -102,7 +102,7 @@ public class OvsVpcPhysicalTopologyConfigCommand extends Command { vpcConfig = new VpcConfig(vpc); } - public String getjsonVpcConfig() { + public String getVpcConfigInJson() { Gson gson = new GsonBuilder().create(); return gson.toJson(vpcConfig).toLowerCase(); } diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java new file mode 100644 index 00000000000..8e4d5d1e2b1 --- /dev/null +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java @@ -0,0 +1,124 @@ +// 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.agent.api; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.UUID; + +/** + * This command represents logical view of VM's connectivity in VPC. + */ +public class OvsVpcRoutingPolicyConfigCommand extends Command { + + VpcConfig vpcConfig =null; + long hostId; + String bridgeName; + + public static class AclItem { + int number; + String uuid; + String action; + String direction; + String sourcePortStart; + String sourcePortEnd; + String protocol; + String[] sourceCidrs; + public AclItem(int number, String uuid, String action, String direction, String sourcePortStart, + String sourcePortEnd, String protocol, String[] sourceCidrs) { + this.number = number; + this.uuid =uuid; + this.action = action; + this.direction = direction; + this.sourceCidrs = sourceCidrs; + this.sourcePortStart = sourcePortStart; + this.sourcePortEnd = sourcePortEnd; + this.protocol = protocol; + } + } + + public static class Acl { + String id; + AclItem[] aclItems; + public Acl(String uuid, AclItem[] aclItems) { + this.id = uuid; + this.aclItems = aclItems; + } + } + + public static class Tier { + String id; + String cidr; + String aclId; + public Tier(String uuid, String cidr, String aclId) { + this.id = uuid; + this.cidr = cidr; + this.aclId = aclId; + } + } + + public class Vpc { + String cidr; + String id; + Acl[] acls; + Tier[] tiers; + public Vpc(String id, String cidr, Acl[] acls, Tier[] tiers) { + this.id = id; + this.cidr = cidr; + this.acls = acls; + this.tiers = tiers; + } + } + + public static class VpcConfig { + Vpc vpc; + public VpcConfig(Vpc vpc) { + this.vpc = vpc; + } + } + + public OvsVpcRoutingPolicyConfigCommand(String id, String cidr, Acl[] acls, Tier[] tiers) { + Vpc vpc = new Vpc(id, cidr, acls, tiers); + vpcConfig = new VpcConfig(vpc); + } + + public String getVpcConfigInJson() { + Gson gson = new GsonBuilder().create(); + return gson.toJson(vpcConfig).toLowerCase(); + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getHostId() { + return hostId; + } + + public String getBridgeName() { + return bridgeName; + } + + public void setBridgeName(String bridgeName) { + this.bridgeName = bridgeName; + } + + @Override + public boolean executeInSequence() { + return false; + } +} \ No newline at end of file diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 82dbfed2fa1..35b00355533 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -16,8 +16,12 @@ // under the License. package com.cloud.network.ovs; +import com.amazonaws.services.ec2.model.NetworkAcl; +import com.cloud.agent.api.*; import com.cloud.network.dao.NetworkDao; -import com.cloud.network.vpc.VpcManager; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.*; +import com.cloud.network.vpc.dao.NetworkACLDao; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.Nic; import com.cloud.vm.NicVO; @@ -33,22 +37,14 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.MessageSubscriber; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.agent.AgentManager; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.OvsCreateTunnelAnswer; -import com.cloud.agent.api.OvsCreateTunnelCommand; -import com.cloud.agent.api.OvsDestroyBridgeCommand; -import com.cloud.agent.api.OvsDestroyTunnelCommand; -import com.cloud.agent.api.OvsFetchInterfaceAnswer; -import com.cloud.agent.api.OvsFetchInterfaceCommand; -import com.cloud.agent.api.OvsSetupBridgeCommand; -import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; import com.cloud.agent.manager.Commands; import com.cloud.configuration.Config; import com.cloud.exception.AgentUnavailableException; @@ -68,7 +64,6 @@ import com.cloud.network.ovs.dao.OvsTunnelNetworkDao; import com.cloud.network.ovs.dao.OvsTunnelNetworkVO; import com.cloud.network.ovs.dao.OvsTunnel; import com.cloud.network.vpc.dao.VpcDao; -import com.cloud.network.vpc.VpcVO; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -112,6 +107,12 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage protected VMInstanceDao _vmInstanceDao; @Inject NetworkDao _networkDao; + @Inject + MessageBus _messageBus; + @Inject + NetworkACLDao _networkACLDao; + @Inject + NetworkACLItemDao _networkACLItemDao; @Override public boolean configure(String name, Map params) @@ -119,6 +120,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); + _messageBus.subscribe("Network_ACL_Replaced", new NetworkAclEventsSubscriber()); return true; } @@ -725,4 +727,82 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } } } + + // Subscriber to ACL replace events. On acl replace event, if the vpc is enabled for distributed routing + // send the ACL update to all the hosts on which VPC spans + public class NetworkAclEventsSubscriber implements MessageSubscriber { + @Override + public void onPublishMessage(String senderAddress, String subject, Object args) { + NetworkVO network = (NetworkVO) args; + String bridgeName=generateBridgeNameForVpc(network.getVpcId()); + if (network.getVpcId() != null & isVpcEnabledForDistributedRouter(network.getVpcId())) { + long vpcId = network.getVpcId(); + OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); + List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + for (Long id: vpcSpannedHostIds) { + if (!sendVpcRoutingPolicyChangeUpdate(cmd, id, bridgeName)) { + s_logger.debug("Failed to send VPC routing policy change update to host : " + id + + ". Moving on with rest of the host updates."); + } + } + } + } + } + + OvsVpcRoutingPolicyConfigCommand prepareVpcRoutingPolicyUpdate(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + assert (vpc != null): "invalid vpc id"; + List acls = new ArrayList<>(); + List tiers = new ArrayList<>(); + + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + for (Network network : vpcNetworks) { + Long networkAclId = network.getNetworkACLId(); + NetworkACLVO networkAcl = _networkACLDao.findById(networkAclId); + + List aclItems = new ArrayList<>(); + List aclItemVos = _networkACLItemDao.listByACL(networkAclId); + for (NetworkACLItemVO aclItem : aclItemVos) { + String[] sourceCidrs = aclItem.getSourceCidrList().toArray(new String[aclItem.getSourceCidrList().size()]); + aclItems.add(new OvsVpcRoutingPolicyConfigCommand.AclItem( + aclItem.getNumber(), aclItem.getUuid(), aclItem.getAction().name(), + aclItem.getTrafficType().name(), + aclItem.getSourcePortStart().toString(), aclItem.getSourcePortEnd().toString(), + aclItem.getProtocol(), sourceCidrs)); + } + + OvsVpcRoutingPolicyConfigCommand.Acl acl = new OvsVpcRoutingPolicyConfigCommand.Acl(networkAcl.getUuid(), + aclItems.toArray(new OvsVpcRoutingPolicyConfigCommand.AclItem[aclItems.size()])); + acls.add(acl); + + OvsVpcRoutingPolicyConfigCommand.Tier tier = new OvsVpcRoutingPolicyConfigCommand.Tier(network.getUuid(), + network.getCidr(), networkAcl.getUuid()); + tiers.add(tier); + } + + OvsVpcRoutingPolicyConfigCommand cmd = new OvsVpcRoutingPolicyConfigCommand(vpc.getUuid(), vpc.getCidr(), + acls.toArray(new OvsVpcRoutingPolicyConfigCommand.Acl[acls.size()]), + tiers.toArray(new OvsVpcRoutingPolicyConfigCommand.Tier[tiers.size()])); + return cmd; + } + + + public boolean sendVpcRoutingPolicyChangeUpdate(OvsVpcRoutingPolicyConfigCommand updateCmd, long hostId, String bridgeName) { + try { + s_logger.debug("Sending VPC routing policy change update to the host " + hostId); + updateCmd.setHostId(hostId); + updateCmd.setBridgeName(bridgeName); + Answer ans = _agentMgr.send(hostId, updateCmd); + if (ans.getResult()) { + s_logger.debug("Successfully updated the host " + hostId + " with latest VPC routing policies." ); + return true; + } else { + s_logger.debug("Failed to update the host " + hostId + " with latest routing policy." ); + return false; + } + } catch (Exception e) { + s_logger.debug("Failed to updated the host " + hostId + " with latest routing policy." ); + return false; + } + } } diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index dbcc2886a5d..ac8a11d18aa 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -312,7 +312,7 @@ class jsonLoader(object): return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems())) -def configure_bridge_for_topology(bridge, this_host_id, json_config): +def configure_bridge_for_network_topology(bridge, this_host_id, json_config): vpconfig = jsonLoader(json.loads(json_config)).vpc if vpconfig is None: @@ -372,4 +372,76 @@ def configure_bridge_for_topology(bridge, this_host_id, json_config): # set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) - return "SUCCESS: successfully configured bridge as per the VPC toplogy" \ No newline at end of file + return "SUCCESS: successfully configured bridge as per the VPC topology" + +def get_acl(vpcconfig, required_acl_id): + acls = vpcconfig.acls + for acl in acls: + if acl.id == required_acl_id: + return acl + return None + +def configure_ovs_bridge_for_routing_policies(bridge, json_config): + vpconfig = jsonLoader(json.loads(json_config)).vpc + + if vpconfig is None: + logging.debug("WARNING:Can't find VPC info in json config file") + return "FAILURE:IMPROPER_JSON_CONFG_FILE" + + # First flush current egress ACL's before re-applying the ACL's + del_flows(bridge, table=3) + + egress_rules_added = False + ingress_rules_added = False + + tiers = vpconfig.tiers + for tier in tiers: + tier_cidr = tier.cidr + acl = get_acl(vpconfig, tier.aclid) + acl_items = acl.aclitems + + for acl_item in acl_items: + number = acl_item.number + action = acl_item.action + direction = acl_item.direction + source_port_start = acl_item.sourceportstart + source_port_end = acl_item.sourceportend + protocol = acl_item.protocol + source_cidrs = acl_item.sourcecidrs + acl_priority = 1000 + number + for source_cidr in source_cidrs: + if direction is "ingress": + ingress_rules_added = True + # add flow rule to do action (allow/deny) for flows where source IP of the packet is in + # source_cidr and destination ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 + + elif direction in "egress": + egress_rules_added = True + # add flow rule to do action (allow/deny) for flows where destination IP of the packet is in + # source_cidr and source ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 + + if egress_rules_added is False: + # add a default rule in egress table to forward packet to L3 lookup table + add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + + if ingress_rules_added is False: + # add a default rule in egress table drop packets + add_flow(bridge, priority=0, table=5, actions='drop') \ No newline at end of file diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel index 9ef0f7ba4c8..98a9d0b82f5 100755 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -184,7 +184,7 @@ def setup_ovs_bridge_for_distributed_routing(session, args): # add a default rule in L3 lookup table to forward packet to L2 lookup table lib.add_flow(bridge, priority=0, table=4, actions='resubmit(,1)') - # add a default rule in egress table to forward packet to L3 lookup table + # add a default rule in ingress table to drop in bound packets lib.add_flow(bridge, priority=0, table=5, actions='drop') result = "SUCCESS: successfully setup bridge with flow rules" @@ -391,7 +391,14 @@ def configure_ovs_bridge_for_network_topology(session, args): json_config = args.pop("config") this_host_id = args.pop("host-id") - return lib.configure_bridge_for_topology(bridge, this_host_id, json_config) + return lib.configure_bridge_for_network_topology(bridge, this_host_id, json_config) + +@echo +def configure_ovs_bridge_for_routing_policies(session, args): + bridge = args.pop("bridge") + json_config = args.pop("config") + + return lib.configure_ovs_bridge_for_router_policies(bridge, json_config) if __name__ == "__main__": XenAPIPlugin.dispatch({"create_tunnel": create_tunnel, @@ -401,4 +408,5 @@ if __name__ == "__main__": "is_xcp": is_xcp, "getLabel": getLabel, "setup_ovs_bridge_for_distributed_routing": setup_ovs_bridge_for_distributed_routing, - "configure_ovs_bridge_for_network_topology": configure_ovs_bridge_for_network_topology}) + "configure_ovs_bridge_for_network_topology": configure_ovs_bridge_for_network_topology, + "configure_ovs_bridge_for_routing_policies": "configure_ovs_bridge_for_routing_policies"}) diff --git a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java index 82f1216d7fb..ad47df14cc9 100644 --- a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java +++ b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java @@ -23,6 +23,8 @@ import javax.ejb.Local; import javax.inject.Inject; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.log4j.Logger; import com.cloud.configuration.ConfigurationManager; @@ -82,6 +84,8 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana EntityManager _entityMgr; @Inject VpcService _vpcSvc; + @Inject + MessageBus _messageBus; @Override public NetworkACL createNetworkACL(String name, String description, long vpcId, Boolean forDisplay) { @@ -210,7 +214,13 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana if (_networkDao.update(network.getId(), network)) { s_logger.debug("Updated network: " + network.getId() + " with Network ACL Id: " + acl.getId() + ", Applying ACL items"); //Apply ACL to network - return applyACLToNetwork(network.getId()); + Boolean result = applyACLToNetwork(network.getId()); + if (result) { + // public message on message bus, so that network elements implementing distributed routing capability + // can act on the event + _messageBus.publish(_name, "Network_ACL_Replaced", PublishScope.LOCAL, network); + } + return result; } return false; } From 36541a2f4ca3216e58b835cb1bb33d38bc39692b Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 11 Mar 2014 13:05:03 +0530 Subject: [PATCH 15/31] adding distributed routing support for KVM OVS some check style error fixes --- .../resource/LibvirtComputingResource.java | 47 ++++ .../xen/resource/CitrixResourceBase.java | 2 +- .../api/OvsVpcRoutingPolicyConfigCommand.java | 1 - .../network/ovs/OvsTunnelManagerImpl.java | 21 +- .../vm/network/vnet/cloudstack_pluginlib.py | 226 ++++++++++++++++++ scripts/vm/network/vnet/ovstunnel.py | 86 ++++++- 6 files changed, 373 insertions(+), 10 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index c1a77214bec..ec8bc110372 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -78,6 +78,8 @@ import com.cloud.agent.api.OvsDestroyTunnelCommand; import com.cloud.agent.api.OvsFetchInterfaceAnswer; import com.cloud.agent.api.OvsFetchInterfaceCommand; import com.cloud.agent.api.OvsSetupBridgeCommand; +import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; +import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PingRoutingWithNwGroupsCommand; @@ -1360,6 +1362,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return execute((OvsCreateTunnelCommand)cmd); } else if (cmd instanceof OvsDestroyTunnelCommand) { return execute((OvsDestroyTunnelCommand)cmd); + } else if (cmd instanceof OvsVpcPhysicalTopologyConfigCommand) { + return execute((OvsVpcPhysicalTopologyConfigCommand) cmd); + } else if (cmd instanceof OvsVpcRoutingPolicyConfigCommand) { + return execute((OvsVpcRoutingPolicyConfigCommand) cmd); } else { s_logger.warn("Unsupported command "); return Answer.createUnsupportedCommandAnswer(cmd); @@ -1401,6 +1407,47 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return new Answer(cmd, true, null); } + public Answer execute(OvsVpcPhysicalTopologyConfigCommand cmd) { + + String bridge = cmd.getBridgeName(); + try { + Script command = new Script(_ovsTunnelPath, _timeout, s_logger); + command.add("configure_ovs_bridge_for_network_topology"); + command.add("--bridge", bridge); + command.add("--config", cmd.getVpcConfigInJson()); + + String result = command.execute(); + if (result.equalsIgnoreCase("SUCCESS")) { + return new Answer(cmd, true, result); + } else { + return new Answer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught exception while updating host with latest routing polcies", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + public Answer execute(OvsVpcRoutingPolicyConfigCommand cmd) { + + try { + Script command = new Script(_ovsTunnelPath, _timeout, s_logger); + command.add("configure_ovs_bridge_for_routing_policies"); + command.add("--bridge", cmd.getBridgeName()); + command.add("--config", cmd.getVpcConfigInJson()); + + String result = command.execute(); + if (result.equalsIgnoreCase("SUCCESS")) { + return new Answer(cmd, true, result); + } else { + return new Answer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught exception while updating host with latest VPC topology", e); + return new Answer(cmd, false, e.getMessage()); + } + } + private synchronized void destroyTunnelNetwork(String bridge) { try { findOrCreateTunnelNetwork(bridge); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index a281002c02a..273c08cfe40 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -88,7 +88,6 @@ import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import com.cloud.agent.IAgentControl; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachVolumeAnswer; @@ -149,6 +148,7 @@ import com.cloud.agent.api.OvsSetTagAndFlowAnswer; import com.cloud.agent.api.OvsSetTagAndFlowCommand; import com.cloud.agent.api.OvsSetupBridgeCommand; import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; +import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand; import com.cloud.agent.api.PerformanceMonitorAnswer; import com.cloud.agent.api.PerformanceMonitorCommand; import com.cloud.agent.api.PingCommand; diff --git a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java index 8e4d5d1e2b1..50f1fdd98a3 100644 --- a/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java +++ b/plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java @@ -18,7 +18,6 @@ package com.cloud.agent.api; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.util.UUID; /** * This command represents logical view of VM's connectivity in VPC. diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 35b00355533..eeb22b12704 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -16,11 +16,25 @@ // under the License. package com.cloud.network.ovs; -import com.amazonaws.services.ec2.model.NetworkAcl; -import com.cloud.agent.api.*; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.OvsCreateTunnelAnswer; +import com.cloud.agent.api.OvsCreateTunnelCommand; +import com.cloud.agent.api.OvsDestroyBridgeCommand; +import com.cloud.agent.api.OvsDestroyTunnelCommand; +import com.cloud.agent.api.OvsFetchInterfaceAnswer; +import com.cloud.agent.api.OvsFetchInterfaceCommand; +import com.cloud.agent.api.OvsSetupBridgeCommand; +import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; +import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; -import com.cloud.network.vpc.*; +import com.cloud.network.vpc.NetworkACLVO; +import com.cloud.network.vpc.NetworkACLItemDao; +import com.cloud.network.vpc.NetworkACLItemVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.VpcManager; +import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.NetworkACLDao; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.Nic; @@ -63,7 +77,6 @@ import com.cloud.network.ovs.dao.OvsTunnelInterfaceVO; import com.cloud.network.ovs.dao.OvsTunnelNetworkDao; import com.cloud.network.ovs.dao.OvsTunnelNetworkVO; import com.cloud.network.ovs.dao.OvsTunnel; -import com.cloud.network.vpc.dao.VpcDao; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/scripts/vm/network/vnet/cloudstack_pluginlib.py b/scripts/vm/network/vnet/cloudstack_pluginlib.py index f886aa3f3b1..6c249170f12 100755 --- a/scripts/vm/network/vnet/cloudstack_pluginlib.py +++ b/scripts/vm/network/vnet/cloudstack_pluginlib.py @@ -174,6 +174,7 @@ def _build_flow_expr(**kwargs): dl_dst = 'dl_dst' in kwargs and ",dl_dst=%s" % kwargs['dl_dst'] or '' nw_src = 'nw_src' in kwargs and ",nw_src=%s" % kwargs['nw_src'] or '' nw_dst = 'nw_dst' in kwargs and ",nw_dst=%s" % kwargs['nw_dst'] or '' + table = 'table' in kwargs and ",table=%s" % kwargs['table'] or '' proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or '' ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or '' flow = (flow + in_port + dl_type + dl_src + dl_dst + @@ -217,3 +218,228 @@ def del_all_flows(bridge): def del_port(bridge, port): delPort = [VSCTL_PATH, "del-port", bridge, port] do_cmd(delPort) + + +def get_network_id_for_vif(vif_name): + domain_id, device_id = vif_name[3:len(vif_name)].split(".") + dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"]) + vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"]) + vnet = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=other-config", + "param-key=cloudstack-network-id"]) + return vnet + +def get_network_id_for_tunnel_port(tunnelif_name): + vnet = do_cmd([VSCTL_PATH, "get", "interface", tunnelif_name, "options:cloudstack-network-id"]) + return vnet + +def clear_flooding_rules_for_port(bridge, ofport): + del_flows(bridge, in_port=ofport, table=2) + +def add_flooding_rules_for_port(bridge, in_ofport, out_ofports): + action = "".join("output:%s," %ofport for ofport in out_ofports)[:-1] + add_flow(bridge, priority=1100, in_port=in_ofport, table=1, actions=action) + +def get_ofport_for_vif(vif_name): + return do_cmd([VSCTL_PATH, "get", "interface", vif_name, "ofport"]) + +def get_macaddress_of_vif(vif_name): + domain_id, device_id = vif_name[3:len(vif_name)].split(".") + dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"]) + vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"]) + mac = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=MAC"]) + return mac + +def get_vif_name_from_macaddress(macaddress): + vif_uuid = do_cmd([XE_PATH, "vif-list", "MAC=%s" % macaddress, "--minimal"]) + vif_device_id = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=device"]) + vm_uuid = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=vm-uuid"]) + vm_domain_id = do_cmd([XE_PATH, "vm-param-get", "uuid=%s" % vm_uuid, "param-name=dom-id"]) + return "vif"+vm_domain_id+"."+vif_device_id + +def add_mac_lookup_table_entry(bridge, mac_address, out_of_port): + add_flow(bridge, priority=1100, dl_dst=mac_address, table=1, actions="output:%s" % out_of_port) + +def delete_mac_lookup_table_entry(bridge, mac_address): + del_flows(bridge, dl_dst=mac_address, table=1) + +def add_ip_lookup_table_entry(bridge, ip, dst_tier_gateway_mac, dst_vm_mac): + action_str = "mod_dl_sr:%s" % dst_tier_gateway_mac + ",mod_dl_dst:%s" % dst_vm_mac +",resubmit(,5)" + addflow = [OFCTL_PATH, "add-flow", bridge, "table=4", "nw_dst=%s" % ip, "actions=%s" %action_str] + do_cmd(addflow) + +def get_vms_on_host(vpc, host_id): + all_vms = vpc.vms + vms_on_host = [] + for vm in all_vms: + if vm.hostid == host_id: + vms_on_host.append(vm) + return vms_on_host + +def get_network_details(vpc, network_uuid): + tiers = vpc.tiers + for tier in tiers: + if tier.networkuuid == network_uuid: + return tier + return None + +class jsonLoader(object): + def __init__(self, obj): + for k in obj: + v = obj[k] + if isinstance(v, dict): + setattr(self, k, jsonLoader(v)) + elif isinstance(v, (list, tuple)): + if len(v) > 0 and isinstance(v[0], dict): + setattr(self, k, [jsonLoader(elem) for elem in v]) + else: + setattr(self, k, v) + else: + setattr(self, k, v) + + def __getattr__(self, val): + if val in self.__dict__: + return self.__dict__[val] + else: + return None + + def __repr__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) + in self.__dict__.iteritems())) + + def __str__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) + in self.__dict__.iteritems())) + +def configure_bridge_for_network_topology(bridge, this_host_id, json_config): + vpconfig = jsonLoader(json.loads(json_config)).vpc + + if vpconfig is None: + logging.debug("WARNING:Can't find VPC info in json config file") + return "FAILURE:IMPROPER_JSON_CONFG_FILE" + + # get the list of Vm's in the VPC from the JSON config + this_host_vms = get_vms_on_host(vpconfig, this_host_id) + + for vm in this_host_vms: + for nic in vm.nics: + mac_addr = nic.macaddress + ip = nic.ipaddress + vif_name = get_vif_name_from_macaddress(mac_addr) + of_port = get_ofport_for_vif(vif_name) + network = get_network_details(vpconfig, nic.networkuuid) + + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet on the found OFPORT + add_mac_lookup_table_entry(bridge, mac_addr, of_port) + + # Add flow rule in L3 look up table: if the destination IP = VM's IP then modify the packet + # to set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) + + # Add flow entry to send with intra tier traffic from the NIC to L2 lookup path) + addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, + "nw_dst=%s" %network.cidr, "actions=resubmit(,1)"] + do_cmd(addflow) + + #add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path) + addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, + "dl_dst=%s" %network.gatewaymac, "nw_dst=%s" %vpconfig.cidr, "actions=resubmit(,3)"] + do_cmd(addflow) + + # get the list of hosts on which VPC spans from the JSON config + vpc_spanning_hosts = vpconfig.hosts + + for host in vpc_spanning_hosts: + if this_host_id == host.hostid: + continue + other_host_vms = get_vms_on_host(vpconfig, host.hostid) + for vm in other_host_vms: + for nic in vm.nics: + mac_addr = nic.macaddress + ip = nic.ipaddress + network = get_network_details(vpconfig, nic.networkuuid) + gre_key = network.grekey + + # generate tunnel name from tunnel naming convention + tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid) + of_port = get_ofport_for_vif(tunnel_name) + + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet tunnel port + add_mac_lookup_table_entry(bridge, mac_addr, of_port) + + # Add flow tule in L3 look up table: if the destination IP = VM's IP then modify the packet + # set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) + + return "SUCCESS: successfully configured bridge as per the VPC topology" + +def get_acl(vpcconfig, required_acl_id): + acls = vpcconfig.acls + for acl in acls: + if acl.id == required_acl_id: + return acl + return None + +def configure_ovs_bridge_for_routing_policies(bridge, json_config): + vpconfig = jsonLoader(json.loads(json_config)).vpc + + if vpconfig is None: + logging.debug("WARNING:Can't find VPC info in json config file") + return "FAILURE:IMPROPER_JSON_CONFG_FILE" + + # First flush current egress ACL's before re-applying the ACL's + del_flows(bridge, table=3) + + egress_rules_added = False + ingress_rules_added = False + + tiers = vpconfig.tiers + for tier in tiers: + tier_cidr = tier.cidr + acl = get_acl(vpconfig, tier.aclid) + acl_items = acl.aclitems + + for acl_item in acl_items: + number = acl_item.number + action = acl_item.action + direction = acl_item.direction + source_port_start = acl_item.sourceportstart + source_port_end = acl_item.sourceportend + protocol = acl_item.protocol + source_cidrs = acl_item.sourcecidrs + acl_priority = 1000 + number + for source_cidr in source_cidrs: + if direction is "ingress": + ingress_rules_added = True + # add flow rule to do action (allow/deny) for flows where source IP of the packet is in + # source_cidr and destination ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 + + elif direction in "egress": + egress_rules_added = True + # add flow rule to do action (allow/deny) for flows where destination IP of the packet is in + # source_cidr and source ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 + + if egress_rules_added is False: + # add a default rule in egress table to forward packet to L3 lookup table + add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + + if ingress_rules_added is False: + # add a default rule in egress table drop packets + add_flow(bridge, priority=0, table=5, actions='drop') diff --git a/scripts/vm/network/vnet/ovstunnel.py b/scripts/vm/network/vnet/ovstunnel.py index 9e054133b40..57085d8592b 100755 --- a/scripts/vm/network/vnet/ovstunnel.py +++ b/scripts/vm/network/vnet/ovstunnel.py @@ -27,6 +27,7 @@ import os import sys import subprocess import time +import simplejson as json from optparse import OptionParser, OptionGroup, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError from time import localtime as _localtime, asctime as _asctime @@ -72,6 +73,58 @@ def setup_ovs_bridge(bridge, key, cs_host_id): logging.debug("Setup_ovs_bridge completed with result:%s" % result) return result +@echo +def setup_ovs_bridge_for_distributed_routing(bridge, cs_host_id): + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + logging.debug("About to manually create the bridge:%s" % bridge) + res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge]) + logging.debug("Bridge has been manually created:%s" % res) + + # Non empty result means something went wrong + if res: + result = "FAILURE:%s" % res + else: + # Verify the bridge actually exists + res = lib.do_cmd([lib.VSCTL_PATH, "list", "bridge", bridge]) + + res = lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other_config:is-ovs_vpc_distributed_vr_network=True"]) + conf_hosts = lib.do_cmd([lib.VSCTL_PATH, "get","bridge", bridge,"other:ovs-host-setup"]) + conf_hosts = cs_host_id + (conf_hosts and ',%s' % conf_hosts or '') + lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, + "other_config:ovs-host-setup=%s" % conf_hosts]) + + # add a default flow rule to send broadcast and multi-cast packets to L2 flooding table + lib.add_flow(bridge, priority=1000, dl_dst='ff:ff:ff:ff:ff:ff', table=0, actions='resubmit(,2)') + lib.add_flow(bridge, priority=1000, nw_dst='224.0.0.0/24', table=0, actions='resubmit(,2)') + + # add a default flow rule to send uni-cast traffic to L2 lookup table + lib.add_flow(bridge, priority=0, table=0, actions='resubmit(,1)') + + # add a default rule to send unknown mac address to L2 flooding table + lib.add_flow(bridge, priority=0, table=1, actions='resubmit(,2)') + + # add a default rule in L2 flood table to drop packet + lib.add_flow(bridge, priority=0, table=2, actions='drop') + + # add a default rule in egress table to forward packet to L3 lookup table + lib.add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + + # add a default rule in L3 lookup table to forward packet to L2 lookup table + lib.add_flow(bridge, priority=0, table=4, actions='resubmit(,1)') + + # add a default rule in ingress table to drop in bound packets + lib.add_flow(bridge, priority=0, table=5, actions='drop') + + result = "SUCCESS: successfully setup bridge with flow rules" + + logging.debug("Setup_ovs_bridge completed with result:%s" % result) + + return result + def destroy_ovs_bridge(bridge): res = lib.check_switch() @@ -163,12 +216,30 @@ def create_tunnel(bridge, remote_ip, key, src_host, dst_host): # Ensure no trailing LF if tun_ofport.endswith('\n'): tun_ofport = tun_ofport[:-1] - # add flow entryies for dropping broadcast coming in from gre tunnel - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + + ovs_tunnel_network = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, "other_config:is-ovs-tun-network"]) + ovs_vpc_distributed_vr_network = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, + "other_config:is-ovs_vpc_distributed_vr_network"]) + + if ovs_tunnel_network == 'True': + # add flow entryies for dropping broadcast coming in from gre tunnel + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, nw_dst='224.0.0.0/24', actions='drop') - drop_flow_setup = True + drop_flow_setup = True + + if ovs_vpc_distributed_vr_network == 'True': + # add flow rules for dropping broadcast coming in from tunnel ports + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + nw_dst='224.0.0.0/24', actions='drop') + + # add flow rule to send the traffic from tunnel ports to L2 switching table only + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, actions='resubmit(,1)') + lib.do_cmd([lib.VSCTL_PATH, "set", "interface", name, "options:cloudstack-network-id=%s" % network_uuid]) + logging.debug("Broadcast drop rules added") # return "SUCCESS:%s" % name return 'true' @@ -210,6 +281,7 @@ if __name__ == '__main__': parser.add_option("--src_host", dest="src_host") parser.add_option("--dst_host", dest="dst_host") parser.add_option("--iface_name", dest="iface_name") + parser.ad_option("--config", dest="config") (option, args) = parser.parse_args() if len(args) == 0: logging.debug("No command to execute") @@ -223,6 +295,12 @@ if __name__ == '__main__': create_tunnel(option.bridge, option.remote_ip, option.key, option.src_host, option.dst_host) elif cmd == "destroy_tunnel": destroy_tunnel(option.bridge, option.iface_name) + elif cmd == "setup_ovs_bridge_for_distributed_routing": + setup_ovs_bridge_for_distributed_routing(bridge, cs_host_id) + elif cmd == "configure_ovs_bridge_for_network_topology": + configure_bridge_for_network_topology(brdige, cs_host_id, config) + elif cmd == "configure_ovs_bridge_for_routing_policies": + configure_ovs_bridge_for_routing_policies(bridge, config) else: logging.debug("Unknown command: " + cmd) sys.exit(1) From 015b667c6a19cc0591ffb5038dac4557f9469c58 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 11 Mar 2014 18:29:36 +0530 Subject: [PATCH 16/31] integration tests for VPC's enabled for distributed routing --- build/simulator.properties | 28 - .../test_vpc_distributed_routing_offering.py | 512 ++++++++++++++++++ tools/marvin/marvin/integration/lib/base.py | 15 + 3 files changed, 527 insertions(+), 28 deletions(-) delete mode 100644 build/simulator.properties create mode 100644 test/integration/component/test_vpc_distributed_routing_offering.py diff --git a/build/simulator.properties b/build/simulator.properties deleted file mode 100644 index d65d05c65be..00000000000 --- a/build/simulator.properties +++ /dev/null @@ -1,28 +0,0 @@ -# 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. - -DBUSER=cloud -DBPW=cloud -MSLOG=vmops.log -APISERVERLOG=api.log -DBHOST=localhost -DBROOTPW= -AGENTLOGDIR=logs -AGENTLOG=logs/agent.log -MSMNTDIR=/mnt -COMPONENTS-SPEC=components-simulator.xml -AWSAPILOG=awsapi.log diff --git a/test/integration/component/test_vpc_distributed_routing_offering.py b/test/integration/component/test_vpc_distributed_routing_offering.py new file mode 100644 index 00000000000..0fa7de7934f --- /dev/null +++ b/test/integration/component/test_vpc_distributed_routing_offering.py @@ -0,0 +1,512 @@ +# 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. +import unittest + +""" Component tests for inter VLAN functionality +""" +#Import Local Modules +import marvin +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from marvin.sshClient import SshClient +import datetime + + +class Services: + """Test inter VLAN services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 128, + }, + "network_offering": { + "name": 'VPC Network offering', + "displaytext": 'VPC Network off', + "guestiptype": 'Isolated', + "supportedservices": 'Vpn,Dhcp,Dns,SourceNat,PortForwarding,Lb,UserData,StaticNat,NetworkACL, Connectivity', + "traffictype": 'GUEST', + "availability": 'Optional', + "useVpc": 'on', + "serviceProviderList": { + "Vpn": 'VpcVirtualRouter', + "Dhcp": 'VpcVirtualRouter', + "Dns": 'VpcVirtualRouter', + "SourceNat": 'VpcVirtualRouter', + "PortForwarding": 'VpcVirtualRouter', + "Lb": 'VpcVirtualRouter', + "UserData": 'VpcVirtualRouter', + "StaticNat": 'VpcVirtualRouter', + "NetworkACL": 'VpcVirtualRouter', + "Connectivity": 'Ovs' + }, + }, + "vpc_offering": { + "name": 'VPC off', + "displaytext": 'VPC off', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Lb,UserData,StaticNat,Connectivity', + "serviceProviderList": { + "Vpn": 'VpcVirtualRouter', + "Dhcp": 'VpcVirtualRouter', + "Dns": 'VpcVirtualRouter', + "SourceNat": 'VpcVirtualRouter', + "PortForwarding": 'VpcVirtualRouter', + "Lb": 'VpcVirtualRouter', + "UserData": 'VpcVirtualRouter', + "StaticNat": 'VpcVirtualRouter', + "Connectivity": 'Ovs' + }, + "serviceCapabilityList": { + "Connectivity": { + "DistributedRouter": "true" + }, + }, + }, + "vpc": { + "name": "TestVPC", + "displaytext": "TestVPC", + "cidr": '10.0.0.1/24' + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "lbrule": { + "name": "SSH", + "alg": "leastconn", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + "openfirewall": False, + "startport": 2222, + "endport": 2222, + "cidrlist": '0.0.0.0/0', + "protocol": 'TCP' + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "startport": 22, + "endport": 22, + "protocol": "TCP", + "cidrlist": '0.0.0.0/0', + }, + "fw_rule": { + "startport": 1, + "endport": 6000, + "cidr": '0.0.0.0/0', + # Any network (For creating FW rule) + "protocol": "TCP" + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + } + + +class TestVPCDistributedRouterOffering(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestVPCDistributedRouterOffering, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + self.cleanup.insert(0, self.account) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def validate_vpc_offering(self, vpc_offering): + """Validates the VPC offering""" + + self.debug("Check if the VPC offering is created successfully?") + vpc_offs = VpcOffering.list( + self.apiclient, + id=vpc_offering.id + ) + self.assertEqual( + isinstance(vpc_offs, list), + True, + "List VPC offerings should return a valid list" + ) + self.assertEqual( + vpc_offering.name, + vpc_offs[0].name, + "Name of the VPC offering should match with listVPCOff data" + ) + self.assertEqual( + vpc_offering.name, + vpc_offs[0].name, + "Name of the VPC offering should match with listVPCOff data" + ) + self.assertEqual( + vpc_offs[0].distributedvpcrouter,True, + "VPC offering is not set up for Distributed routing" + ) + self.debug( + "VPC offering is created successfully - %s" % + vpc_offering.name) + return + + def validate_vpc_network(self, network): + """Validates the VPC network""" + + self.debug("Check if the VPC network is created successfully?") + vpc_networks = VPC.list( + self.apiclient, + id=network.id + ) + self.assertEqual( + isinstance(vpc_networks, list), + True, + "List VPC network should return a valid list" + ) + self.assertEqual( + network.name, + vpc_networks[0].name, + "Name of the VPC network should match with listVPC data" + ) + self.debug("VPC network created successfully - %s" % network.name) + return + + @attr(tags=["advanced", "intervlan"]) + def test_01_create_vpc_offering_with_distributedrouter_service_capability(self): + """ Test create VPC offering + """ + + # Steps for validation + # 1. Create VPC Offering by specifying all supported Services + # 2. VPC offering should be created successfully. + + self.debug("Creating inter VPC offering") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + + self.debug("Check if the VPC offering is created successfully?") + self.cleanup.append(vpc_off) + self.validate_vpc_offering(vpc_off) + return + + @attr(tags=["advanced", "intervlan"]) + def test_02_create_vpc_from_offering_with_distributedrouter_service_capability(self): + """ Test create VPC offering + """ + + # Steps for validation + # 1. Create VPC Offering by specifying all supported Services + # 2. VPC offering should be created successfully. + + self.debug("Creating inter VPC offering") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + vpc_off.update(self.apiclient, state='Enabled') + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_off.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual(vpc.distributedvpcrouter, True, "VPC created should have 'distributedvpcrouter' set to True") + + try: + vpc.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete VPC network - %s" % e) + return + + @attr(tags=["advanced", "intervlan"]) + def test_03_deploy_vms_in_vpc_with_distributedrouter(self): + """Test deploy virtual machines in VPC networks""" + + # 1. Create VPC Offering by specifying all supported Services + # (Vpn,dhcpdns,UserData, SourceNat,Static NAT and PF,LB,NetworkAcl) + # 2. Create a VPC using the above VPC offering + # 3. Create a network as part of this VPC. + # 4. Deploy few Vms. + # 5. Create a LB rule for this VM. + # 6. Create a PF rule for this VM. + # 7. Create a Static Nat rule for this VM. + # 8. Create Ingress rules on the network to open the above created + # LB PF and Static Nat rule + # 9. Create Egress Network ACL for this network to access google.com. + # 10. Enable VPN services + + self.debug("Creating a VPC offering..") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + + vpc_off.update(self.apiclient, state='Enabled') + + self.debug("creating a VPC network in the account: %s" % + self.account.name) + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_off.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.validate_vpc_network(vpc) + + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + conservemode=False + ) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + gateway = vpc.cidr.split('/')[0] + # Split the cidr to retrieve gateway + # for eg. cidr = 10.0.0.1/24 + # Gateway = 10.0.0.1 + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id, + gateway=gateway, + vpcid=vpc.id + ) + self.debug("Created network with ID: %s" % network.id) + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)] + ) + self.debug("Deployed VM in network: %s" % network.id) + + self.debug("Associating public IP for network: %s" % network.name) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + network.id + )) + + self.debug("Creating LB rule for IP address: %s" % + public_ip.ipaddress.ipaddress) + + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.name, + networkid=network.id, + vpcid=vpc.id, + domainid=self.account.domainid + ) + + self.debug("Associating public IP for network: %s" % vpc.name) + public_ip_2 = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip_2.ipaddress.ipaddress, + network.id + )) + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=public_ip_2.ipaddress.id, + openfirewall=False, + networkid=network.id, + vpcid=vpc.id + ) + + self.debug("Adding NetwrokACl rules to make PF and LB accessible") + networkacl_1 = NetworkACL.create( + self.apiclient, + networkid=network.id, + services=self.services["natrule"], + traffictype='Ingress' + ) + + networkacl_2 = NetworkACL.create( + self.apiclient, + networkid=network.id, + services=self.services["lbrule"], + traffictype='Ingress' + ) + self.debug("Checking if we can SSH into VM?") + try: + virtual_machine.get_ssh_client( + ipaddress=public_ip_2.ipaddress.ipaddress, + ) + self.debug("SSH into VM is successfully") + except Exception as e: + self.fail("Failed to SSH into VM - %s, %s" % + (public_ip_2.ipaddress.ipaddress, e)) + + self.debug("Associating public IP for network: %s" % network.name) + public_ip_3 = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip_3.ipaddress.ipaddress, + network.id + )) + self.debug("Enabling static NAT for IP: %s" % + public_ip_3.ipaddress.ipaddress) + try: + StaticNATRule.enable( + self.apiclient, + ipaddressid=public_ip_3.ipaddress.id, + virtualmachineid=virtual_machine.id, + networkid=network.id + ) + self.debug("Static NAT enabled for IP: %s" % + public_ip_3.ipaddress.ipaddress) + except Exception as e: + self.fail("Failed to enable static NAT on IP: %s - %s" % ( + public_ip_3.ipaddress.ipaddress, e)) + + public_ips = PublicIPAddress.list( + self.apiclient, + networkid=network.id, + listall=True, + isstaticnat=True, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(public_ips, list), + True, + "List public Ip for network should list the Ip addr" + ) + self.assertEqual( + public_ips[0].ipaddress, + public_ip_3.ipaddress.ipaddress, + "List public Ip for network should list the Ip addr" + ) + # TODO: Remote Access VPN is not yet supported in VPC + return \ No newline at end of file diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 72a6926b6bb..b7e2be47557 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -3081,6 +3081,21 @@ class VpcOffering: cmd.name = "-".join([services["name"], random_gen()]) cmd.displaytext = services["displaytext"] cmd.supportedServices = services["supportedservices"] + if "serviceProviderList" in services: + for service, provider in services["serviceProviderList"].items(): + cmd.serviceproviderlist.append({ + 'service': service, + 'provider': provider + }) + if "serviceCapabilityList" in services: + cmd.servicecapabilitylist = [] + for service, capability in services["serviceCapabilityList"].items(): + for ctype, value in capability.items(): + cmd.servicecapabilitylist.append({ + 'service': service, + 'capabilitytype': ctype, + 'capabilityvalue': value + }) return VpcOffering(apiclient.createVPCOffering(cmd).__dict__) def update(self, apiclient, name=None, displaytext=None, state=None): From e487b24bb657295dd6d3da1ab9069d4e3b4c15bd Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Thu, 13 Mar 2014 19:18:41 +0530 Subject: [PATCH 17/31] couple of bug fixes --- .../src/com/cloud/network/vpc/VpcVO.java | 1 + .../xen/resource/CitrixResourceBase.java | 12 +- .../network/ovs/OvsNetworkTopologyGuru.java | 5 + .../ovs/OvsNetworkTopologyGuruImpl.java | 18 ++ .../network/ovs/OvsTunnelManagerImpl.java | 251 +++++++++++------- .../xenserver/cloudstack_pluginlib.py | 26 +- .../vm/hypervisor/xenserver/ovs-vif-flows.py | 27 +- 7 files changed, 223 insertions(+), 117 deletions(-) diff --git a/engine/schema/src/com/cloud/network/vpc/VpcVO.java b/engine/schema/src/com/cloud/network/vpc/VpcVO.java index d7aaa95b87c..dea8c314bcc 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcVO.java @@ -206,6 +206,7 @@ public class VpcVO implements Vpc { @Override public IAMEntityType getEntityType() { return IAMEntityType.Vpc; + } @Override public boolean usesDistributedRouter() { diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 273c08cfe40..57debc6a7d2 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -5284,15 +5284,17 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe public Answer execute(OvsVpcPhysicalTopologyConfigCommand cmd) { Connection conn = getConnection(); try { + Network nw = findOrCreateTunnelNetwork(conn, cmd.getBridgeName()); + String bridgeName = nw.getBridge(conn);; String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", - cmd.getBridgeName(), "config", cmd.getVpcConfigInJson()); - if (result.equalsIgnoreCase("SUCCESS")) { + bridgeName, "config", cmd.getVpcConfigInJson(), "host-id", ((Long)cmd.getHostId()).toString()); + if (result.startsWith("SUCCESS")) { return new Answer(cmd, true, result); } else { return new Answer(cmd, false, result); } } catch (Exception e) { - s_logger.warn("caught exception while updating host with latest routing polcies", e); + s_logger.warn("caught exception while updating host with latest VPC topology", e); return new Answer(cmd, false, e.getMessage()); } } @@ -5303,13 +5305,13 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_routing_policies", "bridge", cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString(), "config", cmd.getVpcConfigInJson()); - if (result.equalsIgnoreCase("SUCCESS")) { + if (result.startsWith("SUCCESS")) { return new Answer(cmd, true, result); } else { return new Answer(cmd, false, result); } } catch (Exception e) { - s_logger.warn("caught exception while updating host with latest VPC topology", e); + s_logger.warn("caught exception while updating host with latest routing policies", e); return new Answer(cmd, false, e.getMessage()); } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java index 122175caffc..a9b62bff4f4 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuru.java @@ -56,4 +56,9 @@ public interface OvsNetworkTopologyGuru extends Manager { * get the list of all Vm id's in the network that are running on the host */ public List getActiveVmsInNetworkOnHost(long vpcId, long hostId); + + /** + * get the list of all Vpc id's in which, a VM has a nic in the network that is part of VPC + */ + public List getVpcIdsVmIsPartOf(long vmId); } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java index 7715641872f..740df80ceb3 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java @@ -187,4 +187,22 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor } return vmIds; } + + @Override + public List getVpcIdsVmIsPartOf(long vmId) { + List vpcIds = new ArrayList<>(); + List nics = _nicDao.listByVmId(vmId); + if (nics == null) + return null; + + for (Nic nic: nics) { + Network network = _networkDao.findById(nic.getNetworkId()); + if (network != null && network.getTrafficType() == Networks.TrafficType.Guest && network.getVpcId() != null) { + if (!vpcIds.contains(network.getVpcId())) { + vpcIds.add(network.getVpcId()); + } + } + } + return vpcIds; + } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index eeb22b12704..30088aada4a 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -36,6 +36,7 @@ import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.NetworkACLDao; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.Nic; import com.cloud.vm.NicVO; @@ -80,13 +81,14 @@ import com.cloud.network.ovs.dao.OvsTunnel; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; +import com.cloud.utils.fsm.StateListener; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; @Component @Local(value = {OvsTunnelManager.class}) -public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManager { +public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManager, StateListener { public static final Logger s_logger = Logger.getLogger(OvsTunnelManagerImpl.class.getName()); // boolean _isEnabled; @@ -133,7 +135,12 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); + // register for network ACL updated for a VPC. _messageBus.subscribe("Network_ACL_Replaced", new NetworkAclEventsSubscriber()); + + // register for VM state transition updates + VirtualMachine.State.getStateMachine().registerListener(this); + return true; } @@ -540,92 +547,6 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage return "OVS-DR-VPC-Bridge" + vpcId; } - public boolean sendVpcTopologyChangeUpdate(OvsVpcPhysicalTopologyConfigCommand updateCmd, long hostId, String bridgeName) { - try { - s_logger.debug("Sending VPC topology update to the host " + hostId); - updateCmd.setHostId(hostId); - updateCmd.setBridgeName(bridgeName); - Answer ans = _agentMgr.send(hostId, updateCmd); - if (ans.getResult()) { - s_logger.debug("Successfully updated the host " + hostId + " with latest VPC topology." ); - return true; - } else { - s_logger.debug("Failed to update the host " + hostId + " with latest VPC topology." ); - return false; - } - } catch (Exception e) { - s_logger.debug("Failed to updated the host " + hostId + " with latest VPC topology." ); - return false; - } - } - - OvsVpcPhysicalTopologyConfigCommand prepareVpcTopologyUpdate(long vpcId) { - VpcVO vpc = _vpcDao.findById(vpcId); - assert (vpc != null): "invalid vpc id"; - - List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); - List hostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); - List vmIds = _ovsNetworkToplogyGuru.getAllActiveVmsInVpc(vpcId); - - List hosts = new ArrayList<>(); - List tiers = new ArrayList<>(); - List vms = new ArrayList<>(); - - for (Long hostId : hostIds) { - HostVO hostDetails = _hostDao.findById(hostId); - String remoteIp = null; - for (Network network: vpcNetworks) { - try { - remoteIp = getGreEndpointIP(hostDetails, network); - } catch (Exception e) { - - } - } - OvsVpcPhysicalTopologyConfigCommand.Host host = new OvsVpcPhysicalTopologyConfigCommand.Host(hostId, remoteIp); - hosts.add(host); - } - - for (Network network: vpcNetworks) { - String key = network.getBroadcastUri().getAuthority(); - long gre_key; - if (key.contains(".")) { - String[] parts = key.split("\\."); - gre_key = Long.parseLong(parts[1]); - } else { - try { - gre_key = Long.parseLong(BroadcastDomainType.getValue(key)); - } catch (Exception e) { - return null; - } - } - NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId()); - OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(gre_key, - network.getUuid(), network.getGateway(), nic.getMacAddress(), network.getCidr()); - tiers.add(tier); - } - - for (long vmId: vmIds) { - VirtualMachine vmInstance = _vmInstanceDao.findById(vmId); - List vmNics = new ArrayList(); - for (Nic vmNic :_nicDao.listByVmId(vmId)) { - Network network = _networkDao.findById(vmNic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Guest) { - OvsVpcPhysicalTopologyConfigCommand.Nic nic = new OvsVpcPhysicalTopologyConfigCommand.Nic( - vmNic.getIp4Address(), vmNic.getMacAddress(), ((Long)vmNic.getNetworkId()).toString()); - vmNics.add(nic); - } - } - OvsVpcPhysicalTopologyConfigCommand.Vm vm = new OvsVpcPhysicalTopologyConfigCommand.Vm( - vmInstance.getHostId(), vmNics.toArray(new OvsVpcPhysicalTopologyConfigCommand.Nic[vmNics.size()])); - vms.add(vm); - } - return new OvsVpcPhysicalTopologyConfigCommand( - hosts.toArray(new OvsVpcPhysicalTopologyConfigCommand.Host[hosts.size()]), - tiers.toArray(new OvsVpcPhysicalTopologyConfigCommand.Tier[tiers.size()]), - vms.toArray(new OvsVpcPhysicalTopologyConfigCommand.Vm[vms.size()]), - vpc.getCidr()); - } - @DB protected void checkAndCreateVpcTunnelNetworks(Host host, long vpcId) { @@ -731,16 +652,158 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage s_logger.warn("Ovs Tunnel network created tunnel failed", e); } } + } - OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); - for (Long id: vpcSpannedHostIds) { - if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { - s_logger.debug("Failed to send VPC topology change update to host : " + id + ". Moving on with rest of" + - "the host update."); + @Override + public boolean preStateTransitionEvent(VirtualMachine.State oldState, + VirtualMachine.Event event, VirtualMachine.State newState, + VirtualMachine vo, boolean status, Object opaque) { + return true; + } + + @Override + public boolean postStateTransitionEvent(VirtualMachine.State oldState, VirtualMachine.Event event, + VirtualMachine.State newState, VirtualMachine vm, + boolean status, Object opaque) { + + if (!status) { + return false; + } + + if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling start of vm id" + vm.getId()); + } + handleVmStateChange((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling stop of vm id" + vm.getId()); + } + handleVmStateChange((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmMigrated(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling migration of vm id" + vm.getId()); + } + handleVmStateChange((VMInstanceVO)vm); + } + + return true; + } + + public void handleVmStateChange(VMInstanceVO vm) { + + // get the VPC's impacted with the VM start + List vpcIds = _ovsNetworkToplogyGuru.getVpcIdsVmIsPartOf(vm.getId()); + if (vpcIds == null || vpcIds.isEmpty()) { + return; + } + + for (Long vpcId: vpcIds) { + VpcVO vpc = _vpcDao.findById(vpcId); + if (vpc == null || !vpc.usesDistributedRouter()) { + return; + } + + // get the list of hosts on which VPC spans (i.e hosts that need to be aware of VPC topology change update) + List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + String bridgeName=generateBridgeNameForVpc(vpcId); + + OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); + for (Long id: vpcSpannedHostIds) { + if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { + s_logger.debug("Failed to send VPC topology change update to host : " + id + ". Moving on " + + "with rest of the host update."); + } } } } + public boolean sendVpcTopologyChangeUpdate(OvsVpcPhysicalTopologyConfigCommand updateCmd, long hostId, String bridgeName) { + try { + s_logger.debug("Sending VPC topology update to the host " + hostId); + updateCmd.setHostId(hostId); + updateCmd.setBridgeName(bridgeName); + Answer ans = _agentMgr.send(hostId, updateCmd); + if (ans.getResult()) { + s_logger.debug("Successfully updated the host " + hostId + " with latest VPC topology." ); + return true; + } else { + s_logger.debug("Failed to update the host " + hostId + " with latest VPC topology." ); + return false; + } + } catch (Exception e) { + s_logger.debug("Failed to updated the host " + hostId + " with latest VPC topology." ); + return false; + } + } + + OvsVpcPhysicalTopologyConfigCommand prepareVpcTopologyUpdate(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + assert (vpc != null): "invalid vpc id"; + + List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + List hostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + List vmIds = _ovsNetworkToplogyGuru.getAllActiveVmsInVpc(vpcId); + + List hosts = new ArrayList<>(); + List tiers = new ArrayList<>(); + List vms = new ArrayList<>(); + + for (Long hostId : hostIds) { + HostVO hostDetails = _hostDao.findById(hostId); + String remoteIp = null; + for (Network network: vpcNetworks) { + try { + remoteIp = getGreEndpointIP(hostDetails, network); + } catch (Exception e) { + + } + } + OvsVpcPhysicalTopologyConfigCommand.Host host = new OvsVpcPhysicalTopologyConfigCommand.Host(hostId, remoteIp); + hosts.add(host); + } + + for (Network network: vpcNetworks) { + String key = network.getBroadcastUri().getAuthority(); + long gre_key; + if (key.contains(".")) { + String[] parts = key.split("\\."); + gre_key = Long.parseLong(parts[1]); + } else { + try { + gre_key = Long.parseLong(BroadcastDomainType.getValue(key)); + } catch (Exception e) { + return null; + } + } + NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId()); + OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(gre_key, + network.getUuid(), network.getGateway(), nic.getMacAddress(), network.getCidr()); + tiers.add(tier); + } + + for (long vmId: vmIds) { + VirtualMachine vmInstance = _vmInstanceDao.findById(vmId); + List vmNics = new ArrayList(); + for (Nic vmNic :_nicDao.listByVmId(vmId)) { + Network network = _networkDao.findById(vmNic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest) { + OvsVpcPhysicalTopologyConfigCommand.Nic nic = new OvsVpcPhysicalTopologyConfigCommand.Nic( + vmNic.getIp4Address(), vmNic.getMacAddress(), network.getUuid()); + vmNics.add(nic); + } + } + OvsVpcPhysicalTopologyConfigCommand.Vm vm = new OvsVpcPhysicalTopologyConfigCommand.Vm( + vmInstance.getHostId(), vmNics.toArray(new OvsVpcPhysicalTopologyConfigCommand.Nic[vmNics.size()])); + vms.add(vm); + } + return new OvsVpcPhysicalTopologyConfigCommand( + hosts.toArray(new OvsVpcPhysicalTopologyConfigCommand.Host[hosts.size()]), + tiers.toArray(new OvsVpcPhysicalTopologyConfigCommand.Tier[tiers.size()]), + vms.toArray(new OvsVpcPhysicalTopologyConfigCommand.Vm[vms.size()]), + vpc.getCidr()); + } + // Subscriber to ACL replace events. On acl replace event, if the vpc is enabled for distributed routing // send the ACL update to all the hosts on which VPC spans public class NetworkAclEventsSubscriber implements MessageSubscriber { @@ -755,14 +818,14 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage for (Long id: vpcSpannedHostIds) { if (!sendVpcRoutingPolicyChangeUpdate(cmd, id, bridgeName)) { s_logger.debug("Failed to send VPC routing policy change update to host : " + id + - ". Moving on with rest of the host updates."); + ". But moving on with sending the host updates to the rest of the hosts."); } } } } } - OvsVpcRoutingPolicyConfigCommand prepareVpcRoutingPolicyUpdate(long vpcId) { + private OvsVpcRoutingPolicyConfigCommand prepareVpcRoutingPolicyUpdate(long vpcId) { VpcVO vpc = _vpcDao.findById(vpcId); assert (vpc != null): "invalid vpc id"; List acls = new ArrayList<>(); diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index ac8a11d18aa..1c9d513571e 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -239,7 +239,7 @@ def clear_flooding_rules_for_port(bridge, ofport): def add_flooding_rules_for_port(bridge, in_ofport, out_ofports): action = "".join("output:%s," %ofport for ofport in out_ofports)[:-1] - add_flow(bridge, priority=1100, in_port=in_ofport, table=1, actions=action) + add_flow(bridge, priority=1100, in_port=in_ofport, table=2, actions=action) def get_ofport_for_vif(vif_name): return do_cmd([VSCTL_PATH, "get", "interface", vif_name, "ofport"]) @@ -259,28 +259,30 @@ def get_vif_name_from_macaddress(macaddress): return "vif"+vm_domain_id+"."+vif_device_id def add_mac_lookup_table_entry(bridge, mac_address, out_of_port): - add_flow(bridge, priority=1100, dl_dst=mac_address, table=1, actions="output:%s" % out_of_port) + action = "output=%s" %out_of_port + add_flow(bridge, priority=1100, dl_dst=mac_address, table=1, actions=action) def delete_mac_lookup_table_entry(bridge, mac_address): del_flows(bridge, dl_dst=mac_address, table=1) def add_ip_lookup_table_entry(bridge, ip, dst_tier_gateway_mac, dst_vm_mac): - action_str = "mod_dl_sr:%s" % dst_tier_gateway_mac + ",mod_dl_dst:%s" % dst_vm_mac +",resubmit(,5)" - addflow = [OFCTL_PATH, "add-flow", bridge, "table=4", "nw_dst=%s" % ip, "actions=%s" %action_str] + action_str = "mod_dl_src:%s" % dst_tier_gateway_mac + ",mod_dl_dst:%s" % dst_vm_mac + ",resubmit(,5)" + action_str = "table=4, ip, nw_dst=%s" % ip + ", actions=%s" %action_str + addflow = [OFCTL_PATH, "add-flow", bridge, action_str] do_cmd(addflow) def get_vms_on_host(vpc, host_id): all_vms = vpc.vms vms_on_host = [] for vm in all_vms: - if vm.hostid == host_id: + if str(vm.hostid) == (host_id): vms_on_host.append(vm) return vms_on_host def get_network_details(vpc, network_uuid): tiers = vpc.tiers for tier in tiers: - if tier.networkuuid == network_uuid: + if str(tier.networkuuid) == (network_uuid): return tier return None @@ -338,20 +340,22 @@ def configure_bridge_for_network_topology(bridge, this_host_id, json_config): add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) # Add flow entry to send with intra tier traffic from the NIC to L2 lookup path) - addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, - "nw_dst=%s" %network.cidr, "actions=resubmit(,1)"] + action_str = "table=0, in_port=%s," %of_port + " ip, nw_dst=%s," %network.cidr + " actions=resubmit(,1)" + addflow = [OFCTL_PATH, "add-flow", bridge, action_str] do_cmd(addflow) #add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path) - addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port, - "dl_dst=%s" %network.gatewaymac, "nw_dst=%s" %vpconfig.cidr, "actions=resubmit(,3)"] + action_str = "table=0, in_port=%s," % of_port + " ip, dl_dst=%s," %network.gatewaymac +\ + "nw_dst=%s," %vpconfig.cidr + "actions=resubmit(,3)" + addflow = [OFCTL_PATH, "add-flow", bridge, action_str] + do_cmd(addflow) # get the list of hosts on which VPC spans from the JSON config vpc_spanning_hosts = vpconfig.hosts for host in vpc_spanning_hosts: - if this_host_id == host.hostid: + if str(this_host_id) == str(host.hostid): continue other_host_vms = get_vms_on_host(vpconfig, host.hostid) for vm in other_host_vms: diff --git a/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py b/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py index ae375252e22..1445d940ef6 100644 --- a/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py +++ b/scripts/vm/hypervisor/xenserver/ovs-vif-flows.py @@ -21,9 +21,11 @@ import copy import os import sys +import logging import cloudstack_pluginlib as pluginlib +pluginlib.setup_logging("/var/log/cloud/ovstunnel.log") def clear_flows(bridge, this_vif_ofport, vif_ofports): # Remove flow entries originating from given ofport @@ -83,18 +85,26 @@ def main(command, vif_raw): bridge = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'iface-to-br', this_vif]) - # find xs network for this bridge, verify is used for ovs tunnel network + # find xs network for this bridge, verify is used for ovs tunnel network xs_nw_uuid = pluginlib.do_cmd([pluginlib.XE_PATH, "network-list", "bridge=%s" % bridge, "--minimal"]) - ovs_tunnel_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", + ovs_tunnel_network = False + try: + ovs_tunnel_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", "uuid=%s" % xs_nw_uuid, "param-name=other-config", "param-key=is-ovs-tun-network", "--minimal"]) + except: + pass - ovs_vpc_distributed_vr_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", + ovs_vpc_distributed_vr_network = False + try: + ovs_vpc_distributed_vr_network = pluginlib.do_cmd([pluginlib.XE_PATH,"network-param-get", "uuid=%s" % xs_nw_uuid, "param-name=other-config", "param-key=is-ovs_vpc_distributed_vr_network", "--minimal"]) + except: + pass if ovs_tunnel_network == 'True': vlan = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'br-to-vlan', bridge]) @@ -121,6 +131,7 @@ def main(command, vif_raw): apply_flows(bridge, this_vif_ofport, vif_ofports) + # handle case where brdige is setup for VPC and VPC is enabled for distributed routing if ovs_vpc_distributed_vr_network == 'True': vlan = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'br-to-vlan', bridge]) if vlan != '0': @@ -136,15 +147,15 @@ def main(command, vif_raw): ports = vsctl_output.split('\n') for port in ports: - if_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', 'Interface', vif, 'ofport']) - if vif.startswith('vif'): + if_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', 'Interface', port, 'ofport']) + if port.startswith('vif'): # check VIF is in same network as that of plugged vif if vif_network_id != pluginlib.get_network_id_for_vif(port): continue vnet_vif_ofports.append(if_ofport) vnet_all_ofports.append(if_ofport) - if vif.startswith('t'): + if port.startswith('t'): # check tunnel port is in same network as that of plugged vif if vif_network_id != pluginlib.get_network_id_for_tunnel_port(port): continue @@ -159,7 +170,8 @@ def main(command, vif_raw): for port in vnet_tunnelif_ofports: pluginlib.add_flooding_rules_for_port(bridge, port, vnet_vif_ofports) - # send on all VIF and tunnel port excluding the port on which packet arrived + # for a packet arrived from VIF port send on all VIF and tunnel port excluding the port + # on which packet arrived for port in vnet_vif_ofports: vnet_all_ofports_copy = copy.copy(vnet_all_ofports) vnet_all_ofports_copy.remove(port) @@ -167,6 +179,7 @@ def main(command, vif_raw): #learn that MAC is reachable through the VIF port mac = pluginlib.get_macaddress_of_vif(this_vif) + this_vif_ofport = pluginlib.do_cmd([pluginlib.VSCTL_PATH, 'get', 'Interface', this_vif, 'ofport']) pluginlib.add_mac_lookup_table_entry(bridge, mac, this_vif_ofport) if command == 'offline': From 9a97ba76d382d9f13d2bde73aed6dfdeb4047118 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 14 Mar 2014 15:51:49 +0530 Subject: [PATCH 18/31] findbug fixes, added some comments, bug fixes --- .../xen/resource/CitrixResourceBase.java | 11 +- .../network/guru/OvsGuestNetworkGuru.java | 1 + .../ovs/OvsNetworkTopologyGuruImpl.java | 21 +- .../network/ovs/OvsTunnelManagerImpl.java | 56 +++-- .../xenserver/cloudstack_pluginlib.py | 222 ++++++++++-------- 5 files changed, 186 insertions(+), 125 deletions(-) diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 57debc6a7d2..d1e171656f5 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -1147,7 +1147,11 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (vmSpec != null) { vifr.otherConfig.put("cloudstack-vm-id", vmSpec.getUuid()); } + + // OVS plugin looks at network UUID in the vif 'otherconfig' details to group VIF's & tunnel ports as part of tier + // when bridge is setup for distributed routing vifr.otherConfig.put("cloudstack-network-id", nic.getNetworkUuid()); + vifr.network = getNetwork(conn, nic); if (nic.getNetworkRateMbps() != null && nic.getNetworkRateMbps().intValue() != -1) { @@ -5285,7 +5289,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); try { Network nw = findOrCreateTunnelNetwork(conn, cmd.getBridgeName()); - String bridgeName = nw.getBridge(conn);; + String bridgeName = nw.getBridge(conn); String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_network_topology", "bridge", bridgeName, "config", cmd.getVpcConfigInJson(), "host-id", ((Long)cmd.getHostId()).toString()); if (result.startsWith("SUCCESS")) { @@ -5302,8 +5306,11 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe public Answer execute(OvsVpcRoutingPolicyConfigCommand cmd) { Connection conn = getConnection(); try { + Network nw = findOrCreateTunnelNetwork(conn, cmd.getBridgeName()); + String bridgeName = nw.getBridge(conn); + String result = callHostPlugin(conn, "ovstunnel", "configure_ovs_bridge_for_routing_policies", "bridge", - cmd.getBridgeName(), "host-id", ((Long)cmd.getHostId()).toString(), "config", + bridgeName, "host-id", ((Long)cmd.getHostId()).toString(), "config", cmd.getVpcConfigInJson()); if (result.startsWith("SUCCESS")) { return new Answer(cmd, true, result); diff --git a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java index 9d2efe65bd6..de7410889f4 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/guru/OvsGuestNetworkGuru.java @@ -151,6 +151,7 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { implemented.setBroadcastDomainType(BroadcastDomainType.Vswitch); + // for the networks that are part of VPC enabled for distributed routing use scheme vs://vpcid.GRE key for network if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { String keyStr = BroadcastDomainType.getValue(implemented.getBroadcastUri()); Long vpcid= network.getVpcId(); diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java index 740df80ceb3..ab08d2629fc 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java @@ -91,6 +91,9 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vpcHostIds; } + /** + * get the list of VPC id's of the vpc's for which one or more VM's from the VPC are running on the host + */ @Override public List getVpcOnHost(long hostId) { List vpcIds = new ArrayList<>(); @@ -109,6 +112,9 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vpcIds; } + /** + * get the list of all active Vm id's in a network + */ @Override public List getAllActiveVmsInNetwork(long networkId) { List vmIds = new ArrayList<>(); @@ -117,7 +123,6 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor VirtualMachine.State.Migrating); // Find routers for the network List routers = _routerDao.findByNetwork(networkId); - List ins = new ArrayList(); if (vms != null) { for (UserVmVO vm : vms) { @@ -132,6 +137,9 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vmIds; } + /** + * get the list of all active Vm id's in the VPC for all ther tiers + */ @Override public List getAllActiveVmsInVpc(long vpcId) { @@ -148,6 +156,9 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vmIds; } + /** + * get the list of all Vm id's in the VPC for all the tiers that are running on the host + */ @Override public List getActiveVmsInVpcOnHost(long vpcId, long hostId) { Set vmIdsSet = new HashSet<>(); @@ -163,6 +174,9 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vmIds; } + /** + * get the list of all Vm id's in the network that are running on the host + */ @Override public List getActiveVmsInNetworkOnHost(long networkId, long hostId) { List vmIds = new ArrayList<>(); @@ -171,7 +185,6 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor VirtualMachine.State.Migrating); // Find routers for the network List routers = _routerDao.findByNetwork(networkId); - List ins = new ArrayList(); if (vms != null) { for (UserVmVO vm : vms) { @@ -188,13 +201,15 @@ public class OvsNetworkTopologyGuruImpl extends ManagerBase implements OvsNetwor return vmIds; } + /** + * get the list of all Vpc id's in which, a VM has a nic in the network that is part of VPC + */ @Override public List getVpcIdsVmIsPartOf(long vmId) { List vpcIds = new ArrayList<>(); List nics = _nicDao.listByVmId(vmId); if (nics == null) return null; - for (Nic nic: nics) { Network network = _networkDao.findById(nic.getNetworkId()); if (network != null && network.getTrafficType() == Networks.TrafficType.Guest && network.getVpcId() != null) { diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 30088aada4a..21e90581b84 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -665,7 +665,6 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage public boolean postStateTransitionEvent(VirtualMachine.State oldState, VirtualMachine.Event event, VirtualMachine.State newState, VirtualMachine vm, boolean status, Object opaque) { - if (!status) { return false; } @@ -720,7 +719,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage public boolean sendVpcTopologyChangeUpdate(OvsVpcPhysicalTopologyConfigCommand updateCmd, long hostId, String bridgeName) { try { - s_logger.debug("Sending VPC topology update to the host " + hostId); + s_logger.debug("Sending VPC topology change update to the host " + hostId); updateCmd.setHostId(hostId); updateCmd.setBridgeName(bridgeName); Answer ans = _agentMgr.send(hostId, updateCmd); @@ -732,7 +731,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage return false; } } catch (Exception e) { - s_logger.debug("Failed to updated the host " + hostId + " with latest VPC topology." ); + s_logger.debug("Failed to updated the host " + hostId + " with latest VPC topology.", e ); return false; } } @@ -797,6 +796,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage vmInstance.getHostId(), vmNics.toArray(new OvsVpcPhysicalTopologyConfigCommand.Nic[vmNics.size()])); vms.add(vm); } + return new OvsVpcPhysicalTopologyConfigCommand( hosts.toArray(new OvsVpcPhysicalTopologyConfigCommand.Host[hosts.size()]), tiers.toArray(new OvsVpcPhysicalTopologyConfigCommand.Tier[tiers.size()]), @@ -804,47 +804,58 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage vpc.getCidr()); } - // Subscriber to ACL replace events. On acl replace event, if the vpc is enabled for distributed routing - // send the ACL update to all the hosts on which VPC spans + // Subscriber to ACL replace events. On acl replace event, if the vpc for the tier is enabled for + // distributed routing send the ACL update to all the hosts on which VPC spans public class NetworkAclEventsSubscriber implements MessageSubscriber { @Override public void onPublishMessage(String senderAddress, String subject, Object args) { - NetworkVO network = (NetworkVO) args; - String bridgeName=generateBridgeNameForVpc(network.getVpcId()); - if (network.getVpcId() != null & isVpcEnabledForDistributedRouter(network.getVpcId())) { - long vpcId = network.getVpcId(); - OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); - List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); - for (Long id: vpcSpannedHostIds) { - if (!sendVpcRoutingPolicyChangeUpdate(cmd, id, bridgeName)) { - s_logger.debug("Failed to send VPC routing policy change update to host : " + id + - ". But moving on with sending the host updates to the rest of the hosts."); + try { + NetworkVO network = (NetworkVO) args; + String bridgeName=generateBridgeNameForVpc(network.getVpcId()); + if (network.getVpcId() != null & isVpcEnabledForDistributedRouter(network.getVpcId())) { + long vpcId = network.getVpcId(); + OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); + List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + for (Long id: vpcSpannedHostIds) { + if (!sendVpcRoutingPolicyChangeUpdate(cmd, id, bridgeName)) { + s_logger.debug("Failed to send VPC routing policy change update to host : " + id + + ". But moving on with sending the updates to the rest of the hosts."); + } } } + } catch (Exception e) { + s_logger.debug("Failed to send VPC routing policy change updates all hosts in vpc", e); } } } private OvsVpcRoutingPolicyConfigCommand prepareVpcRoutingPolicyUpdate(long vpcId) { - VpcVO vpc = _vpcDao.findById(vpcId); - assert (vpc != null): "invalid vpc id"; + List acls = new ArrayList<>(); List tiers = new ArrayList<>(); + VpcVO vpc = _vpcDao.findById(vpcId); List vpcNetworks = _vpcMgr.getVpcNetworks(vpcId); + assert (vpc != null && (vpcNetworks != null && !vpcNetworks.isEmpty())): "invalid vpc id"; + for (Network network : vpcNetworks) { Long networkAclId = network.getNetworkACLId(); + if (networkAclId == null) + continue; NetworkACLVO networkAcl = _networkACLDao.findById(networkAclId); List aclItems = new ArrayList<>(); List aclItemVos = _networkACLItemDao.listByACL(networkAclId); for (NetworkACLItemVO aclItem : aclItemVos) { String[] sourceCidrs = aclItem.getSourceCidrList().toArray(new String[aclItem.getSourceCidrList().size()]); + aclItems.add(new OvsVpcRoutingPolicyConfigCommand.AclItem( aclItem.getNumber(), aclItem.getUuid(), aclItem.getAction().name(), aclItem.getTrafficType().name(), - aclItem.getSourcePortStart().toString(), aclItem.getSourcePortEnd().toString(), - aclItem.getProtocol(), sourceCidrs)); + ((aclItem.getSourcePortStart() != null) ?aclItem.getSourcePortStart().toString() :null), + ((aclItem.getSourcePortEnd() != null) ?aclItem.getSourcePortEnd().toString() :null), + aclItem.getProtocol(), + sourceCidrs)); } OvsVpcRoutingPolicyConfigCommand.Acl acl = new OvsVpcRoutingPolicyConfigCommand.Acl(networkAcl.getUuid(), @@ -862,10 +873,9 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage return cmd; } - public boolean sendVpcRoutingPolicyChangeUpdate(OvsVpcRoutingPolicyConfigCommand updateCmd, long hostId, String bridgeName) { try { - s_logger.debug("Sending VPC routing policy change update to the host " + hostId); + s_logger.debug("Sending VPC routing policies change update to the host " + hostId); updateCmd.setHostId(hostId); updateCmd.setBridgeName(bridgeName); Answer ans = _agentMgr.send(hostId, updateCmd); @@ -873,11 +883,11 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage s_logger.debug("Successfully updated the host " + hostId + " with latest VPC routing policies." ); return true; } else { - s_logger.debug("Failed to update the host " + hostId + " with latest routing policy." ); + s_logger.debug("Failed to update the host " + hostId + " with latest routing policies." ); return false; } } catch (Exception e) { - s_logger.debug("Failed to updated the host " + hostId + " with latest routing policy." ); + s_logger.debug("Failed to updated the host " + hostId + " with latest routing policies due to" , e ); return false; } } diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index 1c9d513571e..4ebb435a823 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -321,62 +321,66 @@ def configure_bridge_for_network_topology(bridge, this_host_id, json_config): logging.debug("WARNING:Can't find VPC info in json config file") return "FAILURE:IMPROPER_JSON_CONFG_FILE" - # get the list of Vm's in the VPC from the JSON config - this_host_vms = get_vms_on_host(vpconfig, this_host_id) + try: + # get the list of Vm's in the VPC from the JSON config + this_host_vms = get_vms_on_host(vpconfig, this_host_id) - for vm in this_host_vms: - for nic in vm.nics: - mac_addr = nic.macaddress - ip = nic.ipaddress - vif_name = get_vif_name_from_macaddress(mac_addr) - of_port = get_ofport_for_vif(vif_name) - network = get_network_details(vpconfig, nic.networkuuid) - - # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet on the found OFPORT - add_mac_lookup_table_entry(bridge, mac_addr, of_port) - - # Add flow rule in L3 look up table: if the destination IP = VM's IP then modify the packet - # to set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table - add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) - - # Add flow entry to send with intra tier traffic from the NIC to L2 lookup path) - action_str = "table=0, in_port=%s," %of_port + " ip, nw_dst=%s," %network.cidr + " actions=resubmit(,1)" - addflow = [OFCTL_PATH, "add-flow", bridge, action_str] - do_cmd(addflow) - - #add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path) - action_str = "table=0, in_port=%s," % of_port + " ip, dl_dst=%s," %network.gatewaymac +\ - "nw_dst=%s," %vpconfig.cidr + "actions=resubmit(,3)" - addflow = [OFCTL_PATH, "add-flow", bridge, action_str] - - do_cmd(addflow) - - # get the list of hosts on which VPC spans from the JSON config - vpc_spanning_hosts = vpconfig.hosts - - for host in vpc_spanning_hosts: - if str(this_host_id) == str(host.hostid): - continue - other_host_vms = get_vms_on_host(vpconfig, host.hostid) - for vm in other_host_vms: + for vm in this_host_vms: for nic in vm.nics: mac_addr = nic.macaddress ip = nic.ipaddress + vif_name = get_vif_name_from_macaddress(mac_addr) + of_port = get_ofport_for_vif(vif_name) network = get_network_details(vpconfig, nic.networkuuid) - gre_key = network.grekey - # generate tunnel name from tunnel naming convention - tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid) - of_port = get_ofport_for_vif(tunnel_name) - - # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet tunnel port + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet on the found OFPORT add_mac_lookup_table_entry(bridge, mac_addr, of_port) - # Add flow tule in L3 look up table: if the destination IP = VM's IP then modify the packet - # set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + # Add flow rule in L3 look up table: if the destination IP = VM's IP then modify the packet + # to set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) - return "SUCCESS: successfully configured bridge as per the VPC topology" + # Add flow entry to send with intra tier traffic from the NIC to L2 lookup path) + action_str = "table=0, in_port=%s," %of_port + " ip, nw_dst=%s," %network.cidr + " actions=resubmit(,1)" + addflow = [OFCTL_PATH, "add-flow", bridge, action_str] + do_cmd(addflow) + + #add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path) + action_str = "table=0, in_port=%s," % of_port + " ip, dl_dst=%s," %network.gatewaymac +\ + "nw_dst=%s," %vpconfig.cidr + "actions=resubmit(,3)" + addflow = [OFCTL_PATH, "add-flow", bridge, action_str] + + do_cmd(addflow) + + # get the list of hosts on which VPC spans from the JSON config + vpc_spanning_hosts = vpconfig.hosts + + for host in vpc_spanning_hosts: + if str(this_host_id) == str(host.hostid): + continue + other_host_vms = get_vms_on_host(vpconfig, host.hostid) + for vm in other_host_vms: + for nic in vm.nics: + mac_addr = nic.macaddress + ip = nic.ipaddress + network = get_network_details(vpconfig, nic.networkuuid) + gre_key = network.grekey + + # generate tunnel name from tunnel naming convention + tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid) + of_port = get_ofport_for_vif(tunnel_name) + + # Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet tunnel port + add_mac_lookup_table_entry(bridge, mac_addr, of_port) + + # Add flow tule in L3 look up table: if the destination IP = VM's IP then modify the packet + # set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table + add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr) + + return "SUCCESS: successfully configured bridge as per the VPC topology" + except: + logging.debug("An unexpected error occurred while configuring bridge as per VPC topology.") + raise def get_acl(vpcconfig, required_acl_id): acls = vpcconfig.acls @@ -392,60 +396,84 @@ def configure_ovs_bridge_for_routing_policies(bridge, json_config): logging.debug("WARNING:Can't find VPC info in json config file") return "FAILURE:IMPROPER_JSON_CONFG_FILE" - # First flush current egress ACL's before re-applying the ACL's - del_flows(bridge, table=3) + try: + # First flush current egress ACL's before re-applying the ACL's + del_flows(bridge, table=3) - egress_rules_added = False - ingress_rules_added = False + egress_rules_added = False + ingress_rules_added = False - tiers = vpconfig.tiers - for tier in tiers: - tier_cidr = tier.cidr - acl = get_acl(vpconfig, tier.aclid) - acl_items = acl.aclitems + tiers = vpconfig.tiers + for tier in tiers: + tier_cidr = tier.cidr + acl = get_acl(vpconfig, tier.aclid) + acl_items = acl.aclitems - for acl_item in acl_items: - number = acl_item.number - action = acl_item.action - direction = acl_item.direction - source_port_start = acl_item.sourceportstart - source_port_end = acl_item.sourceportend - protocol = acl_item.protocol - source_cidrs = acl_item.sourcecidrs - acl_priority = 1000 + number - for source_cidr in source_cidrs: - if direction is "ingress": - ingress_rules_added = True - # add flow rule to do action (allow/deny) for flows where source IP of the packet is in - # source_cidr and destination ip is in tier_cidr - port = source_port_start - while (port < source_port_end): - if action is "deny": - add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, - nw_proto=protocol, actions='drop') - if action is "allow": - add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, - nw_proto=protocol, actions='resubmit(,1)') - port = port + 1 + for acl_item in acl_items: + number = acl_item.number + action = acl_item.action + direction = acl_item.direction + source_port_start = acl_item.sourceportstart + source_port_end = acl_item.sourceportend + protocol = acl_item.protocol + source_cidrs = acl_item.sourcecidrs + acl_priority = 1000 + number + for source_cidr in source_cidrs: + if direction is "ingress": + ingress_rules_added = True - elif direction in "egress": - egress_rules_added = True - # add flow rule to do action (allow/deny) for flows where destination IP of the packet is in - # source_cidr and source ip is in tier_cidr - port = source_port_start - while (port < source_port_end): - if action is "deny": - add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, - nw_proto=protocol, actions='drop') - if action is "allow": - add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, - nw_proto=protocol, actions='resubmit(,1)') - port = port + 1 + if source_port_start is None and source_port_end is None: + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, + nw_proto=protocol, actions='resubmit(,1)') + continue - if egress_rules_added is False: - # add a default rule in egress table to forward packet to L3 lookup table - add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + # add flow rule to do action (allow/deny) for flows where source IP of the packet is in + # source_cidr and destination ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 - if ingress_rules_added is False: - # add a default rule in egress table drop packets - add_flow(bridge, priority=0, table=5, actions='drop') \ No newline at end of file + elif direction in "egress": + egress_rules_added = True + + if source_port_start is None and source_port_end is None: + if action is "deny": + add_flow(bridge, priority= acl_priority, table=3, nw_src=source_cidr, nw_dst=tier_cidr, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority,table=3, nw_src=source_cidr, nw_dst=tier_cidr, + nw_proto=protocol, actions='resubmit(,1)') + continue + + # add flow rule to do action (allow/deny) for flows where destination IP of the packet is in + # source_cidr and source ip is in tier_cidr + port = source_port_start + while (port < source_port_end): + if action is "deny": + add_flow(bridge, priority= acl_priority, table=3, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='drop') + if action is "allow": + add_flow(bridge, priority= acl_priority, table=3, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port, + nw_proto=protocol, actions='resubmit(,1)') + port = port + 1 + + if egress_rules_added is False: + # add a default rule in egress table to forward packet to L3 lookup table + add_flow(bridge, priority=0, table=3, actions='resubmit(,4)') + + if ingress_rules_added is False: + # add a default rule in egress table drop packets + add_flow(bridge, priority=0, table=5, actions='drop') + except: + logging.debug("An unexpected error occurred while configuring bridge as per VPC's routing policies.") + raise \ No newline at end of file From 81f6e668084b732c78989d79f6b4ee22f9355a40 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 14 Mar 2014 16:26:52 +0530 Subject: [PATCH 19/31] fix RAT check failure --- .../network/ovs/OvsNetworkTopologyGuruImpl.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java index ab08d2629fc..402a9c8f521 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsNetworkTopologyGuruImpl.java @@ -1,3 +1,20 @@ +// 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.ovs; import com.cloud.network.Network; From 7d20b08aa9a05e20079e5c21ac7950f2b8879507 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 14 Mar 2014 16:54:12 +0530 Subject: [PATCH 20/31] findbug fixes --- .../ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 21e90581b84..53607fc9410 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -531,7 +531,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } } } catch (Exception e) { - s_logger.warn(String.format("Destroy tunnel failed", e)); + s_logger.warn("Destroy tunnel failed", e); } } } @@ -812,7 +812,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage try { NetworkVO network = (NetworkVO) args; String bridgeName=generateBridgeNameForVpc(network.getVpcId()); - if (network.getVpcId() != null & isVpcEnabledForDistributedRouter(network.getVpcId())) { + if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { long vpcId = network.getVpcId(); OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); From 794b38a789a68e539dbecfc76bc861e959730411 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 14 Mar 2014 17:19:27 +0530 Subject: [PATCH 21/31] fix unit-test failure --- server/test/com/cloud/vpc/NetworkACLManagerTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/test/com/cloud/vpc/NetworkACLManagerTest.java b/server/test/com/cloud/vpc/NetworkACLManagerTest.java index f812a551a52..ff38b5e85c0 100644 --- a/server/test/com/cloud/vpc/NetworkACLManagerTest.java +++ b/server/test/com/cloud/vpc/NetworkACLManagerTest.java @@ -26,6 +26,7 @@ import junit.framework.TestCase; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.test.utils.SpringUtils; import org.junit.After; import org.junit.Before; @@ -308,6 +309,11 @@ public class NetworkACLManagerTest extends TestCase { return Mockito.mock(VpcService.class); } + @Bean + public MessageBus messageBus() { + return Mockito.mock(MessageBus.class); + } + public static class Library implements TypeFilter { @Override public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { From 1b4325d2c88d49b394256f2130c509eebfe9bf36 Mon Sep 17 00:00:00 2001 From: Rajesh Battala Date: Wed, 12 Mar 2014 07:26:08 +0530 Subject: [PATCH 22/31] CLOUDSTACK-6106 supporting VPC VR on Hyper-V --- .../agent/api/ModifyVmNicConfigCommand.java | 7 + .../hypervisor/hyperv/guru/HypervGuru.java | 45 ++++-- .../resource/HypervDirectConnectResource.java | 138 +++++++++++++++--- .../com/cloud/network/vpc/VpcManagerImpl.java | 1 + 4 files changed, 158 insertions(+), 33 deletions(-) diff --git a/core/src/com/cloud/agent/api/ModifyVmNicConfigCommand.java b/core/src/com/cloud/agent/api/ModifyVmNicConfigCommand.java index 0230beca732..5f4f0e1d9ac 100644 --- a/core/src/com/cloud/agent/api/ModifyVmNicConfigCommand.java +++ b/core/src/com/cloud/agent/api/ModifyVmNicConfigCommand.java @@ -21,6 +21,7 @@ public class ModifyVmNicConfigCommand extends Command { String vmName; int vlan; String macAddress; + int index; protected ModifyVmNicConfigCommand() { } @@ -30,6 +31,12 @@ public class ModifyVmNicConfigCommand extends Command { this.macAddress = macAddress; } + public ModifyVmNicConfigCommand(String vmName, int vlan, int position) { + this.vmName = vmName; + this.vlan = vlan; + this.index = position; + } + public String getVmName() { return vmName; } diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java index bf0795df059..4e8d3d5524c 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java @@ -34,6 +34,7 @@ import com.cloud.storage.dao.GuestOSDao; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.hypervisor.hyperv.manager.HypervManager; import com.cloud.network.NetworkModel; +import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; @@ -74,16 +75,20 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { if(vm.getVirtualMachine().getType() == VirtualMachine.Type.DomainRouter) { NicProfile publicNicProfile = null; + NicProfile controlNicProfile = null; + NicProfile profile = null; for(NicProfile nicProfile : nicProfiles) { if(nicProfile.getTrafficType() == TrafficType.Public) { publicNicProfile = nicProfile; break; } + else if (nicProfile.getTrafficType() == TrafficType.Control) { + controlNicProfile = nicProfile; + } } - if(publicNicProfile != null) { + if(publicNicProfile != null || controlNicProfile != null) { NicTO[] nics = to.getNics(); - // reserve extra NICs NicTO[] expandedNics = new NicTO[MaxNicSupported]; int i = 0; @@ -95,18 +100,27 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { } deviceId++; - long networkId = publicNicProfile.getNetworkId(); + long networkId = 0; + if(publicNicProfile != null ) { + networkId= publicNicProfile.getNetworkId(); + profile = publicNicProfile; + } + else { + networkId = controlNicProfile.getNetworkId(); + profile = controlNicProfile; + } + NetworkVO network = _networkDao.findById(networkId); // for Hyperv Hot Nic plug is not supported and it will support upto 8 nics. // creating the VR with extra nics (actual nics(3) + extra nics) will be 8 for(; i < MaxNicSupported; i++) { NicTO nicTo = new NicTO(); nicTo.setDeviceId(deviceId++); - nicTo.setBroadcastType(publicNicProfile.getBroadcastType()); - nicTo.setType(publicNicProfile.getTrafficType()); + nicTo.setBroadcastType(BroadcastDomainType.Vlan); + nicTo.setType(TrafficType.Public); nicTo.setIp("0.0.0.0"); nicTo.setNetmask("255.255.255.255"); - nicTo.setName(publicNicProfile.getName()); + nicTo.setName(profile.getName()); try { String mac = _networkMgr.getNextAvailableMacAddressInNetwork(networkId); @@ -114,16 +128,16 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { } catch (InsufficientAddressCapacityException e) { throw new CloudRuntimeException("unable to allocate mac address on network: " + networkId); } - nicTo.setDns1(publicNicProfile.getDns1()); - nicTo.setDns2(publicNicProfile.getDns2()); - if (publicNicProfile.getGateway() != null) { + nicTo.setDns1(profile.getDns1()); + nicTo.setDns2(profile.getDns2()); + if (publicNicProfile != null && publicNicProfile.getGateway() != null) { nicTo.setGateway(publicNicProfile.getGateway()); } else { nicTo.setGateway(network.getGateway()); } nicTo.setDefaultNic(false); - nicTo.setBroadcastUri(publicNicProfile.getBroadCastUri()); - nicTo.setIsolationuri(publicNicProfile.getIsolationUri()); + nicTo.setBroadcastUri(profile.getBroadCastUri()); + nicTo.setIsolationuri(profile.getIsolationUri()); Integer networkRate = _networkMgr.getNetworkRate(network.getId(), null); nicTo.setNetworkRateMbps(networkRate); @@ -137,9 +151,12 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { for(NicTO nicTo : sortNicsByDeviceId(to.getNics())) { sbMacSequence.append(nicTo.getMac()).append("|"); } - sbMacSequence.deleteCharAt(sbMacSequence.length() - 1); - String bootArgs = to.getBootArgs(); - to.setBootArgs(bootArgs + " nic_macs=" + sbMacSequence.toString()); + + if (!sbMacSequence.toString().isEmpty()) { + sbMacSequence.deleteCharAt(sbMacSequence.length() - 1); + String bootArgs = to.getBootArgs(); + to.setBootArgs(bootArgs + " nic_macs=" + sbMacSequence.toString()); + } } diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index a3ffa758f8e..b1f5a6754d0 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -83,10 +83,14 @@ import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PlugNicAnswer; +import com.cloud.agent.api.PlugNicCommand; import com.cloud.agent.api.SetupGuestNetworkCommand; import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.UnPlugNicAnswer; +import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.StartupRoutingCommand.VmState; import com.cloud.agent.api.StartupStorageCommand; import com.cloud.agent.api.UnsupportedAnswer; @@ -172,7 +176,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S protected final int _retry = 24; protected final int _sleep = 10000; protected static final int DEFAULT_DOMR_SSHPORT = 3922; - + private final int maxid = 4094; private String _clusterGuid; // Used by initialize to assert object configured before @@ -477,7 +481,12 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S answer = execute((SetStaticRouteCommand) cmd); } else if (clazz == SetMonitorServiceCommand.class) { answer = execute((SetMonitorServiceCommand) cmd); - } else { + } else if (clazz == PlugNicCommand.class) { + answer = execute((PlugNicCommand)cmd); + } else if (clazz == UnPlugNicCommand.class) { + answer = execute((UnPlugNicCommand)cmd); + } + else { if (clazz == StartCommand.class) { VirtualMachineTO vmSpec = ((StartCommand)cmd).getVirtualMachine(); if (vmSpec.getType() != VirtualMachine.Type.User) { @@ -509,6 +518,59 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S return answer; } + + private PlugNicAnswer execute(PlugNicCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource PlugNicCommand " + s_gson.toJson(cmd)); + } + + try { + + String vmName = cmd.getVmName(); + NicTO nic = cmd.getNic(); + URI broadcastUri = nic.getBroadcastUri(); + if (BroadcastDomainType.getSchemeValue(broadcastUri) != BroadcastDomainType.Vlan) { + throw new InternalErrorException("Unable to assign a public IP to a VIF on network " + nic.getBroadcastUri()); + } + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int publicNicInfo = -1; + publicNicInfo = getVmNics(vmName, maxid); + if (publicNicInfo > 0) { + modifyNicVlan(vmName, vlanId, publicNicInfo); + } + return new PlugNicAnswer(cmd, true, "success"); + } catch (Exception e) { + s_logger.error("Unexpected exception: ", e); + return new PlugNicAnswer(cmd, false, "Unable to execute PlugNicCommand due to " + e.toString()); + } + } + + + private UnPlugNicAnswer execute(UnPlugNicCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource UnPlugNicCommand " + s_gson.toJson(cmd)); + } + + try { + String vmName = cmd.getVmName(); + NicTO nic = cmd.getNic(); + URI broadcastUri = nic.getBroadcastUri(); + if (BroadcastDomainType.getSchemeValue(broadcastUri) != BroadcastDomainType.Vlan) { + throw new InternalErrorException("Unable to unassign a public IP to a VIF on network " + nic.getBroadcastUri()); + } + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int publicNicInfo = -1; + publicNicInfo = getVmNics(vmName, vlanId); + if (publicNicInfo > 0) { + modifyNicVlan(vmName, maxid, publicNicInfo); + } + return new UnPlugNicAnswer(cmd, true, "success"); + } catch (Exception e) { + s_logger.error("Unexpected exception: ", e); + return new UnPlugNicAnswer(cmd, false, "Unable to execute unPlugNicCommand due to " + e.toString()); + } + } + @Override public ExecutionResult executeInVR(String routerIP, String script, String args) { Pair result; @@ -621,14 +683,18 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S protected ExecutionResult prepareNetworkElementCommand(SetupGuestNetworkCommand cmd) { NicTO nic = cmd.getNic(); - String routerIp = getRouterSshControlIp(cmd); String domrName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); try { - int ethDeviceNum = findRouterEthDeviceIndex(domrName, routerIp, - nic.getMac()); - nic.setDeviceId(ethDeviceNum); + URI broadcastUri = nic.getBroadcastUri(); + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int ethDeviceNum = getVmNics(domrName, vlanId); + if (ethDeviceNum > 0) { + nic.setDeviceId(ethDeviceNum); + } else { + return new ExecutionResult(false, "Prepare SetupGuestNetwork failed due to unable to find the nic"); + } } catch (Exception e) { String msg = "Prepare SetupGuestNetwork failed due to " + e.toString(); s_logger.warn(msg, e); @@ -640,23 +706,27 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S private ExecutionResult prepareNetworkElementCommand(IpAssocVpcCommand cmd) { String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); - String routerIp = getRouterSshControlIp(cmd); try { IpAddressTO[] ips = cmd.getIpAddresses(); for (IpAddressTO ip : ips) { - - int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, ip.getVifMacAddress()); - if (ethDeviceNum < 0) { + URI broadcastUri = BroadcastDomainType.fromString(ip.getBroadcastUri()); + if (BroadcastDomainType.getSchemeValue(broadcastUri) != BroadcastDomainType.Vlan) { + throw new InternalErrorException("Invalid Broadcast URI " + ip.getBroadcastUri()); + } + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int publicNicInfo = -1; + publicNicInfo = getVmNics(routerName, vlanId); + if (publicNicInfo < 0) { if (ip.isAdd()) { throw new InternalErrorException("Failed to find DomR VIF to associate/disassociate IP with."); - } else { + } else { s_logger.debug("VIF to deassociate IP with does not exist, return success"); continue; } } - ip.setNicDevId(ethDeviceNum); + ip.setNicDevId(publicNicInfo); } } catch (Exception e) { s_logger.error("Prepare Ip Assoc failure on applying one ip due to exception: ", e); @@ -668,12 +738,17 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S protected ExecutionResult prepareNetworkElementCommand(SetSourceNatCommand cmd) { String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); - String routerIp = getRouterSshControlIp(cmd); IpAddressTO pubIp = cmd.getIpAddress(); try { - int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, pubIp.getVifMacAddress()); - pubIp.setNicDevId(ethDeviceNum); + String broadcastUri = pubIp.getBroadcastUri(); + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int ethDeviceNum = getVmNics(routerName, vlanId); + if (ethDeviceNum > 0) { + pubIp.setNicDevId(ethDeviceNum); + } else { + return new ExecutionResult(false, "Prepare Ip SNAT failed due to unable to find the nic"); + } } catch (Exception e) { String msg = "Prepare Ip SNAT failure due to " + e.toString(); s_logger.error(msg, e); @@ -686,12 +761,16 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S NicTO nic = cmd.getNic(); String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); - String routerIp = getRouterSshControlIp(cmd); try { - int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, - nic.getMac()); - nic.setDeviceId(ethDeviceNum); + URI broadcastUri = nic.getBroadcastUri(); + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int ethDeviceNum = getVmNics(routerName, vlanId); + if (ethDeviceNum > 0) { + nic.setDeviceId(ethDeviceNum); + } else { + return new ExecutionResult(false, "Prepare SetNetworkACL failed due to unable to find the nic"); + } } catch (Exception e) { String msg = "Prepare SetNetworkACL failed due to " + e.toString(); s_logger.error(msg, e); @@ -1730,6 +1809,27 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } } + protected void modifyNicVlan(String vmName, int vlanId, int pos) { + ModifyVmNicConfigCommand modifynic = new ModifyVmNicConfigCommand(vmName, vlanId, pos); + URI agentUri = null; + try { + String cmdName = ModifyVmNicConfigCommand.class.getName(); + agentUri = + new URI("https", null, _agentIp, _port, + "/api/HypervResource/" + cmdName, null, null); + } catch (URISyntaxException e) { + String errMsg = "Could not generate URI for Hyper-V agent"; + s_logger.error(errMsg, e); + } + String ansStr = postHttpRequest(s_gson.toJson(modifynic), agentUri); + Answer[] result = s_gson.fromJson(ansStr, Answer[].class); + s_logger.debug("executeRequest received response " + + s_gson.toJson(result)); + if (result.length > 0) { + ModifyVmNicConfigAnswer ans = ((ModifyVmNicConfigAnswer)result[0]); + } + } + protected void assignPublicIpAddress(final String vmName, final String privateIpAddress, final String publicIpAddress, final boolean add, final boolean firstIP, final boolean sourceNat, final String broadcastId, final String vlanGateway, final String vlanNetmask, final String vifMacAddress) throws Exception { diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index c5a048c25cc..2e63639c33d 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -2289,6 +2289,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis hTypes.add(HypervisorType.KVM); hTypes.add(HypervisorType.Simulator); hTypes.add(HypervisorType.LXC); + hTypes.add(HypervisorType.Hyperv); return hTypes; } From 4523f5d8de64761a507b5a8f1f9b4d451a8be17a Mon Sep 17 00:00:00 2001 From: Rajesh Battala Date: Wed, 12 Mar 2014 07:45:50 +0530 Subject: [PATCH 23/31] CLOUDSTACK-6106 Agent side changes for VPC on Hyper-V --- .../HypervResourceController.cs | 14 +++++++--- .../HypervResource/IWmiCallsV2.cs | 1 + .../HypervResource/WmiCallsV2.cs | 26 ++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index 2d447532d7f..40609e4e857 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -992,6 +992,7 @@ namespace HypervResource using (log4net.NDC.Push(Guid.NewGuid().ToString())) { logger.Info(CloudStackTypes.PlugNicCommand + cmd.ToString()); + object ansContent = new { result = true, @@ -1299,9 +1300,16 @@ namespace HypervResource String vmName = cmd.vmName; uint vlan = (uint)cmd.vlan; string macAddress = cmd.macAddress; - wmiCallsV2.ModifyVmVLan(vmName, vlan, macAddress); - - result = true; + uint pos = cmd.index; + if (macAddress != null) + { + wmiCallsV2.ModifyVmVLan(vmName, vlan, macAddress); + } + else if (pos > 1) + { + wmiCallsV2.ModifyVmVLan(vmName, vlan, pos); + } + result = true; object ansContent = new { diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 601889646bd..8c682ad68b2 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -70,5 +70,6 @@ namespace HypervResource void SetState(ComputerSystem vm, ushort requiredState); Dictionary GetVmSync(String privateIpAddress); void ModifyVmVLan(string vmName, uint vlanid, string mac); + void ModifyVmVLan(string vmName, uint vlanid, uint pos); } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index 73156c5e3e7..d5cf7c91432 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -229,6 +229,7 @@ namespace HypervResource string errMsg = vmName; var diskDrives = vmInfo.disks; var bootArgs = vmInfo.bootArgs; + string defaultvlan = "4094"; // assert errMsg = vmName + ": missing disk information, array empty or missing, agent expects *at least* one disk for a VM"; @@ -391,6 +392,8 @@ namespace HypervResource string vlan = null; string isolationUri = nic.isolationUri; string broadcastUri = nic.broadcastUri; + string nicIp = nic.ip; + string nicNetmask = nic.netmask; if ( (broadcastUri != null ) || (isolationUri != null && isolationUri.StartsWith("vlan://"))) { if (broadcastUri != null && broadcastUri.StartsWith("storage")) @@ -415,6 +418,10 @@ namespace HypervResource throw ex; } } + if(nicIp.Equals("0.0.0.0") && nicNetmask.Equals("255.255.255.255") ) { + // this is the extra nic added to VR. + vlan = defaultvlan; + } if (nicCount == 2) { @@ -913,7 +920,6 @@ namespace HypervResource ResourceAllocationSettingData defaultDiskDriveSettings = defaultDiskDriveSettingsObjs.OfType().First(); return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone()); } - // Modify the systemvm nic's VLAN id public void ModifyVmVLan(string vmName, uint vlanid, String mac) @@ -945,6 +951,24 @@ namespace HypervResource vlanSettings.LateBoundObject.GetText(TextFormat.CimDtd20)}); } + // Modify the systemvm nic's VLAN id + public void ModifyVmVLan(string vmName, uint vlanid, uint pos) + { + ComputerSystem vm = GetComputerSystem(vmName); + SyntheticEthernetPortSettingData[] nicSettingsViaVm = GetEthernetPortSettings(vm); + // Obtain controller for Hyper-V virtualisation subsystem + VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService(); + + EthernetPortAllocationSettingData[] ethernetConnections = GetEthernetConnections(vm); + EthernetSwitchPortVlanSettingData vlanSettings = GetVlanSettings(ethernetConnections[pos]); + + //Assign configuration to new NIC + vlanSettings.LateBoundObject["AccessVlanId"] = vlanid; + vlanSettings.LateBoundObject["OperationMode"] = 1; + ModifyFeatureVmResources(vmMgmtSvc, vm, new String[] { + vlanSettings.LateBoundObject.GetText(TextFormat.CimDtd20)}); + } + public void AttachIso(string displayName, string iso) { logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName); From 6a4927f660f776bcbd12ae45f4e63ae2c2e96774 Mon Sep 17 00:00:00 2001 From: Rajesh Battala Date: Wed, 12 Mar 2014 11:32:44 +0530 Subject: [PATCH 24/31] Fixed all findbugs in hyperv plugin code --- .../hyperv/src/com/cloud/ha/HypervInvestigator.java | 4 ++-- .../hyperv/discoverer/HypervServerDiscoverer.java | 5 +++-- .../hyperv/resource/HypervDirectConnectResource.java | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java b/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java index cebfb7ac70c..01d75fa64ce 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java @@ -45,9 +45,9 @@ public class HypervInvestigator extends AdapterBase implements Investigator { public Boolean isVmAlive(com.cloud.vm.VirtualMachine vm, Host host) { Status status = isAgentAlive(host); if (status == null) { - return null; + return false; } - return status == Status.Up ? true : null; + return status == Status.Up ? true : false; } @Override diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java index c3d79c55ea6..f4c15f62ce2 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java @@ -19,6 +19,7 @@ package com.cloud.hypervisor.hyperv.discoverer; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -254,7 +255,7 @@ public class HypervServerDiscoverer extends DiscovererBase implements Discoverer // pool in the database // This GUID may change. if (cluster.getGuid() == null) { - cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes()).toString()); + cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes(Charset.forName("UTF-8"))).toString()); _clusterDao.update(clusterId, cluster); } @@ -318,7 +319,7 @@ public class HypervServerDiscoverer extends DiscovererBase implements Discoverer * @return GUID in form of a string. */ public static String calcServerResourceGuid(final String uuidSeed) { - String guid = UUID.nameUUIDFromBytes(uuidSeed.getBytes()).toString(); + String guid = UUID.nameUUIDFromBytes(uuidSeed.getBytes(Charset.forName("UTF-8"))).toString(); return guid; } diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index b1f5a6754d0..13254a51542 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -24,6 +24,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; import java.rmi.RemoteException; import java.security.KeyManagementException; import java.security.KeyStoreException; @@ -597,7 +598,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S public ExecutionResult createFileInVR(String routerIp, String filePath, String fileName, String content) { File keyFile = getSystemVMKeyFile(); try { - SshHelper.scpTo(routerIp, 3922, "root", keyFile, null, filePath, content.getBytes(), fileName, null); + SshHelper.scpTo(routerIp, 3922, "root", keyFile, null, filePath, content.getBytes(Charset.forName("UTF-8")), fileName, null); } catch (Exception e) { s_logger.warn("Fail to create file " + filePath + fileName + " in VR " + routerIp, e); return new ExecutionResult(false, e.getMessage()); @@ -1262,7 +1263,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } try { - SshHelper.scpTo(controlIp, DEFAULT_DOMR_SSHPORT, "root", keyFile, null, "/tmp/", tmpCfgFileContents.toString().getBytes(), routerIp.replace('.', '_') + + SshHelper.scpTo(controlIp, DEFAULT_DOMR_SSHPORT, "root", keyFile, null, "/tmp/", tmpCfgFileContents.toString().getBytes(Charset.forName("UTF-8")), routerIp.replace('.', '_') + ".cfg", null); try { @@ -1459,7 +1460,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S String json = new Gson().toJson(data); s_logger.debug("VM data JSON IS:" + json); - json = Base64.encodeBase64String(json.getBytes()); + json = Base64.encodeBase64String(json.getBytes(Charset.forName("UTF-8"))); String args = "-d " + json; From 48f8a95b06b0348ba1349cb5434183c2c18710db Mon Sep 17 00:00:00 2001 From: Saksham Srivastava Date: Tue, 11 Mar 2014 17:52:00 +0530 Subject: [PATCH 25/31] CLOUDSTACK-6092: Storage OverProvisioning as a Per Primary Basis Allow storage.overprovisioning.factor to be specified at storape pool level. Signed-off-by: Sateesh Chodapuneedi --- .../api/response/StoragePoolResponse.java | 8 ++ .../com/cloud/capacity/CapacityManager.java | 2 +- .../api/query/dao/StoragePoolJoinDaoImpl.java | 4 +- .../capacity/StorageCapacityListener.java | 2 +- .../com/cloud/storage/StorageManagerImpl.java | 25 ++-- setup/db/db/schema-430to440.sql | 3 + .../smoke/test_over_provisioning.py | 107 ++++++++++++++++++ 7 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 test/integration/smoke/test_over_provisioning.py diff --git a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java index 03a4f349ba0..98c90e2fd39 100644 --- a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java +++ b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java @@ -107,6 +107,10 @@ public class StoragePoolResponse extends BaseResponse { @Param(description = "the scope of the storage pool") private String scope; + @SerializedName("overprovisionfactor") + @Param(description = "the overprovisionfactor for the storage pool") + private String overprovisionfactor; + @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the hypervisor type of the storage pool") private String hypervisor; @@ -301,4 +305,8 @@ public class StoragePoolResponse extends BaseResponse { public void setSuitableForMigration(Boolean suitableForMigration) { this.suitableForMigration = suitableForMigration; } + + public void setOverProvisionFactor(String overProvisionFactor) { + this.overprovisionfactor = overProvisionFactor; + } } diff --git a/engine/components-api/src/com/cloud/capacity/CapacityManager.java b/engine/components-api/src/com/cloud/capacity/CapacityManager.java index bd1a61096c0..038a2c05f01 100755 --- a/engine/components-api/src/com/cloud/capacity/CapacityManager.java +++ b/engine/components-api/src/com/cloud/capacity/CapacityManager.java @@ -44,7 +44,7 @@ public interface CapacityManager { "Percentage (as a value between 0 and 1) of storage utilization above which allocators will disable using the pool for low storage available.", true, ConfigKey.Scope.Zone); static final ConfigKey StorageOverprovisioningFactor = new ConfigKey("Storage", Double.class, StorageOverprovisioningFactorCK, "2", - "Used for storage overprovisioning calculation; available storage will be (actualStorageSize * storage.overprovisioning.factor)", true, ConfigKey.Scope.Zone); + "Used for storage overprovisioning calculation; available storage will be (actualStorageSize * storage.overprovisioning.factor)", true, ConfigKey.Scope.StoragePool); static final ConfigKey StorageAllocatedCapacityDisableThreshold = new ConfigKey( "Alert", diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index 274bf1c24b8..1d89b193057 100644 --- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -24,13 +24,13 @@ import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; - import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.capacity.Capacity; +import com.cloud.capacity.CapacityManager; import com.cloud.storage.ScopeType; import com.cloud.storage.StoragePool; import com.cloud.storage.StorageStats; @@ -103,6 +103,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase Date: Fri, 14 Mar 2014 17:17:38 +0100 Subject: [PATCH 26/31] Fix database upgrade from 4.3.0 to 4.4.0 --- setup/db/db/schema-430to440.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 5c5511ac5f7..1d511c40dc5 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -664,7 +664,7 @@ CREATE TABLE `cloud`.`op_router_monitoring_services` ( `last_alert_timestamp` varchar(255) NOT NULL COMMENT 'Timestamp of the last alert received from Virtual Router', PRIMARY KEY (`vm_id`), CONSTRAINT `fk_virtual_router__id` FOREIGN KEY `fk_virtual_router__id` (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET=utf8 +) ENGINE = InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `cloud`.`event` ADD COLUMN `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user'; From 7a929d1a0e0a32cdd2fb9a871617b291f04b46f5 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 24 Jan 2014 11:15:38 +0530 Subject: [PATCH 27/31] region level VPC support introduce 'RegionLevelVpc' as capability of 'Connectivity' service. Add support for CreateVPCOffering to take the 'regionlevelvpc' as capability of service 'connectivity'. introduces new capability 'StretchedL2Subnet' for 'Connectivity' service. Also add support to createNetworkOffering api to allow StretchedL2Subnet capablity for the connectivity service. adds check to ensure 'Connectivity' service provider supports 'StretchedL2Subnet' and 'RegionLevelVpc' capabilities when specified in createNetworkOffering and createVpcOffering respectivley enable ovs plug-in to support both StretchedL2Subnet and RegionLevelVpc capabilities make zone id optional parameter in createVpc, zone id can be null only if vpc offfering supports region level VPC in region level vpc, let the network/tier to be created in any zone of the region keep zoneid as required param for createVpc skip external guest network guru if 'Connectivy' service is present in network offering fix build break in contrail manager permit VM's to be created in different zone that in which network is created if the network support streched L2 subnet add integration tests for region level VPC rebase to master Conflicts: setup/db/db/schema-430to440.sql --- api/src/com/cloud/network/Network.java | 6 +- api/src/com/cloud/network/NetworkProfile.java | 8 + api/src/com/cloud/network/vpc/Vpc.java | 6 + .../com/cloud/network/vpc/VpcOffering.java | 6 +- .../network/vpc/VpcProvisioningService.java | 1 + .../com/cloud/offering/NetworkOffering.java | 2 + .../apache/cloudstack/api/ApiConstants.java | 4 +- .../api/command/user/vpc/CreateVPCCmd.java | 2 +- .../api/response/NetworkOfferingResponse.java | 7 + .../api/response/VpcOfferingResponse.java | 8 + .../cloudstack/api/response/VpcResponse.java | 9 +- .../src/com/cloud/network/dao/NetworkVO.java | 12 + .../com/cloud/network/vpc/VpcOfferingVO.java | 11 +- .../src/com/cloud/network/vpc/VpcVO.java | 11 +- .../cloud/offerings/NetworkOfferingVO.java | 11 +- .../com/cloud/network/element/OvsElement.java | 2 + .../src/com/cloud/api/ApiResponseHelper.java | 3 + .../ConfigurationManagerImpl.java | 54 +- .../guru/ExternalGuestNetworkGuru.java | 4 + .../com/cloud/network/vpc/VpcManagerImpl.java | 298 ++++++---- .../cloud/server/ConfigurationServerImpl.java | 6 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 2 +- .../network/CreatePrivateNetworkTest.java | 2 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 2 +- server/test/com/cloud/vpc/VpcApiUnitTest.java | 2 +- .../com/cloud/vpc/dao/MockVpcDaoImpl.java | 4 +- setup/db/db/schema-430to440.sql | 4 +- test/integration/component/test_region_vpc.py | 517 ++++++++++++++++++ 28 files changed, 874 insertions(+), 130 deletions(-) create mode 100644 test/integration/component/test_region_vpc.py diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 3283a55bfe7..ef3bcdf51c4 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -57,7 +57,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Service PortForwarding = new Service("PortForwarding"); public static final Service SecurityGroup = new Service("SecurityGroup"); public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols); - public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter); + public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet); private final String name; private final Capability[] caps; @@ -187,6 +187,8 @@ public interface Network extends ControlledEntity, StateObject, I public static final Capability LbSchemes = new Capability("LbSchemes"); public static final Capability DhcpAccrossMultipleSubnets = new Capability("DhcpAccrossMultipleSubnets"); public static final Capability DistributedRouter = new Capability("DistributedRouter"); + public static final Capability StretchedL2Subnet = new Capability("StretchedL2Subnet"); + public static final Capability RegionLevelVpc = new Capability("RegionLevelVpc"); private final String name; @@ -337,4 +339,6 @@ public interface Network extends ControlledEntity, StateObject, I Long getNetworkACLId(); void setNetworkACLId(Long networkACLId); + + boolean isStrechedL2Network(); } diff --git a/api/src/com/cloud/network/NetworkProfile.java b/api/src/com/cloud/network/NetworkProfile.java index 3f576945a57..390ec25cf6b 100644 --- a/api/src/com/cloud/network/NetworkProfile.java +++ b/api/src/com/cloud/network/NetworkProfile.java @@ -57,6 +57,7 @@ public class NetworkProfile implements Network { private final boolean displayNetwork; private Long networkAclId; private final String guruName; + private boolean strechedL2Subnet; public NetworkProfile(Network network) { id = network.getId(); @@ -89,6 +90,7 @@ public class NetworkProfile implements Network { displayNetwork = network.getDisplayNetwork(); networkAclId = network.getNetworkACLId(); guruName = network.getGuruName(); + strechedL2Subnet = network.isStrechedL2Network(); } public String getDns1() { @@ -282,4 +284,10 @@ public class NetworkProfile implements Network { public IAMEntityType getEntityType() { return IAMEntityType.Network; } + + @Override + public boolean isStrechedL2Network() { + return false; + } + } diff --git a/api/src/com/cloud/network/vpc/Vpc.java b/api/src/com/cloud/network/vpc/Vpc.java index 4bc8c98872d..23388787d23 100644 --- a/api/src/com/cloud/network/vpc/Vpc.java +++ b/api/src/com/cloud/network/vpc/Vpc.java @@ -79,4 +79,10 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity { * @return true if VPC is configured to use distributed router to provides one-hop forwarding and hypervisor based ACL */ boolean usesDistributedRouter(); + + /** + * + * @return true if VPC spans multiple zones in the region + */ + boolean isRegionLevelVpc(); } diff --git a/api/src/com/cloud/network/vpc/VpcOffering.java b/api/src/com/cloud/network/vpc/VpcOffering.java index a0a1b1528cf..660c79dc1e4 100644 --- a/api/src/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/com/cloud/network/vpc/VpcOffering.java @@ -56,8 +56,12 @@ public interface VpcOffering extends InternalIdentity, Identity { Long getServiceOfferingId(); /** - * * @return true if the offering provides a distributed router capable of one-hop forwarding */ boolean supportsDistributedRouter(); + + /** + * @return true if VPC created with the offering can span multiple zones in the region + */ + boolean offersRegionLevelVPC(); } diff --git a/api/src/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/com/cloud/network/vpc/VpcProvisioningService.java index e545275217f..82a7baae959 100644 --- a/api/src/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/com/cloud/network/vpc/VpcProvisioningService.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network.vpc; + import java.util.List; import java.util.Map; diff --git a/api/src/com/cloud/offering/NetworkOffering.java b/api/src/com/cloud/offering/NetworkOffering.java index b4f3863293a..f1ef69c2f68 100644 --- a/api/src/com/cloud/offering/NetworkOffering.java +++ b/api/src/com/cloud/offering/NetworkOffering.java @@ -129,4 +129,6 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, Integer getConcurrentConnections(); boolean isKeepAliveEnabled(); + + boolean getSupportsStrechedL2(); } diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 32107edf840..0dbc1b823c1 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -589,7 +589,9 @@ public class ApiConstants { public static final String VGPUTYPE = "vgputype"; public static final String REMAININGCAPACITY = "remainingcapacity"; public static final String DISTRIBUTED_VPC_ROUTER = "distributedvpcrouter"; - + public static final String SUPPORTS_REGION_LEVEL_VPC = "supportsregionLevelvpc"; + public static final String SUPPORTS_STRECHED_L2_SUBNET = "supportsstrechedl2subnet"; + public static final String REGION_LEVEL_VPC = "regionlevelvpc"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index c179ec857f0..4bf79da9aef 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -65,7 +65,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd { private Long projectId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - required = true, description = "the ID of the availability zone") + required = true, description = "the ID of the availability zone") private Long zoneId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the VPC") diff --git a/api/src/org/apache/cloudstack/api/response/NetworkOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/NetworkOfferingResponse.java index 8219147726e..55ca2ce886d 100644 --- a/api/src/org/apache/cloudstack/api/response/NetworkOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NetworkOfferingResponse.java @@ -116,6 +116,10 @@ public class NetworkOfferingResponse extends BaseResponse { @Param(description = "maximum number of concurrents connections to be handled by lb") private Integer concurrentConnections; + @SerializedName(ApiConstants.SUPPORTS_STRECHED_L2_SUBNET) + @Param(description = "true if network offering supports network that span multiple zones") + private Boolean supportsStrechedL2Subnet; + public void setId(String id) { this.id = id; } @@ -200,4 +204,7 @@ public class NetworkOfferingResponse extends BaseResponse { this.concurrentConnections = concurrentConnections; } + public void setSupportsStrechedL2Subnet(Boolean supportsStrechedL2Subnet) { + this.supportsStrechedL2Subnet = supportsStrechedL2Subnet; + } } diff --git a/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java index 89697f04ad0..de5adef611b 100644 --- a/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -63,6 +63,10 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = " indicates if the vpc offering supports distributed router for one-hop forwarding") private Boolean supportsDistributedRouter; + @SerializedName((ApiConstants.SUPPORTS_REGION_LEVEL_VPC)) + @Param(description = " indicated if the offering can support region level vpc") + private Boolean supportsRegionLevelVpc; + public void setId(String id) { this.id = id; } @@ -94,4 +98,8 @@ public class VpcOfferingResponse extends BaseResponse { public void setSupportsDistributedRouter(Boolean supportsDistributedRouter) { this.supportsDistributedRouter = supportsDistributedRouter; } + + public void setSupportsRegionLevelVpc(Boolean supports) { + this.supportsRegionLevelVpc = supports; + } } diff --git a/api/src/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/org/apache/cloudstack/api/response/VpcResponse.java index e3b44f2113f..d86045408cb 100644 --- a/api/src/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VpcResponse.java @@ -111,11 +111,14 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons @Param(description = "is vpc for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; - @SerializedName(ApiConstants.DISTRIBUTED_VPC_ROUTER) @Param(description = "is VPC uses distributed router for one hop forwarding and host based network ACL's") private boolean usesDistributedRouter; + @SerializedName((ApiConstants.REGION_LEVEL_VPC)) + @Param(description = "true if VPC is region level") + private Boolean regionLevelVpc; + public void setId(String id) { this.id = id; } @@ -205,6 +208,10 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons this.forDisplay = forDisplay; } + public void setRegionLevelVpc(Boolean regionLevelVpc) { + this.regionLevelVpc = regionLevelVpc; + } + public void setUsesDistributedRouter(Boolean usesDistributedRouter) { this.usesDistributedRouter = usesDistributedRouter; } diff --git a/engine/schema/src/com/cloud/network/dao/NetworkVO.java b/engine/schema/src/com/cloud/network/dao/NetworkVO.java index 13e8dbff09e..dc9d60aa05e 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkVO.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkVO.java @@ -167,6 +167,9 @@ public class NetworkVO implements Network { @Column(name = "network_acl_id") Long networkACLId; + @Column(name = "streched_l2") + boolean strechedL2Network = false; + public NetworkVO() { uuid = UUID.randomUUID().toString(); } @@ -589,4 +592,13 @@ public class NetworkVO implements Network { public IAMEntityType getEntityType() { return IAMEntityType.Network; } + + @Override + public boolean isStrechedL2Network() { + return strechedL2Network; + } + + public void setStrechedL2Network(boolean strechedL2Network) { + this.strechedL2Network = strechedL2Network; + } } diff --git a/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java index 53f6f6055e1..a7d61b36923 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcOfferingVO.java @@ -70,6 +70,9 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "supports_distributed_router") boolean supportsDistributedRouter=false; + @Column(name = "supports_region_level_vpc") + boolean offersRegionLevelVPC = false; + public VpcOfferingVO() { this.uuid = UUID.randomUUID().toString(); } @@ -84,10 +87,11 @@ public class VpcOfferingVO implements VpcOffering { } public VpcOfferingVO(String name, String displayText, boolean isDefault, Long serviceOfferingId, - boolean supportsDistributedRouter) { + boolean supportsDistributedRouter, boolean offersRegionLevelVPC) { this(name, displayText, serviceOfferingId); this.isDefault = isDefault; this.supportsDistributedRouter = supportsDistributedRouter; + this.offersRegionLevelVPC = offersRegionLevelVPC; } @Override @@ -155,4 +159,9 @@ public class VpcOfferingVO implements VpcOffering { public boolean supportsDistributedRouter() { return supportsDistributedRouter; } + + @Override + public boolean offersRegionLevelVPC() { + return offersRegionLevelVPC; + } } diff --git a/engine/schema/src/com/cloud/network/vpc/VpcVO.java b/engine/schema/src/com/cloud/network/vpc/VpcVO.java index dea8c314bcc..8d0f6a2a0f1 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcVO.java @@ -84,12 +84,15 @@ public class VpcVO implements Vpc { @Column(name="uses_distributed_router") boolean usesDistributedRouter = false; + @Column(name = "region_level_vpc") + boolean regionLevelVpc = false; + public VpcVO() { uuid = UUID.randomUUID().toString(); } public VpcVO(long zoneId, String name, String displayText, long accountId, long domainId, long vpcOffId, String cidr, - String networkDomain, boolean useDistributedRouter) { + String networkDomain, boolean useDistributedRouter, boolean regionLevelVpc) { this.zoneId = zoneId; this.name = name; this.displayText = displayText; @@ -101,6 +104,7 @@ public class VpcVO implements Vpc { this.networkDomain = networkDomain; this.vpcOfferingId = vpcOffId; this.usesDistributedRouter = useDistributedRouter; + this.regionLevelVpc = regionLevelVpc; } @Override @@ -193,6 +197,11 @@ public class VpcVO implements Vpc { this.uuid = uuid; } + @Override + public boolean isRegionLevelVpc() { + return regionLevelVpc; + } + public void setDisplay(boolean display) { this.display = display; diff --git a/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java index bf078074948..c3d849d134c 100755 --- a/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java @@ -139,6 +139,9 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "keep_alive_enabled") boolean keepAliveEnabled = false; + @Column(name="supports_streched_l2") + boolean supportsStrechedL2 = false; + @Override public String getDisplayText() { return displayText; @@ -331,7 +334,7 @@ public class NetworkOfferingVO implements NetworkOffering { public NetworkOfferingVO(String name, String displayText, TrafficType trafficType, boolean systemOnly, boolean specifyVlan, Integer rateMbps, Integer multicastRateMbps, boolean isDefault, Availability availability, String tags, Network.GuestType guestType, boolean conserveMode, boolean dedicatedLb, boolean sharedSourceNat, boolean redundantRouter, boolean elasticIp, boolean elasticLb, boolean specifyIpRanges, boolean inline, boolean isPersistent, - boolean associatePublicIP, boolean publicLb, boolean internalLb, boolean egressdefaultpolicy) { + boolean associatePublicIP, boolean publicLb, boolean internalLb, boolean egressdefaultpolicy, boolean supportsStrechedL2) { this(name, displayText, trafficType, @@ -356,6 +359,7 @@ public class NetworkOfferingVO implements NetworkOffering { this.inline = inline; this.eipAssociatePublicIp = associatePublicIP; this.egressdefaultpolicy = egressdefaultpolicy; + this.supportsStrechedL2 = supportsStrechedL2; } public NetworkOfferingVO() { @@ -486,4 +490,9 @@ public class NetworkOfferingVO implements NetworkOffering { public void setPublicLb(boolean publicLb) { this.publicLb = publicLb; } + + @Override + public boolean getSupportsStrechedL2() { + return supportsStrechedL2; + } } diff --git a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java index 036c3194d57..d7678340a3b 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java @@ -261,6 +261,8 @@ StaticNatServiceProvider, IpDeployer { // L2 Support : SDN provisioning Map connectivityCapabilities = new HashMap(); connectivityCapabilities.put(Capability.DistributedRouter, null); + connectivityCapabilities.put(Capability.StretchedL2Subnet, null); + connectivityCapabilities.put(Capability.RegionLevelVpc, null); capabilities.put(Service.Connectivity, connectivityCapabilities); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index a37bf7b2d4d..bc496a34975 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -1901,6 +1901,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setNetworkRate(ApiDBUtils.getNetworkRate(offering.getId())); response.setEgressDefaultPolicy(offering.getEgressDefaultPolicy()); response.setConcurrentConnections(offering.getConcurrentConnections()); + response.setSupportsStrechedL2Subnet(offering.getSupportsStrechedL2()); Long so = null; if (offering.getServiceOfferingId() != null) { so = offering.getServiceOfferingId(); @@ -2766,6 +2767,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setIsDefault(offering.isDefault()); response.setState(offering.getState().name()); response.setSupportsDistributedRouter(offering.supportsDistributedRouter()); + response.setSupportsRegionLevelVpc(offering.offersRegionLevelVPC()); Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(offering.getId()); List serviceResponses = new ArrayList(); @@ -2809,6 +2811,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setNetworkDomain(vpc.getNetworkDomain()); response.setForDisplay(vpc.isDisplay()); response.setUsesDistributedRouter(vpc.usesDistributedRouter()); + response.setRegionLevelVpc(vpc.isRegionLevelVpc()); Map> serviceProviderMap = ApiDBUtils.listVpcOffServices(vpc.getVpcOfferingId()); List serviceResponses = new ArrayList(); diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 9b9bd13d261..bdceed79f4a 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.configuration; +import com.cloud.network.element.NetworkElement; import java.net.URI; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -3814,10 +3815,21 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + // validate the 'Connectivity' service capabilities specified in the network offering, if 'Connectivity' service + // is in the supported services of network offering + Map connectivityServiceCapabilityMap = cmd.getServiceCapabilities(Service.Connectivity); + if (!serviceProviderMap.containsKey(Service.Connectivity) && + (connectivityServiceCapabilityMap != null && !connectivityServiceCapabilityMap.isEmpty())) { + throw new InvalidParameterValueException("Capabilities for 'Connectivity' service can be specified " + + "only when Connectivity service is enabled for network offering."); + } + validateConnectivityServiceCapablities(serviceProviderMap.get(Service.Connectivity), connectivityServiceCapabilityMap); + Map> serviceCapabilityMap = new HashMap>(); serviceCapabilityMap.put(Service.Lb, lbServiceCapabilityMap); serviceCapabilityMap.put(Service.SourceNat, sourceNatServiceCapabilityMap); serviceCapabilityMap.put(Service.StaticNat, staticNatServiceCapabilityMap); + serviceCapabilityMap.put(Service.Connectivity, connectivityServiceCapabilityMap); // if Firewall service is missing, add Firewall service/provider // combination @@ -3952,6 +3964,36 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + void validateConnectivityServiceCapablities(Set providers, Map connectivityServiceCapabilityMap) { + if (connectivityServiceCapabilityMap != null && !connectivityServiceCapabilityMap.isEmpty()) { + for (Capability capability: connectivityServiceCapabilityMap.keySet()) { + if (capability == Capability.StretchedL2Subnet) { + String value = connectivityServiceCapabilityMap.get(capability).toLowerCase(); + if (!(value.contains("true") ^ value.contains("false"))) { + throw new InvalidParameterValueException("Invalid value (" + value + ") for " + capability + + " should be true/false"); + } + } else { + throw new InvalidParameterValueException("Capability " + capability.getName() + " can not be " + + " specified with connectivity service."); + } + } + } + + if (providers != null && !providers.isEmpty()) { + for (Provider provider: providers) { + NetworkElement element = _networkModel.getElementImplementingProvider(provider.getName()); + Map> capabilities = element.getCapabilities(); + if (capabilities != null && !capabilities.isEmpty()) { + Map connectivityCapabilities = capabilities.get(Service.Connectivity); + if (connectivityCapabilities == null || (connectivityCapabilities != null && !connectivityCapabilities.keySet().contains(Capability.StretchedL2Subnet))) { + throw new InvalidParameterValueException("Provider: " + provider.getName() + " does not support " + + Capability.StretchedL2Subnet.getName()); + } + } + } + } + } @Override @DB public NetworkOfferingVO createNetworkOffering(String name, String displayText, TrafficType trafficType, String tags, boolean specifyVlan, Availability availability, @@ -4012,6 +4054,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati boolean inline = false; boolean publicLb = false; boolean internalLb = false; + boolean strechedL2Subnet = false; + if (serviceCapabilityMap != null && !serviceCapabilityMap.isEmpty()) { Map lbServiceCapabilityMap = serviceCapabilityMap.get(Service.Lb); @@ -4079,6 +4123,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } } + + Map connectivityServiceCapabilityMap = serviceCapabilityMap.get(Service.Connectivity); + if (connectivityServiceCapabilityMap != null && !connectivityServiceCapabilityMap.isEmpty()) { + String value = connectivityServiceCapabilityMap.get(Capability.StretchedL2Subnet); + if ("true".equalsIgnoreCase(value)) { + strechedL2Subnet = true; + } + } } if (serviceProviderMap != null && serviceProviderMap.containsKey(Service.Lb) && !internalLb && !publicLb) { @@ -4088,7 +4140,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final NetworkOfferingVO offeringFinal = new NetworkOfferingVO(name, displayText, trafficType, systemOnly, specifyVlan, networkRate, multicastRate, isDefault, availability, tags, type, conserveMode, dedicatedLb, sharedSourceNat, redundantRouter, elasticIp, elasticLb, specifyIpRanges, inline, isPersistent, associatePublicIp, publicLb, - internalLb, egressDefaultPolicy); + internalLb, egressDefaultPolicy, strechedL2Subnet); if (serviceOfferingId != null) { offeringFinal.setServiceOfferingId(serviceOfferingId); diff --git a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java index 13246a74a5d..ddf1f9c3d8b 100644 --- a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -127,6 +127,10 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { throws InsufficientVirtualNetworkCapcityException { assert (config.getState() == State.Implementing) : "Why are we implementing " + config; + if (_networkModel.areServicesSupportedInNetwork(config.getId(), Network.Service.Connectivity)) { + return null; + } + if (!_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { return super.implement(config, offering, dest, context); } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 2e63639c33d..ad212b09074 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.vpc; + +import com.cloud.network.element.NetworkElement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -65,7 +67,6 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.network.element.NetworkElement; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; @@ -220,44 +221,49 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCOfferingName) == null) { - s_logger.debug("Creating default VPC offering " + VpcOffering.defaultVPCOfferingName); - Map> svcProviderMap = new HashMap>(); - Set defaultProviders = new HashSet(); - defaultProviders.add(Provider.VPCVirtualRouter); - for (Service svc : getSupportedServices()) { - if (svc == Service.Lb) { - Set lbProviders = new HashSet(); - lbProviders.add(Provider.VPCVirtualRouter); - lbProviders.add(Provider.InternalLbVm); - svcProviderMap.put(svc, lbProviders); - } else { - svcProviderMap.put(svc, defaultProviders); + if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCOfferingName) == null) { + s_logger.debug("Creating default VPC offering " + VpcOffering.defaultVPCOfferingName); + + Map> svcProviderMap = new HashMap>(); + Set defaultProviders = new HashSet(); + defaultProviders.add(Provider.VPCVirtualRouter); + for (Service svc : getSupportedServices()) { + if (svc == Service.Lb) { + Set lbProviders = new HashSet(); + lbProviders.add(Provider.VPCVirtualRouter); + lbProviders.add(Provider.InternalLbVm); + svcProviderMap.put(svc, lbProviders); + } else { + svcProviderMap.put(svc, defaultProviders); + } + } + createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, + svcProviderMap, true, State.Enabled, null, false, false); } - } - createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null, false); - } - //configure default vpc offering with Netscaler as LB Provider + //configure default vpc offering with Netscaler as LB Provider if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCNSOfferingName) == null) { - s_logger.debug("Creating default VPC offering with Netscaler as LB Provider" + VpcOffering.defaultVPCNSOfferingName); - Map> svcProviderMap = new HashMap>(); - Set defaultProviders = new HashSet(); - defaultProviders.add(Provider.VPCVirtualRouter); - for (Service svc : getSupportedServices()) { - if (svc == Service.Lb) { - Set lbProviders = new HashSet(); - lbProviders.add(Provider.Netscaler); - lbProviders.add(Provider.InternalLbVm); - svcProviderMap.put(svc, lbProviders); - } else { - svcProviderMap.put(svc, defaultProviders); + s_logger.debug("Creating default VPC offering with Netscaler as LB Provider" + VpcOffering.defaultVPCNSOfferingName); + Map> svcProviderMap = new HashMap>(); + Set defaultProviders = new HashSet(); + defaultProviders.add(Provider.VPCVirtualRouter); + for (Service svc : getSupportedServices()) { + if (svc == Service.Lb) { + Set lbProviders = new HashSet(); + lbProviders.add(Provider.Netscaler); + lbProviders.add(Provider.InternalLbVm); + svcProviderMap.put(svc, lbProviders); + } else { + svcProviderMap.put(svc, defaultProviders); + } + } + createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, + svcProviderMap, false, State.Enabled, null, false, false); + } } - createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false); - } - } + }); Map configs = _configDao.getConfiguration(params); @@ -381,9 +387,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis validateConnectivtyServiceCapablitlies(svcProviderMap.get(Service.Connectivity), serviceCapabilitystList); boolean supportsDistributedRouter = isVpcOfferingSupportsDistributedRouter(serviceCapabilitystList); - + boolean offersRegionLevelVPC = isVpcOfferingForRegionLevelVpc(serviceCapabilitystList); VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, - serviceOfferingId,supportsDistributedRouter); + serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC); CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); return offering; @@ -393,37 +399,149 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis protected VpcOffering createVpcOffering(final String name, final String displayText, final Map> svcProviderMap, final boolean isDefault, final State state, final Long serviceOfferingId, - final boolean supportsDistributedRouter) { + final boolean supportsDistributedRouter, final boolean offersRegionLevelVPC) { + return Transaction.execute(new TransactionCallback() { @Override public VpcOffering doInTransaction(TransactionStatus status) { // create vpc offering object - VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId, supportsDistributedRouter); + VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId, + supportsDistributedRouter, offersRegionLevelVPC); - if (state != null) { - offering.setState(state); - } - s_logger.debug("Adding vpc offering " + offering); - offering = _vpcOffDao.persist(offering); - // populate services and providers - if (svcProviderMap != null) { - for (Network.Service service : svcProviderMap.keySet()) { - Set providers = svcProviderMap.get(service); - if (providers != null && !providers.isEmpty()) { - for (Network.Provider provider : providers) { - VpcOfferingServiceMapVO offService = new VpcOfferingServiceMapVO(offering.getId(), service, provider); - _vpcOffSvcMapDao.persist(offService); - s_logger.trace("Added service for the vpc offering: " + offService + " with provider " + provider.getName()); + if (state != null) { + offering.setState(state); + } + s_logger.debug("Adding vpc offering " + offering); + offering = _vpcOffDao.persist(offering); + // populate services and providers + if (svcProviderMap != null) { + for (Network.Service service : svcProviderMap.keySet()) { + Set providers = svcProviderMap.get(service); + if (providers != null && !providers.isEmpty()) { + for (Network.Provider provider : providers) { + VpcOfferingServiceMapVO offService = new VpcOfferingServiceMapVO(offering.getId(), service, provider); + _vpcOffSvcMapDao.persist(offService); + s_logger.trace("Added service for the vpc offering: " + offService + " with provider " + provider.getName()); + } + } else { + throw new InvalidParameterValueException("Provider is missing for the VPC offering service " + service.getName()); + } + } + } + + return offering; + } + }); + } + + private void validateConnectivtyServiceCapablitlies(Set providers, Map serviceCapabilitystList) { + + if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { + Collection serviceCapabilityCollection = serviceCapabilitystList.values(); + Iterator iter = serviceCapabilityCollection.iterator(); + Map capabilityMap = null; + boolean distributedRouterCapabilitySpecified = false; + boolean regionLevelVpcCapabilitySpecified = false; + + while (iter.hasNext()) { + HashMap svcCapabilityMap = (HashMap)iter.next(); + Network.Capability capability = null; + String svc = svcCapabilityMap.get("service"); + String capabilityName = svcCapabilityMap.get("capabilitytype"); + String capabilityValue = svcCapabilityMap.get("capabilityvalue"); + if (capabilityName != null) { + capability = Network.Capability.getCapability(capabilityName); + } + + if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { + throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); + } + + if (!svc.equalsIgnoreCase(Service.Connectivity.getName())) { + throw new InvalidParameterValueException("Invalid Service:" + svc + " specified. Only 'Connectivity'" + + " service capabilities can be specified"); + } + + if (!capabilityName.equalsIgnoreCase("DistributedRouter") && !capabilityName.equalsIgnoreCase("RegionLevelVpc")) { + throw new InvalidParameterValueException("Invalid Capability:" + capabilityName + " specified." + + " Only 'DistributedRouter'/'RegionLevelVpc' capability can be specified."); + } + + if (capabilityName.equalsIgnoreCase("DistributedRouter")) { + distributedRouterCapabilitySpecified = true; + } + + if (capabilityName.equalsIgnoreCase("RegionLevelVpc")) { + regionLevelVpcCapabilitySpecified = true; + } + + if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { + throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified."); + } + } + + if (providers != null && !providers.isEmpty()) { + for (Provider provider: providers) { + NetworkElement element = _ntwkModel.getElementImplementingProvider(provider.getName()); + Map> capabilities = element.getCapabilities(); + if (capabilities != null && !capabilities.isEmpty()) { + Map connectivityCapabilities = capabilities.get(Service.Connectivity); + if (regionLevelVpcCapabilitySpecified) { + if (connectivityCapabilities == null || (connectivityCapabilities != null && + !connectivityCapabilities.keySet().contains(Network.Capability.RegionLevelVpc))) { + throw new InvalidParameterValueException("Provider: " + provider.getName() + " does not support " + + Network.Capability.RegionLevelVpc.getName() + " capability."); + } + } + if (distributedRouterCapabilitySpecified) { + if (connectivityCapabilities == null || (connectivityCapabilities != null && + !connectivityCapabilities.keySet().contains(Network.Capability.DistributedRouter))) { + throw new InvalidParameterValueException("Provider: " + provider.getName() + " does not support " + + Network.Capability.DistributedRouter.getName() + " capability."); + } + } } - } else { - throw new InvalidParameterValueException("Provider is missing for the VPC offering service " + service.getName()); } } } - - return offering; } - }); + + private boolean isVpcOfferingForRegionLevelVpc(Map serviceCapabilitystList) { + boolean offersRegionLevelVPC = false; + if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { + Collection serviceCapabilityCollection = serviceCapabilitystList.values(); + Iterator iter = serviceCapabilityCollection.iterator(); + Map capabilityMap = null; + + while (iter.hasNext()) { + HashMap svcCapabilityMap = (HashMap)iter.next(); + Network.Capability capability = null; + String svc = svcCapabilityMap.get("service"); + String capabilityName = svcCapabilityMap.get("capabilitytype"); + String capabilityValue = svcCapabilityMap.get("capabilityvalue"); + if (capabilityName != null) { + capability = Network.Capability.getCapability(capabilityName); + } + + if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { + throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); + } + + if (!svc.equalsIgnoreCase(Service.Connectivity.getName())) { + throw new InvalidParameterValueException("Invalid Service:" + svc + " specified. Only for 'Connectivity' service capabilities can be specified"); + } + + if (!capabilityName.equalsIgnoreCase("RegionLevelVpc")) { + continue; + } + + if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { + throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified."); + } + offersRegionLevelVPC = capabilityValue.equalsIgnoreCase("true"); + } + } + return offersRegionLevelVPC; } private boolean isVpcOfferingSupportsDistributedRouter(Map serviceCapabilitystList) { @@ -452,7 +570,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } if (!capabilityName.equalsIgnoreCase("DistributedRouter")) { - throw new InvalidParameterValueException("Invalid Capability:" + capabilityName + " specified. Only 'DistributedRouter' capability can be specified."); + continue; } if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { @@ -464,56 +582,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return supportsDistributedRouter; } - private void validateConnectivtyServiceCapablitlies(Set providers, Map serviceCapabilitystList) { - - if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) { - Collection serviceCapabilityCollection = serviceCapabilitystList.values(); - Iterator iter = serviceCapabilityCollection.iterator(); - Map capabilityMap = null; - - while (iter.hasNext()) { - HashMap svcCapabilityMap = (HashMap)iter.next(); - Network.Capability capability = null; - String svc = svcCapabilityMap.get("service"); - String capabilityName = svcCapabilityMap.get("capabilitytype"); - String capabilityValue = svcCapabilityMap.get("capabilityvalue"); - if (capabilityName != null) { - capability = Network.Capability.getCapability(capabilityName); - } - - if ((capability == null) || (capabilityName == null) || (capabilityValue == null)) { - throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue); - } - - if (!svc.equalsIgnoreCase(Service.Connectivity.getName())) { - throw new InvalidParameterValueException("Invalid Service:" + svc + " specified. Only for 'Connectivity' service capabilities can be specified"); - } - - if (!capabilityName.equalsIgnoreCase("DistributedRouter")) { - throw new InvalidParameterValueException("Invalid Capability:" + capabilityName + " specified. Only 'DistributedRouter' capability can be specified."); - } - - if (!capabilityValue.equalsIgnoreCase("true") && capabilityValue.equalsIgnoreCase("false")) { - throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified."); - } - } - - if (providers != null && !providers.isEmpty()) { - for (Provider provider: providers) { - NetworkElement element = _ntwkModel.getElementImplementingProvider(provider.getName()); - Map> capabilities = element.getCapabilities(); - if (capabilities != null && !capabilities.isEmpty()) { - Map connectivityCapabilities = capabilities.get(Service.Connectivity); - if (connectivityCapabilities == null || (connectivityCapabilities != null && !connectivityCapabilities.keySet().contains(Network.Capability.DistributedRouter))) { - throw new InvalidParameterValueException("Provider: " + provider.getName() + " does not support " - + Network.Capability.DistributedRouter.getName() + " capability."); - } - } - } - } - } - } - @Override public Vpc getActiveVpc(long vpcId) { return _vpcDao.getActiveVpcById(vpcId); @@ -708,6 +776,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw ex; } + boolean isRegionLevelVpcOff = vpcOff.offersRegionLevelVPC(); + if (isRegionLevelVpcOff && networkDomain == null) { + throw new InvalidParameterValueException("Network domain must be specified for region level VPC"); + } + //Validate zone DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); if (zone == null) { @@ -730,13 +803,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis networkDomain = "cs" + Long.toHexString(owner.getId()) + NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId); } } + boolean useDistributedRouter = vpcOff.supportsDistributedRouter(); - return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain, displayVpc, useDistributedRouter); + return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain, displayVpc, + useDistributedRouter, isRegionLevelVpcOff); } @DB protected Vpc createVpc(final long zoneId, final long vpcOffId, final Account vpcOwner, final String vpcName, final String displayText, final String cidr, - final String networkDomain, final Boolean displayVpc, final boolean useDistributedRouter) { + final String networkDomain, final Boolean displayVpc, final boolean useDistributedRouter, final boolean regionLevelVpc) { //Validate CIDR if (!NetUtils.isValidCIDR(cidr)) { @@ -759,7 +834,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override public VpcVO doInTransaction(TransactionStatus status) { VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, vpcOwner.getId(), vpcOwner.getDomainId(), vpcOffId, - cidr, networkDomain, useDistributedRouter); + cidr, networkDomain, useDistributedRouter, regionLevelVpc); + if (displayVpc != null) { vpc.setDisplay(displayVpc); } @@ -2210,7 +2286,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis networkDomain = vpc.getNetworkDomain(); } - if (vpc.getZoneId() != zoneId) { + if (!vpc.isRegionLevelVpc() && vpc.getZoneId() != zoneId) { throw new InvalidParameterValueException("New network doesn't belong to vpc zone"); } diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index b8da4c8831b..478101027cc 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -1153,9 +1153,9 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio // Offering #5 NetworkOfferingVO defaultNetscalerNetworkOffering = - new NetworkOfferingVO(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, - "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, false, true, null, null, true, - Availability.Optional, null, Network.GuestType.Shared, true, false, false, false, true, true, true, false, false, true, true, false, false); + new NetworkOfferingVO(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, + "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, false, true, null, null, true, + Availability.Optional, null, Network.GuestType.Shared, true, false, false, false, true, true, true, false, false, true, true, false, false, false); defaultNetscalerNetworkOffering.setState(NetworkOffering.State.Enabled); defaultNetscalerNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultNetscalerNetworkOffering); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 393613a140e..6a7345cf61b 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2642,7 +2642,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir boolean securityGroupEnabled = false; boolean vpcNetwork = false; for (NetworkVO network : networkList) { - if (network.getDataCenterId() != zone.getId()) { + if ((network.getDataCenterId() != zone.getId()) && !network.isStrechedL2Network()) { throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId()); } diff --git a/server/test/com/cloud/network/CreatePrivateNetworkTest.java b/server/test/com/cloud/network/CreatePrivateNetworkTest.java index dc979d05c94..b124b20dadc 100644 --- a/server/test/com/cloud/network/CreatePrivateNetworkTest.java +++ b/server/test/com/cloud/network/CreatePrivateNetworkTest.java @@ -103,7 +103,7 @@ public class CreatePrivateNetworkTest { NetworkOfferingVO ntwkOff = new NetworkOfferingVO("offer", "fakeOffer", TrafficType.Guest, true, true, null, null, false, null, null, GuestType.Isolated, false, false, false, false, - false, false, false, false, false, false, false, false, false); + false, false, false, false, false, false, false, false, false, false); when(networkService._networkOfferingDao.findById(anyLong())).thenReturn(ntwkOff); List netofferlist = new ArrayList(); netofferlist.add(ntwkOff); diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index ac303dddfcb..387a710d9da 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -606,7 +606,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches @Override public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6, - String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan) throws ConcurrentOperationException, InsufficientCapacityException, + String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan ) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { // TODO Auto-generated method stub return null; diff --git a/server/test/com/cloud/vpc/VpcApiUnitTest.java b/server/test/com/cloud/vpc/VpcApiUnitTest.java index 5e283743b7a..dee9afe3b57 100644 --- a/server/test/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/test/com/cloud/vpc/VpcApiUnitTest.java @@ -85,7 +85,7 @@ public class VpcApiUnitTest extends TestCase { public void validateNtwkOffForVpc() { //validate network offering //1) correct network offering - VpcVO vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); + VpcVO vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false); boolean result = false; try { _vpcService.validateNtwkOffForNtwkInVpc(2L, 1, "0.0.0.0", "111-", vo, "10.1.1.1", new AccountVO(), null); diff --git a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java index e1a6ac2ae46..7492598e901 100644 --- a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java +++ b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java @@ -98,9 +98,9 @@ public class MockVpcDaoImpl extends GenericDaoBase implements VpcDa public VpcVO findById(Long id) { VpcVO vo = null; if (id.longValue() == 1) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false); } else if (id.longValue() == 2) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false); vo.setState(State.Inactive); } diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 1d511c40dc5..b71ce852e4d 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -742,6 +742,8 @@ UPDATE `cloud`.`guest_os` SET `created` = now(); ALTER TABLE `cloud`.`vm_reservation` ADD COLUMN `deployment_planner` varchar(40) DEFAULT NULL COMMENT 'Preferred deployment planner for the vm'; ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_distributed_router boolean default false; ALTER TABLE `cloud`.`vpc` ADD COLUMN uses_distributed_router boolean default false; - INSERT INTO `cloud`.`storage_pool_details` (pool_id,name,value,display) SELECT storage_pool.id,data_center_details.name,data_center_details.value,data_center_details.display FROM `cloud`.`storage_pool` JOIN `cloud`.`data_center_details` ON data_center_details.dc_id=storage_pool.data_center_id WHERE data_center_details.name = "storage.overprovisioning.factor"; DELETE FROM `cloud`.`data_center_details` WHERE name="storage.overprovisioning.factor"; +ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_region_level_vpc boolean default false; +ALTER TABLE `cloud`.`network_offerings` ADD COLUMN supports_streched_l2 boolean default false; +ALTER TABLE `cloud`.`vpc` ADD COLUMN region_level_vpc boolean default false; diff --git a/test/integration/component/test_region_vpc.py b/test/integration/component/test_region_vpc.py new file mode 100644 index 00000000000..2cd9de9073a --- /dev/null +++ b/test/integration/component/test_region_vpc.py @@ -0,0 +1,517 @@ +# 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. +import unittest + +""" Component tests for region level VPC functionality +""" +#Import Local Modules +import marvin +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from marvin.sshClient import SshClient +import datetime + + +class Services: + """Test inter VLAN services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 128, + }, + "network_offering": { + "name": 'VPC Network offering', + "displaytext": 'VPC Network off', + "guestiptype": 'Isolated', + "supportedservices": 'Vpn,Dhcp,Dns,SourceNat,PortForwarding,Lb,UserData,StaticNat,NetworkACL, Connectivity', + "traffictype": 'GUEST', + "availability": 'Optional', + "useVpc": 'on', + "serviceProviderList": { + "Vpn": 'VpcVirtualRouter', + "Dhcp": 'VpcVirtualRouter', + "Dns": 'VpcVirtualRouter', + "SourceNat": 'VpcVirtualRouter', + "PortForwarding": 'VpcVirtualRouter', + "Lb": 'VpcVirtualRouter', + "UserData": 'VpcVirtualRouter', + "StaticNat": 'VpcVirtualRouter', + "NetworkACL": 'VpcVirtualRouter', + "Connectivity": 'Ovs' + }, + "serviceCapabilityList": { + "Connectivity": { + "StretchedL2Subnet": "true" + }, + }, + }, + "vpc_offering": { + "name": 'VPC off', + "displaytext": 'VPC off', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Lb,UserData,StaticNat,Connectivity', + "serviceProviderList": { + "Vpn": 'VpcVirtualRouter', + "Dhcp": 'VpcVirtualRouter', + "Dns": 'VpcVirtualRouter', + "SourceNat": 'VpcVirtualRouter', + "PortForwarding": 'VpcVirtualRouter', + "Lb": 'VpcVirtualRouter', + "UserData": 'VpcVirtualRouter', + "StaticNat": 'VpcVirtualRouter', + "Connectivity": 'Ovs' + }, + "serviceCapabilityList": { + "Connectivity": { + "RegionLevelVpc": "true" + }, + }, + }, + "vpc": { + "name": "TestVPC", + "displaytext": "TestVPC", + "cidr": '10.0.0.1/24' + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "lbrule": { + "name": "SSH", + "alg": "leastconn", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + "openfirewall": False, + "startport": 2222, + "endport": 2222, + "cidrlist": '0.0.0.0/0', + "protocol": 'TCP' + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "startport": 22, + "endport": 22, + "protocol": "TCP", + "cidrlist": '0.0.0.0/0', + }, + "fw_rule": { + "startport": 1, + "endport": 6000, + "cidr": '0.0.0.0/0', + # Any network (For creating FW rule) + "protocol": "TCP" + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + } + + +class TestRegionVpcOffering(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestRegionVpcOffering, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + self.cleanup.insert(0, self.account) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def validate_vpc_offering(self, vpc_offering): + """Validates the VPC offering""" + + self.debug("Check if the VPC offering is created successfully?") + vpc_offs = VpcOffering.list( + self.apiclient, + id=vpc_offering.id + ) + self.assertEqual( + isinstance(vpc_offs, list), + True, + "List VPC offerings should return a valid list" + ) + self.assertEqual( + vpc_offering.name, + vpc_offs[0].name, + "Name of the VPC offering should match with listVPCOff data" + ) + self.assertEqual( + vpc_offering.name, + vpc_offs[0].name, + "Name of the VPC offering should match with listVPCOff data" + ) + self.assertEqual( + vpc_offs[0].regionlevelvpc,True, + "VPC offering is not set up for region level VPC" + ) + self.debug( + "VPC offering is created successfully - %s" % + vpc_offering.name) + return + + def validate_vpc_network(self, network): + """Validates the VPC network""" + + self.debug("Check if the VPC network is created successfully?") + vpc_networks = VPC.list( + self.apiclient, + id=network.id + ) + self.assertEqual( + isinstance(vpc_networks, list), + True, + "List VPC network should return a valid list" + ) + self.assertEqual( + network.name, + vpc_networks[0].name, + "Name of the VPC network should match with listVPC data" + ) + self.debug("VPC network created successfully - %s" % network.name) + return + + @attr(tags=["advanced", "intervlan"]) + def test_01_create_vpc_offering_with_regionlevelvpc_service_capability(self): + """ Test create VPC offering + """ + + # Steps for validation + # 1. Create VPC Offering by specifying all supported Services + # 2. VPC offering should be created successfully. + + self.debug("Creating inter VPC offering") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + + self.debug("Check if the VPC offering is created successfully?") + self.cleanup.append(vpc_off) + self.validate_vpc_offering(vpc_off) + return + + @attr(tags=["advanced", "intervlan"]) + def test_02_create_vpc_from_offering_with_regionlevelvpc_service_capability(self): + """ Test create VPC offering + """ + + # Steps for validation + # 1. Create VPC Offering by specifying all supported Services + # 2. VPC offering should be created successfully. + + self.debug("Creating inter VPC offering") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + vpc_off.update(self.apiclient, state='Enabled') + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_off.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual(vpc.distributedvpcrouter, True, "VPC created should have 'distributedvpcrouter' set to True") + + try: + vpc.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete VPC network - %s" % e) + return + + @attr(tags=["advanced", "intervlan"]) + def test_03_deploy_vms_in_vpc_with_regionlevelvpc(self): + """Test deploy virtual machines in VPC networks""" + + # 1. Create VPC Offering by specifying all supported Services + # (Vpn,dhcpdns,UserData, SourceNat,Static NAT and PF,LB,NetworkAcl) + # 2. Create a VPC using the above VPC offering + # 3. Create a network as part of this VPC. + # 4. Deploy few Vms. + # 5. Create a LB rule for this VM. + # 6. Create a PF rule for this VM. + # 7. Create a Static Nat rule for this VM. + # 8. Create Ingress rules on the network to open the above created + # LB PF and Static Nat rule + # 9. Create Egress Network ACL for this network to access google.com. + # 10. Enable VPN services + + self.debug("Creating a VPC offering..") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"] + ) + + vpc_off.update(self.apiclient, state='Enabled') + + self.debug("creating a VPC network in the account: %s" % + self.account.name) + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_off.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.validate_vpc_network(vpc) + + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + conservemode=False + ) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + gateway = vpc.cidr.split('/')[0] + # Split the cidr to retrieve gateway + # for eg. cidr = 10.0.0.1/24 + # Gateway = 10.0.0.1 + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id, + gateway=gateway, + vpcid=vpc.id + ) + self.debug("Created network with ID: %s" % network.id) + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)] + ) + self.debug("Deployed VM in network: %s" % network.id) + + self.debug("Associating public IP for network: %s" % network.name) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + network.id + )) + + self.debug("Creating LB rule for IP address: %s" % + public_ip.ipaddress.ipaddress) + + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.name, + networkid=network.id, + vpcid=vpc.id, + domainid=self.account.domainid + ) + + self.debug("Associating public IP for network: %s" % vpc.name) + public_ip_2 = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip_2.ipaddress.ipaddress, + network.id + )) + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=public_ip_2.ipaddress.id, + openfirewall=False, + networkid=network.id, + vpcid=vpc.id + ) + + self.debug("Adding NetwrokACl rules to make PF and LB accessible") + networkacl_1 = NetworkACL.create( + self.apiclient, + networkid=network.id, + services=self.services["natrule"], + traffictype='Ingress' + ) + + networkacl_2 = NetworkACL.create( + self.apiclient, + networkid=network.id, + services=self.services["lbrule"], + traffictype='Ingress' + ) + self.debug("Checking if we can SSH into VM?") + try: + virtual_machine.get_ssh_client( + ipaddress=public_ip_2.ipaddress.ipaddress, + ) + self.debug("SSH into VM is successfully") + except Exception as e: + self.fail("Failed to SSH into VM - %s, %s" % + (public_ip_2.ipaddress.ipaddress, e)) + + self.debug("Associating public IP for network: %s" % network.name) + public_ip_3 = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.debug("Associated %s with network %s" % ( + public_ip_3.ipaddress.ipaddress, + network.id + )) + self.debug("Enabling static NAT for IP: %s" % + public_ip_3.ipaddress.ipaddress) + try: + StaticNATRule.enable( + self.apiclient, + ipaddressid=public_ip_3.ipaddress.id, + virtualmachineid=virtual_machine.id, + networkid=network.id + ) + self.debug("Static NAT enabled for IP: %s" % + public_ip_3.ipaddress.ipaddress) + except Exception as e: + self.fail("Failed to enable static NAT on IP: %s - %s" % ( + public_ip_3.ipaddress.ipaddress, e)) + + public_ips = PublicIPAddress.list( + self.apiclient, + networkid=network.id, + listall=True, + isstaticnat=True, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(public_ips, list), + True, + "List public Ip for network should list the Ip addr" + ) + self.assertEqual( + public_ips[0].ipaddress, + public_ip_3.ipaddress.ipaddress, + "List public Ip for network should list the Ip addr" + ) + # TODO: Remote Access VPN is not yet supported in VPC + return \ No newline at end of file From c1085ed94bdf8148973ba977ccea0952f6887082 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 14 Mar 2014 21:54:06 +0530 Subject: [PATCH 28/31] fix schema issue --- setup/db/db/schema-430to440.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index b71ce852e4d..6b4601a454e 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -746,4 +746,5 @@ INSERT INTO `cloud`.`storage_pool_details` (pool_id,name,value,display) SELECT s DELETE FROM `cloud`.`data_center_details` WHERE name="storage.overprovisioning.factor"; ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_region_level_vpc boolean default false; ALTER TABLE `cloud`.`network_offerings` ADD COLUMN supports_streched_l2 boolean default false; +ALTER TABLE `cloud`.`networks` ADD COLUMN streched_l2 boolean default false; ALTER TABLE `cloud`.`vpc` ADD COLUMN region_level_vpc boolean default false; From 14a4dd116f09b7dc13a5d8ef79d02aedda57f34f Mon Sep 17 00:00:00 2001 From: Jayapal Date: Fri, 14 Mar 2014 22:28:45 +0530 Subject: [PATCH 29/31] CLOUDSTACK-2692 Assigning LB rule for vm nic secondary ips Conflicts: setup/db/db/schema-430to440.sql --- .../network/lb/LoadBalancingRulesService.java | 2 +- .../apache/cloudstack/api/ApiConstants.java | 2 + .../AssignToLoadBalancerRuleCmd.java | 47 +++++++- .../network/dao/LoadBalancerVMMapDao.java | 2 + .../network/dao/LoadBalancerVMMapDaoImpl.java | 8 ++ .../network/dao/LoadBalancerVMMapVO.java | 19 ++++ .../com/cloud/network/NetworkServiceImpl.java | 22 +++- .../network/as/AutoScaleManagerImpl.java | 2 +- .../lb/LoadBalancingRulesManagerImpl.java | 101 +++++++++++++++--- setup/db/db/schema-430to440.sql | 2 + 10 files changed, 185 insertions(+), 22 deletions(-) diff --git a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java index 4c87d342db5..ee1df33cd41 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java @@ -94,7 +94,7 @@ public interface LoadBalancingRulesService { /** * Assign a virtual machine, or list of virtual machines, to a load balancer. */ - boolean assignToLoadBalancer(long lbRuleId, List vmIds); + boolean assignToLoadBalancer(long lbRuleId, List vmIds, Map> vmIdIpMap); boolean assignSSLCertToLoadBalancerRule(Long lbRuleId, String certName, String publicCert, String privateKey); diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 0dbc1b823c1..6142a0d37db 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -262,6 +262,8 @@ public class ApiConstants { public static final String VALUE = "value"; public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; + public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap"; + public static final String VLAN = "vlan"; public static final String VLAN_RANGE = "vlanrange"; public static final String REMOVE_VLAN = "removevlan"; diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java index 6bd75378272..fad2c2a0c1d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java @@ -16,7 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.loadbalancer; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.util.Iterator; +import java.util.ArrayList; import org.apache.log4j.Logger; @@ -62,10 +67,12 @@ public class AssignToLoadBalancerRuleCmd extends BaseAsyncCmd { type = CommandType.LIST, collectionType = CommandType.UUID, entityType = UserVmResponse.class, - required = true, description = "the list of IDs of the virtual machine that are being assigned to the load balancer rule(i.e. virtualMachineIds=1,2,3)") private List virtualMachineIds; + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID_IP, type = CommandType.MAP, description = "VM ID and IP map, vmidipmap[0].vmid=1 vmidipmap[0].ip=10.1.1.75") + private Map vmIdIpMap; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -78,6 +85,10 @@ public class AssignToLoadBalancerRuleCmd extends BaseAsyncCmd { return virtualMachineIds; } + public Map getVmIdIpMap() { + return vmIdIpMap; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -106,10 +117,42 @@ public class AssignToLoadBalancerRuleCmd extends BaseAsyncCmd { return "applying instances for load balancer: " + getLoadBalancerId() + " (ids: " + StringUtils.join(getVirtualMachineIds(), ",") + ")"; } + + public Map> getVmIdIpListMap() { + Map> vmIdIpsMap = null; + if (vmIdIpMap != null && !vmIdIpMap.isEmpty()) { + vmIdIpsMap = new HashMap>(); + Collection idIpsCollection = vmIdIpMap.values(); + Iterator iter = idIpsCollection.iterator(); + while (iter.hasNext()) { + HashMap idIpsMap = (HashMap)iter.next(); + String vmId = idIpsMap.get("vmid"); + String vmIp = idIpsMap.get("vmip"); + + Long longVmId = new Long(vmId); + + List ipsList = null; + if (vmIdIpsMap.containsKey(longVmId)) { + ipsList = vmIdIpsMap.get(longVmId); + } else { + ipsList = new ArrayList(); + } + ipsList.add(vmIp); + vmIdIpsMap.put(longVmId, ipsList); + + } + } + + return vmIdIpsMap; + } + @Override public void execute() { CallContext.current().setEventDetails("Load balancer Id: " + getLoadBalancerId() + " VmIds: " + StringUtils.join(getVirtualMachineIds(), ",")); - boolean result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds); + + Map> vmIdIpsMap = getVmIdIpListMap(); + + boolean result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds, vmIdIpsMap); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDao.java index 79eaf8e16ba..51f45c219eb 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDao.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDao.java @@ -34,4 +34,6 @@ public interface LoadBalancerVMMapDao extends GenericDao listByInstanceIp(String instanceIp); } diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java index b0b14fc23c1..bb24e042212 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapDaoImpl.java @@ -59,6 +59,14 @@ public class LoadBalancerVMMapDaoImpl extends GenericDaoBase listByInstanceIp(String instanceIp) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("instanceIp", SearchCriteria.Op.EQ, instanceIp); + + return listBy(sc); + } + @Override public List listByLoadBalancerId(long loadBalancerId) { SearchCriteria sc = createSearchCriteria(); diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapVO.java index 0a67106dc9b..08b918475c3 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapVO.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerVMMapVO.java @@ -39,6 +39,9 @@ public class LoadBalancerVMMapVO implements InternalIdentity { @Column(name = "instance_id") private long instanceId; + @Column(name = "instance_ip") + private String instanceIp; + @Column(name = "revoke") private boolean revoke = false; @@ -59,6 +62,13 @@ public class LoadBalancerVMMapVO implements InternalIdentity { this.revoke = revoke; } + public LoadBalancerVMMapVO(long loadBalancerId, long instanceId, String vmIp, boolean revoke) { + this.loadBalancerId = loadBalancerId; + this.instanceId = instanceId; + this.instanceIp = vmIp; + this.revoke = revoke; + } + @Override public long getId() { return id; @@ -87,4 +97,13 @@ public class LoadBalancerVMMapVO implements InternalIdentity { public void setState(String state) { this.state = state; } + + public String getInstanceIp() { + return instanceIp; + } + + public void setInstanceIp(String instanceIp) { + this.instanceIp = instanceIp; + } + } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 9238a1e8ce7..e255976ae8a 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -113,10 +113,12 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.OvsProviderDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.OvsProviderVO; import com.cloud.network.element.VirtualRouterElement; @@ -307,6 +309,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { IpAddressManager _ipAddrMgr; @Inject EntityManager _entityMgr; + @Inject + LoadBalancerVMMapDao _lbVmMapDao; int _cidrLimit; boolean _allowSubdomainNetworkAccess; @@ -806,7 +810,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { s_logger.debug("VM nic IP " + secondaryIp + " is associated with the static NAT rule public IP address id " + publicIpVO.getId()); throw new InvalidParameterValueException("Can' remove the ip " + secondaryIp + "is associate with static NAT rule public IP address id " + publicIpVO.getId()); } - } else if (dc.getNetworkType() == NetworkType.Basic || ntwkOff.getGuestType() == Network.GuestType.Shared) { + + List lbVmMap = _lbVmMapDao.listByInstanceIp(secondaryIp); + if (lbVmMap != null && lbVmMap.size() != 0) { + s_logger.debug("VM nic IP " + secondaryIp + " is mapped to load balancing rule"); + throw new InvalidParameterValueException("Can't remove the secondary ip " + secondaryIp + " is mapped to load balancing rule"); + + } + + } else if (dc.getNetworkType() == NetworkType.Basic || ntwkOff.getGuestType() == Network.GuestType.Shared) { final IPAddressVO ip = _ipAddressDao.findByIpAndSourceNetworkId(secIpVO.getNetworkId(), secIpVO.getIp4Address()); if (ip != null) { Transaction.execute(new TransactionCallbackNoReturn() { @@ -3819,12 +3831,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO pvo = _physicalNetworkDao.findById(physicalNetworkId); DataCenterVO dvo = _dcDao.findById(pvo.getDataCenterId()); if (dvo.getNetworkType() == NetworkType.Basic) { - - Provider provider = Network.Provider.getProvider("BaremetalDhcpProvider"); - if (provider == null) { + + Provider provider = Network.Provider.getProvider("BaremetalDhcpProvider"); + if (provider == null) { // baremetal is not loaded return null; - } + } addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalDhcpProvider", null, null); addProviderToPhysicalNetwork(physicalNetworkId, "BaremetalPxeProvider", null, null); diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java index 99189fe43aa..b6e917dac92 100644 --- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java @@ -1414,7 +1414,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScale } } lstVmId.add(new Long(vmId)); - return _loadBalancingRulesService.assignToLoadBalancer(lbId, lstVmId); + return _loadBalancingRulesService.assignToLoadBalancer(lbId, lstVmId, null); } diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 9f280ef7a80..8a7e0298b45 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -30,6 +30,7 @@ import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.vm.dao.NicSecondaryIpDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; @@ -258,6 +259,9 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements @Inject LoadBalancerCertMapDao _lbCertMapDao; + @Inject + NicSecondaryIpDao _nicSecondaryIpDao; + // Will return a string. For LB Stickiness this will be a json, for // autoscale this will be "," separated values @Override @@ -923,7 +927,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements @Override @DB @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true) - public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds) { + public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds, Map> vmIdIpMap) { CallContext ctx = CallContext.current(); Account caller = ctx.getCallingAccount(); @@ -932,22 +936,68 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements throw new InvalidParameterValueException("Failed to assign to load balancer " + loadBalancerId + ", the load balancer was not found."); } + + if (instanceIds == null && vmIdIpMap == null) { + throw new InvalidParameterValueException("Both instanceids and vmidipmap can't be null"); + } + + // instanceIds and vmIdipmap is passed + if (instanceIds != null && vmIdIpMap != null) { + for(long instanceId: instanceIds) { + if (!vmIdIpMap.containsKey(instanceId)) { + vmIdIpMap.put(instanceId, null); + } + } + } + + //only instanceids list passed + if (instanceIds != null && vmIdIpMap == null){ + vmIdIpMap = new HashMap>(); + for (long instanceId: instanceIds){ + vmIdIpMap.put(instanceId, null); + } + } + List mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false); Set mappedInstanceIds = new HashSet(); for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId())); } - final List vmsToAdd = new ArrayList(); + Map> existingVmIdIps = new HashMap>(); + // now get the ips of vm and add it to map + for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { - if (instanceIds == null || instanceIds.isEmpty()) { - s_logger.warn("List of vms to assign to the lb, is empty"); - return false; + List ipsList = null; + if (existingVmIdIps.containsKey(mappedInstance.getInstanceId())) { + ipsList = existingVmIdIps.get(mappedInstance.getInstanceId()); + } else { + ipsList = new ArrayList(); + } + ipsList.add(mappedInstance.getInstanceIp()); + existingVmIdIps.put(mappedInstance.getInstanceId(), ipsList); } - for (Long instanceId : instanceIds) { - if (mappedInstanceIds.contains(instanceId)) { - throw new InvalidParameterValueException("VM " + instanceId + " is already mapped to load balancer."); + + + + final List vmsToAdd = new ArrayList(); + + // check for conflict + Set passedInstanceIds = vmIdIpMap.keySet(); + for (Long instanceId : passedInstanceIds) { + if (existingVmIdIps.containsKey(instanceId)) { + // now check for ip address + List mappedIps = existingVmIdIps.get(instanceId); + List newIps = vmIdIpMap.get(instanceId); + + if (newIps != null) { + for (String newIp: newIps) { + if (mappedIps.contains(newIp)) { + throw new InvalidParameterValueException("VM " + instanceId + " with " + newIp +" is already mapped to load balancer."); + } + } + } } UserVm vm = _vmDao.findById(instanceId); @@ -985,18 +1035,43 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements throw ex; } + String priIp = nicInSameNetwork.getIp4Address(); + List vmIpsList = vmIdIpMap.get(instanceId); + String vmLbIp = null; + + if (vmIpsList == null) { + vmIpsList = new ArrayList(); + vmIpsList.add(priIp); + vmIdIpMap.put(instanceId, vmIpsList); + } else { + //check if the ips belongs to nic secondary ip + for (String ip: vmIpsList) { + if(_nicSecondaryIpDao.findByIp4AddressAndNicId(ip,nicInSameNetwork.getId()) == null) { + throw new InvalidParameterValueException("VM ip specified " + ip + " is not belongs to nic in network " + nicInSameNetwork.getNetworkId()); + } + } + } + + if (s_logger.isDebugEnabled()) { s_logger.debug("Adding " + vm + " to the load balancer pool"); } vmsToAdd.add(vm); } + final Set vmIds = vmIdIpMap.keySet(); + final Map> newMap = vmIdIpMap; + Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - for (UserVm vm : vmsToAdd) { - LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vm.getId(), false); + + for (Long vmId : vmIds) { + final List lbVmIps = newMap.get(vmId); + for (String vmIp: lbVmIps) { + LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vmId, vmIp, false); map = _lb2VmMapDao.persist(map); + } } } }); @@ -1020,8 +1095,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - for (UserVm vm : vmsToAdd) { - vmInstanceIds.add(vm.getId()); + for (Long vmId : vmIds) { + vmInstanceIds.add(vmId); } } }); @@ -1875,7 +1950,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); - dstIp = nic.getIp4Address(); + dstIp = lbVmMap.getInstanceIp() == null ? nic.getIp4Address(): lbVmMap.getInstanceIp(); LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke()); dstList.add(lbDst); } diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 6b4601a454e..75a303d050d 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -748,3 +748,5 @@ ALTER TABLE `cloud`.`vpc_offerings` ADD COLUMN supports_region_level_vpc boolean ALTER TABLE `cloud`.`network_offerings` ADD COLUMN supports_streched_l2 boolean default false; ALTER TABLE `cloud`.`networks` ADD COLUMN streched_l2 boolean default false; ALTER TABLE `cloud`.`vpc` ADD COLUMN region_level_vpc boolean default false; +ALTER TABLE `cloud`.`load_balancer_vm_map` ADD COLUMN instance_ip VARCHAR(40); +ALTER TABLE `cloud`.`load_balancer_vm_map` DROP KEY `load_balancer_id`, ADD UNIQUE KEY load_balancer_id (`load_balancer_id`, `instance_id`, `instance_ip`); \ No newline at end of file From ba5621012747aae5d8c8f110936dca98b7537b45 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 14 Mar 2014 10:54:27 -0700 Subject: [PATCH 30/31] Deal with concurrent state update for VM and Host objects. --- api/src/com/cloud/vm/VirtualMachine.java | 1 + .../cloud/vm/VirtualMachineManagerImpl.java | 25 ++---- .../src/com/cloud/host/dao/HostDaoImpl.java | 77 ++++++++++--------- .../com/cloud/vm/dao/VMInstanceDaoImpl.java | 52 +++++-------- .../jobs/impl/AsyncJobManagerImpl.java | 18 +++-- 5 files changed, 79 insertions(+), 94 deletions(-) diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index b085d4a7c4e..72cbb252ee7 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -102,6 +102,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I s_fsm.addTransition(State.Running, VirtualMachine.Event.StopRequested, State.Stopping); s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportMigrated, State.Running); + s_fsm.addTransition(State.Running, VirtualMachine.Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Migrating, VirtualMachine.Event.MigrationRequested, State.Migrating); s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationFailed, State.Running); diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index e9d2fd2073e..91d58b81b6c 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -36,12 +36,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; -import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -51,19 +49,9 @@ import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.jobs.AsyncJob; -import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; -import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.framework.jobs.Outcome; -import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao; -import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; -import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; -import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.MessageDispatcher; import org.apache.cloudstack.framework.messagebus.MessageHandler; -import org.apache.cloudstack.jobs.JobInfo; -import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -291,7 +279,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } public void setHostAllocators(List hostAllocators) { - this.hostAllocators = hostAllocators; + hostAllocators = hostAllocators; } protected List _storagePoolAllocators; @@ -3243,9 +3231,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @SuppressWarnings("unchecked") public AgentVmInfo(String name, VMInstanceVO vm, State state, String host) { - this.name = name; - this.state = state; - this.vm = vm; + name = name; + state = state; + vm = vm; hostUuid = host; } @@ -4100,7 +4088,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac List pendingWorkJobs = _workJobDao.listPendingWorkJobs( VirtualMachine.Type.Instance, vmId); - if (pendingWorkJobs.size() == 0 || !_haMgr.hasPendingHaWork(vmId)) { + if (pendingWorkJobs.size() == 0 && !_haMgr.hasPendingHaWork(vmId)) { // there is no pending operation job VMInstanceVO vm = _vmDao.findById(vmId); if (vm != null) { @@ -4407,7 +4395,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean checkCondition() { VMInstanceVO instance = _vmDao.findById(vmId); - if (instance.getPowerState() == desiredPowerState && (srcHostIdForMigration != null && srcHostIdForMigration.equals(instance.getPowerHostId()))) + if ((instance.getPowerState() == desiredPowerState && srcHostIdForMigration == null) || + (instance.getPowerState() == desiredPowerState && (srcHostIdForMigration != null && instance.getPowerHostId() != srcHostIdForMigration))) return true; return false; } diff --git a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java index c2a9826405d..426c90db69c 100755 --- a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java @@ -953,46 +953,49 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao int result = update(ub, sc, null); assert result <= 1 : "How can this update " + result + " rows? "; - if (status_logger.isDebugEnabled() && result == 0) { + if (result == 0) { HostVO ho = findById(host.getId()); assert ho != null : "How how how? : " + host.getId(); - StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); - str.append(". Name=").append(host.getName()); - str.append("; New=[status=") - .append(newStatus.toString()) - .append(":msid=") - .append(newStatus.lostConnection() ? "null" : host.getManagementServerId()) - .append(":lastpinged=") - .append(host.getLastPinged()) - .append("]"); - str.append("; Old=[status=") - .append(oldStatus.toString()) - .append(":msid=") - .append(host.getManagementServerId()) - .append(":lastpinged=") - .append(oldPingTime) - .append("]"); - str.append("; DB=[status=") - .append(vo.getStatus().toString()) - .append(":msid=") - .append(vo.getManagementServerId()) - .append(":lastpinged=") - .append(vo.getLastPinged()) - .append(":old update count=") - .append(oldUpdateCount) - .append("]"); - status_logger.debug(str.toString()); - } else { - StringBuilder msg = new StringBuilder("Agent status update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old status = " + oldStatus); - msg.append("; event = " + event); - msg.append("; new status = " + newStatus); - msg.append("; old update count = " + oldUpdateCount); - msg.append("; new update count = " + newUpdateCount + "]"); - status_logger.debug(msg.toString()); + if (status_logger.isDebugEnabled()) { + + StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); + str.append(". Name=").append(host.getName()); + str.append("; New=[status=") + .append(newStatus.toString()) + .append(":msid=") + .append(newStatus.lostConnection() ? "null" : host.getManagementServerId()) + .append(":lastpinged=") + .append(host.getLastPinged()) + .append("]"); + str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(host.getManagementServerId()).append(":lastpinged=").append(oldPingTime) + .append("]"); + str.append("; DB=[status=") + .append(vo.getStatus().toString()) + .append(":msid=") + .append(vo.getManagementServerId()) + .append(":lastpinged=") + .append(vo.getLastPinged()) + .append(":old update count=") + .append(oldUpdateCount) + .append("]"); + status_logger.debug(str.toString()); + } else { + StringBuilder msg = new StringBuilder("Agent status update: ["); + msg.append("id = " + host.getId()); + msg.append("; name = " + host.getName()); + msg.append("; old status = " + oldStatus); + msg.append("; event = " + event); + msg.append("; new status = " + newStatus); + msg.append("; old update count = " + oldUpdateCount); + msg.append("; new update count = " + newUpdateCount + "]"); + status_logger.debug(msg.toString()); + } + + if (ho.getState() == newStatus) { + status_logger.debug("Host " + ho.getName() + " state has already been updated to " + newStatus); + return true; + } } return result > 0; diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index 2f25f577f47..7c5949290d7 100644 --- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -452,41 +452,29 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem ub.set(vmi, _updateTimeAttr, new Date()); int result = update(vmi, sc); - if (result == 0 && s_logger.isDebugEnabled()) { - + if (result == 0) { VMInstanceVO vo = findByIdIncludingRemoved(vm.getId()); - if (vo != null) { - StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); - str.append(": DB Data={Host=") - .append(vo.getHostId()) - .append("; State=") - .append(vo.getState().toString()) - .append("; updated=") - .append(vo.getUpdated()) - .append("; time=") - .append(vo.getUpdateTime()); - str.append("} New Data: {Host=") - .append(vm.getHostId()) - .append("; State=") - .append(vm.getState().toString()) - .append("; updated=") - .append(vmi.getUpdated()) - .append("; time=") - .append(vo.getUpdateTime()); - str.append("} Stale Data: {Host=") - .append(oldHostId) - .append("; State=") - .append(oldState) - .append("; updated=") - .append(oldUpdated) - .append("; time=") - .append(oldUpdateDate) - .append("}"); - s_logger.debug(str.toString()); + if (s_logger.isDebugEnabled()) { + if (vo != null) { + StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); + str.append(": DB Data={Host=").append(vo.getHostId()).append("; State=").append(vo.getState().toString()).append("; updated=").append(vo.getUpdated()) + .append("; time=").append(vo.getUpdateTime()); + str.append("} New Data: {Host=").append(vm.getHostId()).append("; State=").append(vm.getState().toString()).append("; updated=").append(vmi.getUpdated()) + .append("; time=").append(vo.getUpdateTime()); + str.append("} Stale Data: {Host=").append(oldHostId).append("; State=").append(oldState).append("; updated=").append(oldUpdated).append("; time=") + .append(oldUpdateDate).append("}"); + s_logger.debug(str.toString()); - } else { - s_logger.debug("Unable to update the vm id=" + vm.getId() + "; the vm either doesn't exist or already removed"); + } else { + s_logger.debug("Unable to update the vm id=" + vm.getId() + "; the vm either doesn't exist or already removed"); + } + } + + if (vo != null && vo.getState() == newState) { + // allow for concurrent update if target state has already been matched + s_logger.debug("VM " + vo.getInstanceName() + " state has been already been updated to " + newState); + return true; } } return result > 0; diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index 49c3032b120..7f3701d7e4a 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -256,13 +256,17 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, } }); - for (Long id : wakeupList) { - // TODO, we assume that all jobs in this category is API job only - AsyncJobVO jobToWakeup = _jobDao.findById(id); - if (jobToWakeup != null && (jobToWakeup.getPendingSignals() & AsyncJob.Constants.SIGNAL_MASK_WAKEUP) != 0) - scheduleExecution(jobToWakeup, false); - } - + // + // disable wakeup scheduling now, since all API jobs are currently using block-waiting for sub-jobs + // + /* + for (Long id : wakeupList) { + // TODO, we assume that all jobs in this category is API job only + AsyncJobVO jobToWakeup = _jobDao.findById(id); + if (jobToWakeup != null && (jobToWakeup.getPendingSignals() & AsyncJob.Constants.SIGNAL_MASK_WAKEUP) != 0) + scheduleExecution(jobToWakeup, false); + } + */ _messageBus.publish(null, AsyncJob.Topics.JOB_STATE, PublishScope.GLOBAL, jobId); } From 1db329f0f9d9814c09b1158a0127d5b7f337b2dd Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 14 Mar 2014 14:22:23 -0700 Subject: [PATCH 31/31] Fix the missing java imports from automatic merge --- .../src/com/cloud/vm/VirtualMachineManagerImpl.java | 12 ++++++++++++ .../framework/jobs/impl/AsyncJobManagerImpl.java | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 91d58b81b6c..ae882bfbb40 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -36,10 +36,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -49,9 +51,19 @@ import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.AsyncJob; +import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.Outcome; +import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.MessageDispatcher; import org.apache.cloudstack.framework.messagebus.MessageHandler; +import org.apache.cloudstack.jobs.JobInfo; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index 7f3701d7e4a..4acfdbfd32d 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -259,7 +259,7 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, // // disable wakeup scheduling now, since all API jobs are currently using block-waiting for sub-jobs // - /* + /* for (Long id : wakeupList) { // TODO, we assume that all jobs in this category is API job only AsyncJobVO jobToWakeup = _jobDao.findById(id);