From b1ef74737af85997fa250ccbb53089ecb20a65eb Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Sun, 7 Apr 2013 00:13:46 -0700 Subject: [PATCH 01/81] CLOUDSTACK-1065: cloudstack UI - AWS Style Regions - when switching region, set location without parameters. --- ui/scripts/cloud.core.callbacks.js | 41 +++++++++++++++++------------- ui/scripts/cloudStack.js | 14 +++++----- ui/scripts/sharedFunctions.js | 2 +- ui/scripts/ui-custom/regions.js | 4 +-- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/ui/scripts/cloud.core.callbacks.js b/ui/scripts/cloud.core.callbacks.js index 1a9e0456d0b..a081294293c 100644 --- a/ui/scripts/cloud.core.callbacks.js +++ b/ui/scripts/cloud.core.callbacks.js @@ -52,25 +52,31 @@ Below is a sample login attempt var clientApiUrl = "/client/api"; var clientConsoleUrl = "/client/console"; -$(document).ready(function() { - /* - condition 1: If window.location.href contains parameter 'loginUrl', save the parameter's value to a cookie, then reload the page without any URL parameter. - (After the page is reloaded without any URL parameter, it will fall in condition 2.) - */ - if ($.urlParam('loginUrl') != 0) { - $.cookie('loginUrl', $.urlParam('loginUrl'), { expires: 1}); - document.location.href = window.location.href.substring(0, window.location.href.indexOf('?')); - } +$(document).ready(function() { - /* - condition 2: If window.location.href does not contain parameter 'loginUrl' but cookie 'loginUrl' exists, - save the cookie's value to g_regionUrlParam (a global variable for switching regions), - then call login API to set g_loginResponse (a global variable for single-sign-on). - */ - else if($.cookie('loginUrl') != null) { - g_regionUrlParam = '?loginUrl=' + $.cookie('loginUrl'); + var url = $.urlParam("loginUrl"); + if (url != undefined && url != null && url.length > 0) { + url = unescape(clientApiUrl+"?"+url); $.ajax({ - url: unescape(clientApiUrl + "?" + $.cookie('loginUrl')), + url: url, + dataType: "json", + async: false, + success: function(json) { + g_loginResponse = json.loginresponse; + }, + error: function() { + onLogoutCallback(); + // This means the login failed. You should redirect to your login page. + }, + beforeSend: function(XMLHttpRequest) { + return true; + } + }); + } + else if(window.name != null && window.name.indexOf("command=login") != -1) { //from region switching + g_loginCmdText = window.name; + $.ajax({ + url: clientApiUrl + "?" + window.name, dataType: "json", async: false, success: function(json) { @@ -85,7 +91,6 @@ $(document).ready(function() { } }); } - }); diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js index 985627b824e..499cb833040 100644 --- a/ui/scripts/cloudStack.js +++ b/ui/scripts/cloudStack.js @@ -251,12 +251,11 @@ array1.push("&domain=" + encodeURIComponent("/")); } - g_regionUrlParam = '?loginUrl=' + escape("command=login" + array1.join("") + "&response=json"); - $.cookie('loginUrl', escape("command=login" + array1.join("") + "&response=json"), { expires: 1}); + g_loginCmdText = "command=login" + array1.join("") + "&response=json"; $.ajax({ type: "POST", - data: "command=login" + array1.join("") + "&response=json", + data: g_loginCmdText, dataType: "json", async: false, success: function(json) { @@ -386,7 +385,8 @@ g_timezoneoffset = null; g_timezone = null; g_supportELB = null; - g_regionUrlParam = null; + g_loginCmdText = null; + window.name = ''; $.cookie('JSESSIONID', null); $.cookie('sessionKey', null); @@ -398,8 +398,7 @@ $.cookie('timezoneoffset', null); $.cookie('timezone', null); $.cookie('supportELB', null); - $.cookie('loginUrl', null); - + if(onLogoutCallback()) { //onLogoutCallback() will set g_loginResponse(single-sign-on variable) to null, then bypassLoginCheck() will show login screen. document.location.reload(); //when onLogoutCallback() returns true, reload the current document. } @@ -466,7 +465,8 @@ document.title = 'CloudStack'; - if ($.cookie('loginUrl') != null || $.urlParam('loginUrl') != 0) { + if ($.urlParam('loginUrl') != 0 + ||(window.name != null && window.name.indexOf("command=login") != -1)) { // SSO loginArgs.hideLoginScreen = true; } diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index dbcb781a6fa..86fe7f6416c 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -20,7 +20,7 @@ var g_role = null; // roles - root, domain-admin, ro-admin, user var g_username = null; var g_account = null; var g_domainid = null; -var g_regionUrlParam = null; +var g_loginCmdText = null; var g_enableLogging = false; var g_timezoneoffset = null; var g_timezone = null; diff --git a/ui/scripts/ui-custom/regions.js b/ui/scripts/ui-custom/regions.js index 17bc86cb5ad..2f61dfddeaf 100644 --- a/ui/scripts/ui-custom/regions.js +++ b/ui/scripts/ui-custom/regions.js @@ -89,8 +89,8 @@ closeRegionSelector({ complete: function() { $('#container').prepend($('
').addClass('loading-overlay')); - - document.location.href = url + g_regionUrlParam; + window.name = g_loginCmdText; + document.location.href = url; } }); }; From a85b498afd224fbc34e52b032ee4ba05f141f2a0 Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Sun, 7 Apr 2013 17:41:26 +0800 Subject: [PATCH 02/81] 1) fix CLOUDSTACK-1946 2) remove redundant VMSnapshotVO.java --- .../src/com/cloud/vm/UserVmManagerImpl.java | 22 +- .../com/cloud/vm/snapshot/VMSnapshotVO.java | 224 ------------------ 2 files changed, 4 insertions(+), 242 deletions(-) delete mode 100644 server/src/com/cloud/vm/snapshot/VMSnapshotVO.java diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 24bce8b1705..646cf9285e3 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -222,7 +222,9 @@ import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.VMSnapshotManager; +import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Local(value = { UserVmManager.class, UserVmService.class }) @@ -699,22 +701,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } - - - - private void checkVMSnapshots(UserVmVO vm, Long volumeId, boolean attach) { - // Check that if vm has any VM snapshot - /*Long vmId = vm.getId(); - List listSnapshot = _vmSnapshotDao.listByInstanceId(vmId, - VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging); - if (listSnapshot != null && listSnapshot.size() != 0) { - throw new InvalidParameterValueException( - "The VM has VM snapshots, do not allowed to attach volume. Please delete the VM snapshots first."); - }*/ - } - - - private UserVm rebootVirtualMachine(long userId, long vmId) throws InsufficientCapacityException, ResourceUnavailableException { UserVmVO vm = _vmDao.findById(vmId); @@ -777,7 +763,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use _itMgr.checkIfCanUpgrade(vmInstance, svcOffId); // remove diskAndMemory VM snapshots - /* List vmSnapshots = _vmSnapshotDao.findByVm(vmId); + List vmSnapshots = _vmSnapshotDao.findByVm(vmId); for (VMSnapshotVO vmSnapshotVO : vmSnapshots) { if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory){ if(!_vmSnapshotMgr.deleteAllVMSnapshots(vmId, VMSnapshot.Type.DiskAndMemory)){ @@ -787,7 +773,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } - }*/ + } _itMgr.upgradeVmDb(vmId, svcOffId); diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotVO.java b/server/src/com/cloud/vm/snapshot/VMSnapshotVO.java deleted file mode 100644 index 03d4945fda0..00000000000 --- a/server/src/com/cloud/vm/snapshot/VMSnapshotVO.java +++ /dev/null @@ -1,224 +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.vm.snapshot; - -import java.util.Date; -import java.util.UUID; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.TableGenerator; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; - -import com.cloud.utils.db.GenericDao; - -@Entity -@Table(name = "vm_snapshots") -public class VMSnapshotVO implements VMSnapshot { - @Id - @TableGenerator(name = "vm_snapshots_sq", table = "sequence", pkColumnName = "name", valueColumnName = "value", pkColumnValue = "vm_snapshots_seq", allocationSize = 1) - @GeneratedValue(strategy = GenerationType.TABLE) - @Column(name = "id") - long id; - - @Column(name = "uuid") - String uuid = UUID.randomUUID().toString(); - - @Column(name = "name") - String name; - - @Column(name = "display_name") - String displayName; - - @Column(name = "description") - String description; - - @Column(name = "vm_id") - long vmId; - - @Column(name = "account_id") - long accountId; - - @Column(name = "domain_id") - long domainId; - - @Column(name = "vm_snapshot_type") - @Enumerated(EnumType.STRING) - VMSnapshot.Type type; - - @Column(name = "state", updatable = true, nullable = false) - @Enumerated(value = EnumType.STRING) - private State state; - - @Column(name = GenericDao.CREATED_COLUMN) - Date created; - - @Column(name = GenericDao.REMOVED_COLUMN) - Date removed; - - @Column(name = "current") - Boolean current; - - @Column(name = "parent") - Long parent; - - @Column(name = "updated") - @Temporal(value = TemporalType.TIMESTAMP) - Date updated; - - @Column(name="update_count", updatable = true, nullable=false) - protected long updatedCount; - - public Long getParent() { - return parent; - } - - public void setParent(Long parent) { - this.parent = parent; - } - - public VMSnapshotVO() { - - } - - public Date getRemoved() { - return removed; - } - - public VMSnapshotVO(Long accountId, Long domainId, Long vmId, - String description, String vmSnapshotName, String vsDisplayName, - Long serviceOfferingId, Type type, Boolean current) { - this.accountId = accountId; - this.domainId = domainId; - this.vmId = vmId; - this.state = State.Allocated; - this.description = description; - this.name = vmSnapshotName; - this.displayName = vsDisplayName; - this.type = type; - this.current = current; - } - - public String getDescription() { - return description; - } - - @Override - public Date getCreated() { - return created; - } - - public void setCreated(Date created) { - this.created = created; - } - - @Override - public long getId() { - return id; - } - - @Override - public Long getVmId() { - return vmId; - } - - public void setVmId(Long vmId) { - this.vmId = vmId; - } - - @Override - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public State getState() { - return state; - } - - public void setState(State state) { - this.state = state; - } - - @Override - public String getUuid() { - return uuid; - } - - @Override - public long getAccountId() { - return accountId; - } - - @Override - public long getDomainId() { - return domainId; - } - - @Override - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - public Boolean getCurrent() { - return current; - } - - public void setCurrent(Boolean current) { - this.current = current; - } - - @Override - public long getUpdatedCount() { - return updatedCount; - } - - @Override - public void incrUpdatedCount() { - this.updatedCount++; - } - - @Override - public Date getUpdated() { - return updated; - } - - @Override - public Type getType() { - return type; - } - - public void setRemoved(Date removed) { - this.removed = removed; - } -} From d9f7bb2854c812516caca4985a846e30258a8f7b Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Sun, 7 Apr 2013 12:08:30 +0200 Subject: [PATCH 03/81] Changing the number of threads in the test from 1000 to 750. 1000 will more often than not result in build failures due to constraints on the build systems. Most recent centos based systems have process limit of 1024. Running this test will result in an OutOfMemory exception with description unable to create native thread. --- .../test/com/cloud/network/security/SecurityGroupQueueTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/test/com/cloud/network/security/SecurityGroupQueueTest.java b/server/test/com/cloud/network/security/SecurityGroupQueueTest.java index b9e417675e4..e6237850825 100644 --- a/server/test/com/cloud/network/security/SecurityGroupQueueTest.java +++ b/server/test/com/cloud/network/security/SecurityGroupQueueTest.java @@ -139,7 +139,7 @@ public class SecurityGroupQueueTest extends TestCase { testNumJobsEqToNumVms2(400,5000); testNumJobsEqToNumVms2(1,1); testNumJobsEqToNumVms2(1,1000000); - testNumJobsEqToNumVms2(1000,1); + testNumJobsEqToNumVms2(750,1); } From ebcdef55b0ea823ea5c9c97ad8713d702d6c168d Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Sun, 7 Apr 2013 08:59:08 -0700 Subject: [PATCH 04/81] CLOUDSTACK-1065: cloudstack UI - AWS Style Regions - shorten value of g_loginCmdText --- ui/scripts/cloud.core.callbacks.js | 4 ++-- ui/scripts/cloudStack.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/scripts/cloud.core.callbacks.js b/ui/scripts/cloud.core.callbacks.js index a081294293c..7fd1b4d1244 100644 --- a/ui/scripts/cloud.core.callbacks.js +++ b/ui/scripts/cloud.core.callbacks.js @@ -73,10 +73,10 @@ $(document).ready(function() { } }); } - else if(window.name != null && window.name.indexOf("command=login") != -1) { //from region switching + else if(window.name != null && window.name.indexOf("&domain=") != -1) { //from region switching g_loginCmdText = window.name; $.ajax({ - url: clientApiUrl + "?" + window.name, + url: clientApiUrl + "?command=login" + window.name + "&response=json", dataType: "json", async: false, success: function(json) { diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js index 499cb833040..faa77899da1 100644 --- a/ui/scripts/cloudStack.js +++ b/ui/scripts/cloudStack.js @@ -250,12 +250,12 @@ else { array1.push("&domain=" + encodeURIComponent("/")); } - - g_loginCmdText = "command=login" + array1.join("") + "&response=json"; + + g_loginCmdText = array1.join(""); $.ajax({ type: "POST", - data: g_loginCmdText, + data: "command=login" + g_loginCmdText + "&response=json", dataType: "json", async: false, success: function(json) { @@ -466,7 +466,7 @@ document.title = 'CloudStack'; if ($.urlParam('loginUrl') != 0 - ||(window.name != null && window.name.indexOf("command=login") != -1)) { + ||(window.name != null && window.name.indexOf("&domain=") != -1)) { // SSO loginArgs.hideLoginScreen = true; } From 7de2b4b30adbf48922f849eb10fd5f49f6d19ad0 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Sun, 7 Apr 2013 11:40:46 -0700 Subject: [PATCH 05/81] CLOUDSTACK-1957: cloudstack UI - fix a JS error "'logout' is undefined" which sometimes shows on login screen. --- ui/scripts/cloudStack.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js index faa77899da1..0cf80b02066 100644 --- a/ui/scripts/cloudStack.js +++ b/ui/scripts/cloudStack.js @@ -115,6 +115,11 @@ // Use this for checking the session, to bypass login screen bypassLoginCheck: function(args) { //determine to show or bypass login screen if (g_loginResponse == null) { //show login screen + /* + but if this is a 2nd browser window (of the same domain), login screen still won't show because $.cookie('sessionKey') is valid for 2nd browser window (of the same domain) as well. + i.e. calling listCapabilities API with g_sessionKey from $.cookie('sessionKey') will succeed, + then userValid will be set to true, then an user object (instead of "false") will be returned, then login screen will be bypassed. + */ g_mySession = $.cookie('JSESSIONID'); g_sessionKey = $.cookie('sessionKey'); g_role = $.cookie('role'); @@ -176,8 +181,7 @@ userValid = true; }, - error: function(xmlHTTP) { - logout(false); + error: function(xmlHTTP) { //override default error handling, do nothing instead of showing error "unable to verify user credentials" on login screen }, beforeSend : function(XMLHttpResponse) { return true; From c9c68e19283535266e69a96c57e5bb453d4791d6 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Sun, 7 Apr 2013 21:46:38 +0200 Subject: [PATCH 06/81] Replace restart with SIGHUP This signal will force the dnsmasq daemon to reload the configuration directly. This is much faster than restarting the daemon, which result in a much smaller window during which no dns server is available. Tested by using the replaced version of edithosts.sh on a running vrouter causing dns problems. --- patches/systemvm/debian/config/root/edithosts.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patches/systemvm/debian/config/root/edithosts.sh b/patches/systemvm/debian/config/root/edithosts.sh index 9f21f206172..1f98fbf96ae 100755 --- a/patches/systemvm/debian/config/root/edithosts.sh +++ b/patches/systemvm/debian/config/root/edithosts.sh @@ -200,7 +200,8 @@ fi pid=$(pidof dnsmasq) if [ "$pid" != "" ] then - service dnsmasq restart + #service dnsmasq restart + kill -HUP $pid else if [ $no_redundant -eq 1 ] then From f09d981c3e55d3acf2506679f43300869e8d8f30 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Mon, 8 Apr 2013 12:34:57 +0200 Subject: [PATCH 07/81] Switching netscaler to the maven location of the netscaler.nitro jar. If it's there we might as well use it. This fixes build problems for people with old netscaler jars as the version was not updated in the jar. --- plugins/network-elements/netscaler/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/network-elements/netscaler/pom.xml b/plugins/network-elements/netscaler/pom.xml index 1eb73a236dc..d5c7cdc5bcc 100644 --- a/plugins/network-elements/netscaler/pom.xml +++ b/plugins/network-elements/netscaler/pom.xml @@ -28,9 +28,9 @@ - com.cloud.com.citrix - netscaler - 1.0 + com.citrix.netscaler.nitro + nitro + 10.0.e com.cloud.com.citrix From 04a7fed35f27c20bdd2b10ed30367d6cdb751779 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 8 Apr 2013 17:09:54 +0200 Subject: [PATCH 08/81] CLOUDSTACK-1935: Rename tools from cloud-* to cloudstack-* --- debian/cloudstack-agent.install | 6 +++--- debian/cloudstack-common.install | 8 ++++---- debian/cloudstack-management.install | 8 ++++---- debian/rules | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/debian/cloudstack-agent.install b/debian/cloudstack-agent.install index 02501855354..a3cc86964dd 100644 --- a/debian/cloudstack-agent.install +++ b/debian/cloudstack-agent.install @@ -5,9 +5,9 @@ # 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 @@ -20,7 +20,7 @@ /etc/cloudstack/agent/log4j-cloud.xml /etc/init.d/cloudstack-agent /usr/bin/cloudstack-setup-agent -/usr/bin/cloud-ssh +/usr/bin/cloudstack-ssh /var/log/cloudstack/agent /usr/share/cloudstack-agent/lib/* /usr/share/cloudstack-agent/plugins diff --git a/debian/cloudstack-common.install b/debian/cloudstack-common.install index 021474e1b65..7e01adadedf 100644 --- a/debian/cloudstack-common.install +++ b/debian/cloudstack-common.install @@ -5,9 +5,9 @@ # 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 @@ -27,6 +27,6 @@ /usr/share/cloudstack-common/scripts/vm/hypervisor/versions.sh /usr/share/cloudstack-common/scripts/vm/hypervisor/xenserver/* /usr/share/cloudstack-common/lib/* -/usr/bin/cloud-set-guest-password -/usr/bin/cloud-set-guest-sshkey +/usr/bin/cloudstack-set-guest-password +/usr/bin/cloudstack-set-guest-sshkey /usr/lib/python2.?/*-packages/* diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index 6d87748c757..ed7744b2afb 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -25,8 +25,8 @@ /var/log/cloudstack/management /var/lib/cloudstack/mnt /var/lib/cloudstack/management -/usr/bin/cloud-update-xenserver-licenses -/usr/bin/cloud-setup-management -/usr/bin/cloud-setup-databases -/usr/bin/cloud-migrate-databases +/usr/bin/cloudstack-update-xenserver-licenses +/usr/bin/cloudstack-setup-management +/usr/bin/cloudstack-setup-databases +/usr/bin/cloudstack-migrate-databases /usr/share/cloudstack-management/* diff --git a/debian/rules b/debian/rules index f05277ed5d1..c50214a30a4 100755 --- a/debian/rules +++ b/debian/rules @@ -69,7 +69,7 @@ install: install -D plugins/hypervisors/kvm/target/dependencies/* $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D packaging/debian/init/cloud-agent $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-agent install -D agent/bindir/cloud-setup-agent.in $(DESTDIR)/usr/bin/cloudstack-setup-agent - install -D agent/bindir/cloud-ssh.in $(DESTDIR)/usr/bin/cloud-ssh + install -D agent/bindir/cloud-ssh.in $(DESTDIR)/usr/bin/cloudstack-ssh install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent # cloudstack-management @@ -100,7 +100,7 @@ install: ln -s tomcat6-nonssl.conf $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management/tomcat6.conf ln -s server-nonssl.xml $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management/server.xml install -D packaging/debian/init/cloud-management $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-management - install -D client/bindir/cloud-update-xenserver-licenses.in $(DESTDIR)/usr/bin/cloud-update-xenserver-licenses + install -D client/bindir/cloud-update-xenserver-licenses.in $(DESTDIR)/usr/bin/cloudstack-update-xenserver-licenses ln -s /usr/share/tomcat6/bin $(DESTDIR)/usr/share/$(PACKAGE)-management/bin # Remove configuration in /ur/share/cloudstack-management/webapps/client/WEB-INF # This should all be in /etc/cloudstack/management @@ -121,11 +121,11 @@ install: cp -r scripts/storage $(DESTDIR)/usr/share/$(PACKAGE)-common/scripts cp -r scripts/util $(DESTDIR)/usr/share/$(PACKAGE)-common/scripts cp -r scripts/vm $(DESTDIR)/usr/share/$(PACKAGE)-common/scripts - install -D client/target/utilities/bin/cloud-migrate-databases $(DESTDIR)/usr/bin - install -D client/target/utilities/bin/cloud-set-guest-password $(DESTDIR)/usr/bin - install -D client/target/utilities/bin/cloud-set-guest-sshkey $(DESTDIR)/usr/bin - install -D client/target/utilities/bin/cloud-setup-databases $(DESTDIR)/usr/bin - install -D client/target/utilities/bin/cloud-setup-management $(DESTDIR)/usr/bin + install -D client/target/utilities/bin/cloud-migrate-databases $(DESTDIR)/usr/bin/cloudstack-migrate-databases + install -D client/target/utilities/bin/cloud-set-guest-password $(DESTDIR)/usr/bin/cloudstack-set-guest-password + install -D client/target/utilities/bin/cloud-set-guest-sshkey $(DESTDIR)/usr/bin/cloudstack-set-guest-sshkey + install -D client/target/utilities/bin/cloud-setup-databases $(DESTDIR)/usr/bin/cloudstack-setup-databases + install -D client/target/utilities/bin/cloud-setup-management $(DESTDIR)/usr/bin/cloudstack-setup-management install -D services/console-proxy/server/dist/systemvm.iso $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/systemvm.iso # We need jasypt for cloud-install-sys-tmplt, so this is a nasty hack to get it into the right place install -D agent/target/dependencies/jasypt-1.9.0.jar $(DESTDIR)/usr/share/$(PACKAGE)-common/lib From 7214375c72dce863a92386310a7a31e53a277343 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Mon, 8 Apr 2013 11:27:25 -0500 Subject: [PATCH 09/81] Continuing work on release notes. --- docs/en-US/Release_Notes.xml | 52 +++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index be0267dd4b3..dd93aa46a9c 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -35,9 +35,16 @@ Upgrade from 4.0.x to 4.1.0 This section will guide you from Apache CloudStack 4.0.x versions (4.0.0-incubating, 4.0.1-incubating, and 4.0.2) to &PRODUCT; 4.1.0. Any steps that are hypervisor-specific will be called out with a note. + Package Structure Changes + The package structure for &PRODUCT; has changed significantly since the 4.0.x releases. If you've compiled your own packages, you'll notice that the package names and the number of packages has changed. This is not a bug. + However, this does mean that the procedure is not as simple as an apt-get upgrade or yum update, so please follow this section carefully. + + We recommend reading through this section once or twice before beginning your upgrade procedure, and working through it on a test system before working on a production system. - Create RPM or Debian packages (as appropriate) and a repository from the 4.1.0 source, or check the Apache CloudStack downloads page at http://cloudstack.apache.org/downloads.html for package repositories supplied by community members. Instructions for creating packages from the &PRODUCT; source are in the Installation Guide. + Most users of &PRODUCT; manage the installation and upgrades of &PRODUCT; with one of Linux's predominant package systems, RPM or APT. This guide assumes you'll be using RPM and Yum (for Red Hat Enterprise Linux or CentOS), or APT and Debian packages (for Ubuntu). + Create RPM or Debian packages (as appropriate) and a repository from the 4.1.0 source, or check the Apache CloudStack downloads page at http://cloudstack.apache.org/downloads.html for package repositories supplied by community members. You will need them for step or step . + Instructions for creating packages from the &PRODUCT; source are in the Installation Guide. Stop your management server or servers. Run this on all management server hosts: @@ -47,6 +54,41 @@ Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. # mysqldump -u root -p cloud > cloudstack-backup.sql + + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . + Community Packages + This section assumes you're using the community supplied packages for &PRODUCT;. If you've created your own packages and APT repository, substitute your own URL for the ones used in these examples. + + + Start by opening /etc/apt/sources.list.d/cloudstack.list. + This file should have one line, which contains: + deb http://cloudstack.apt-get.eu/ubuntu precise 4.0 + We'll change it to point to the new package repository: + deb http://cloudstack.apt-get.eu/ubuntu precise 4.1 + + + Now update your apt package list: + $ sudo apt-get update + + + You will need to manually install the cloudstack-agent package: ### + $ sudo apt-get install cloudstack-agent + During the installation of cloudstack-agent, APT will copy your agent.properties, log4j-cloud.xml, and environment.properties from /etc/cloud/agent to /etc/cloudstack/agent. + When prompted whether you wish to keep your configuration, say Yes. + + + Verify that the file /etc/cloudstack/agent/environment.properties has a line that reads: + paths.script=/usr/share/cloudstack-common + If not, add the line. + + + Restart the agent: + + + + + ### +
@@ -1049,11 +1091,9 @@ Done restarting router(s). - CLOUDSTACK-301 - Nexus 1000v DVS integration is not functional - This source code release includes some partial functionality to support the - Cisco Nexus 1000v Distributed Virtual Switch within a VMware hypervisor - environment. The functionality is not complete at this time. + CLOUDSTACK-1824 + Service CloudStack-Management is being displayed as cloud-management service + Many scripts and text entries have references to cloud-management rather than cloudstack-management due to the changeover between 4.0 and 4.1 to rename services. This is a minor issue and should be corrected by 4.2. From 4c6697fd236583c100e57c41121f23469636bd23 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Mon, 8 Apr 2013 11:31:13 -0500 Subject: [PATCH 10/81] Removing a few stray characters. --- docs/en-US/Release_Notes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index dd93aa46a9c..9edc1920c46 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -71,7 +71,7 @@ $ sudo apt-get update - You will need to manually install the cloudstack-agent package: ### + You will need to manually install the cloudstack-agent package: $ sudo apt-get install cloudstack-agent During the installation of cloudstack-agent, APT will copy your agent.properties, log4j-cloud.xml, and environment.properties from /etc/cloud/agent to /etc/cloudstack/agent. When prompted whether you wish to keep your configuration, say Yes. From 68a30d41dbd2c1f71cb1a9d3ed4d4d2f949aca05 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Mon, 8 Apr 2013 16:19:01 -0500 Subject: [PATCH 11/81] added minor issues to known issues. --- docs/en-US/Release_Notes.xml | 2319 ++++++++++++++++++---------------- 1 file changed, 1227 insertions(+), 1092 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 9edc1920c46..b8465ed2e82 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -4,1104 +4,1239 @@ %BOOK_ENTITIES; ]> - - Welcome to &PRODUCT; 4.1 - Welcome to the 4.1.0 release of &PRODUCT;, the first major release from the Apache CloudStack project since its graduation from the Apache Incubator. - This document contains information specific to this release of &PRODUCT;, including upgrade instructions from prior releases, new features added to &PRODUCT;, API changes, and issues fixed in the release. For installation instructions, please see the Installation Guide. For usage and administration instructions, please see the &PRODUCT; Administrator's Guide. Developers and users who wish to work with the API will find instruction in the &PRODUCT; API Developer's Guide - If you find any errors or problems in this guide, please see . We hope you enjoy working with &PRODUCT;! - - - Upgrade Instructions - This section contains upgrade instructions from prior versions of CloudStack to Apache CloudStack 4.1.0. We include instructions on upgrading to Apache CloudStack from pre-Apache versions of Citrix CloudStack (last version prior to Apache is 3.0.2) and from the releases made while CloudStack was in the Apache Incubator. - If you run into any issues during upgrades, please feel free to ask questions on users@apache.cloudstack.org or dev@apache.cloudstack.org. -
- Upgrade from 4.0.x to 4.1.0 - This section will guide you from Apache CloudStack 4.0.x versions (4.0.0-incubating, 4.0.1-incubating, and 4.0.2) to &PRODUCT; 4.1.0. - Any steps that are hypervisor-specific will be called out with a note. - Package Structure Changes - The package structure for &PRODUCT; has changed significantly since the 4.0.x releases. If you've compiled your own packages, you'll notice that the package names and the number of packages has changed. This is not a bug. - However, this does mean that the procedure is not as simple as an apt-get upgrade or yum update, so please follow this section carefully. - - We recommend reading through this section once or twice before beginning your upgrade procedure, and working through it on a test system before working on a production system. - - - Most users of &PRODUCT; manage the installation and upgrades of &PRODUCT; with one of Linux's predominant package systems, RPM or APT. This guide assumes you'll be using RPM and Yum (for Red Hat Enterprise Linux or CentOS), or APT and Debian packages (for Ubuntu). - Create RPM or Debian packages (as appropriate) and a repository from the 4.1.0 source, or check the Apache CloudStack downloads page at http://cloudstack.apache.org/downloads.html for package repositories supplied by community members. You will need them for step or step . - Instructions for creating packages from the &PRODUCT; source are in the Installation Guide. - - - Stop your management server or servers. Run this on all management server hosts: - # service cloud-management stop - - - Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. - # mysqldump -u root -p cloud > cloudstack-backup.sql - - - If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . - Community Packages - This section assumes you're using the community supplied packages for &PRODUCT;. If you've created your own packages and APT repository, substitute your own URL for the ones used in these examples. - - - Start by opening /etc/apt/sources.list.d/cloudstack.list. - This file should have one line, which contains: - deb http://cloudstack.apt-get.eu/ubuntu precise 4.0 - We'll change it to point to the new package repository: - deb http://cloudstack.apt-get.eu/ubuntu precise 4.1 - - - Now update your apt package list: - $ sudo apt-get update - - - You will need to manually install the cloudstack-agent package: - $ sudo apt-get install cloudstack-agent - During the installation of cloudstack-agent, APT will copy your agent.properties, log4j-cloud.xml, and environment.properties from /etc/cloud/agent to /etc/cloudstack/agent. - When prompted whether you wish to keep your configuration, say Yes. - - - Verify that the file /etc/cloudstack/agent/environment.properties has a line that reads: - paths.script=/usr/share/cloudstack-common - If not, add the line. - - - Restart the agent: - - - - - ### - - -
-
- Upgrade from 3.0.2 to 4.0.0-incubating - Perform the following to upgrade from version 3.0.2 to version 4.0.0-incubating. Note - that some of the steps here are only required if you're using a specific hypervisor. The - steps that are hypervisor-specific are called out with a note. - - - Ensure that you query your IP address usage records and process them or make a - backup. During the upgrade you will lose the old IP address usage records. - Starting in 3.0.2, the usage record format for IP addresses is the same as the rest - of the usage types. Instead of a single record with the assignment and release dates, - separate records are generated per aggregation period with start and end dates. After - upgrading, any existing IP address usage records in the old format will no longer be - available. - - - - The following upgrade instructions apply only if you're using VMware hosts. If - you're not using VMware hosts, skip this step and move on to step 3: stopping all - usage servers. - - In each zone that includes VMware hosts, you need to add a new system VM template. - - - While running the existing 3.0.2 system, log in to the UI as root - administrator. - - - In the left navigation bar, click Templates. - - - In Select view, click Templates. - - - Click Register template. - The Register template dialog box is displayed. - - - In the Register template dialog box, specify the following values (do not change - these): - + + Welcome to &PRODUCT; 4.1 + Welcome to the 4.1.0 release of &PRODUCT;, the first major release from the Apache CloudStack project since its graduation from the Apache Incubator. + This document contains information specific to this release of &PRODUCT;, including upgrade instructions from prior releases, new features added to &PRODUCT;, API changes, and issues fixed in the release. For installation instructions, please see the Installation Guide. For usage and administration instructions, please see the &PRODUCT; Administrator's Guide. Developers and users who wish to work with the API will find instruction in the &PRODUCT; API Developer's Guide + If you find any errors or problems in this guide, please see . We hope you enjoy working with &PRODUCT;! + + + Upgrade Instructions + This section contains upgrade instructions from prior versions of CloudStack to Apache CloudStack 4.1.0. We include instructions on upgrading to Apache CloudStack from pre-Apache versions of Citrix CloudStack (last version prior to Apache is 3.0.2) and from the releases made while CloudStack was in the Apache Incubator. + If you run into any issues during upgrades, please feel free to ask questions on users@apache.cloudstack.org or dev@apache.cloudstack.org. +
+ Upgrade from 4.0.x to 4.1.0 + This section will guide you from Apache CloudStack 4.0.x versions (4.0.0-incubating, 4.0.1-incubating, and 4.0.2) to &PRODUCT; 4.1.0. + Any steps that are hypervisor-specific will be called out with a note. + Package Structure Changes + The package structure for &PRODUCT; has changed significantly since the 4.0.x releases. If you've compiled your own packages, you'll notice that the package names and the number of packages has changed. This is not a bug. + However, this does mean that the procedure is not as simple as an apt-get upgrade or yum update, so please follow this section carefully. + + We recommend reading through this section once or twice before beginning your upgrade procedure, and working through it on a test system before working on a production system. + + + Most users of &PRODUCT; manage the installation and upgrades of &PRODUCT; with one of Linux's predominant package systems, RPM or APT. This guide assumes you'll be using RPM and Yum (for Red Hat Enterprise Linux or CentOS), or APT and Debian packages (for Ubuntu). + Create RPM or Debian packages (as appropriate) and a repository from the 4.1.0 source, or check the Apache CloudStack downloads page at http://cloudstack.apache.org/downloads.html for package repositories supplied by community members. You will need them for step or step . + Instructions for creating packages from the &PRODUCT; source are in the Installation Guide. + + + Stop your management server or servers. Run this on all management server hosts: + # service cloud-management stop + + + Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. + # mysqldump -u root -p cloud > cloudstack-backup.sql + + + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . + Community Packages + This section assumes you're using the community supplied packages for &PRODUCT;. If you've created your own packages and APT repository, substitute your own URL for the ones used in these examples. + + + + The first order of business will be to change the sources list for each system with &PRODUCT; packages. This means all management servers, and any hosts that have the KVM agent. (No changes should be necessary for hosts that are running VMware or Xen.) + Start by opening /etc/apt/sources.list.d/cloudstack.list on any systems that have &PRODUCT; packages installed. + This file should have one line, which contains: + deb http://cloudstack.apt-get.eu/ubuntu precise 4.0 + We'll change it to point to the new package repository: + deb http://cloudstack.apt-get.eu/ubuntu precise 4.1 + If you're using your own package repository, change this line to read as appropriate for your 4.1.0 repository. + + + Now update your apt package list: + $ sudo apt-get update + + + ### upgrade steps for Debian on master servers goes here. + + + You will need to manually install the cloudstack-agent package: + $ sudo apt-get install cloudstack-agent + During the installation of cloudstack-agent, APT will copy your agent.properties, log4j-cloud.xml, and environment.properties from /etc/cloud/agent to /etc/cloudstack/agent. + When prompted whether you wish to keep your configuration, say Yes. + + + Verify that the file /etc/cloudstack/agent/environment.properties has a line that reads: + paths.script=/usr/share/cloudstack-common + If not, add the line. + + + Restart the agent: + + + + + ### + + +
+
+ Upgrade from 3.0.2 to 4.1.0 + This section will guide you from Citrix CloudStack 3.0.2 to Apache CloudStack 4.1.0. Sections that are hypervisor-specific will be called out with a note. + + + Ensure that you query your IP address usage records and process them or make a + backup. During the upgrade you will lose the old IP address usage records. + Starting in 3.0.2, the usage record format for IP addresses is the same as the rest + of the usage types. Instead of a single record with the assignment and release dates, + separate records are generated per aggregation period with start and end dates. After + upgrading, any existing IP address usage records in the old format will no longer be + available. + + + + The following upgrade instructions apply only if you're using VMware hosts. If + you're not using VMware hosts, skip this step and move on to . + + In each zone that includes VMware hosts, you need to add a new system VM template. + + + While running the existing 3.0.2 system, log in to the UI as root administrator. + + + In the left navigation bar, click Templates. + + + In Select view, click Templates. + + + Click Register template. + The Register template dialog box is displayed. + + + In the Register template dialog box, specify the following values (do not change these): + + + + + + + Field + Value + + + + + Name + systemvm-vmware-4.0 + + + Description + systemvm-vmware-4.0 + + + URL + http://download.cloud.com/templates/burbank/burbank-systemvm-08012012.ova + + + Zone + Choose the zone where this hypervisor is used + + + Hypervisor + VMware + + + Format + OVA + + + OS Type + Debian GNU/Linux 5.0 (32-bit) + + + Extractable + no + + + Password Enabled + no + + + Public + no + + + Featured + no + + + + + + + Watch the screen to be sure that the template downloads successfully and enters + the READY state. Do not proceed until this is successful. + + + + + Stop all Usage Servers if running. Run this on all Usage Server hosts. + # service cloud-usage stop + + + Stop the Management Servers. Run this on all Management Server hosts. + # service cloud-management stop + + + On the MySQL master, take a backup of the MySQL databases. We recommend performing + this step even in test upgrades. If there is an issue, this will assist with + debugging. + In the following commands, it is assumed that you have set the root password on the + database, which is a CloudStack recommended best practice. Substitute your own MySQL + root password. + # mysqldump -u root -pmysql_password cloud > cloud-backup.dmp + # mysqldump -u root -pmysql_password cloud_usage > cloud-usage-backup.dmp + + + Either build RPM/DEB packages as detailed in the Installation Guide, or use one of + the community provided yum/apt repositories to gain access to the &PRODUCT; + binaries. + + + After you have configured an appropriate yum or apt repository, you may execute the + one of the following commands as appropriate for your environment in order to upgrade + &PRODUCT;: # yum update cloud-* + # apt-get update + # apt-get upgrade cloud-* + + You will, of course, have to agree to the changes suggested by Yum or APT. + + If the upgrade output includes a message similar to the following, then some + custom content was found in your old components.xml, and you need to merge the two + files: + warning: /etc/cloud/management/components.xml created as /etc/cloud/management/components.xml.rpmnew + Instructions follow in the next step. + + + + If you have made changes to your copy of + /etc/cloud/management/components.xml the changes will be + preserved in the upgrade. However, you need to do the following steps to place these + changes in a new version of the file which is compatible with version + 4.1.0. + + + Make a backup copy of /etc/cloud/management/components.xml. + For example: + # mv /etc/cloud/management/components.xml /etc/cloud/management/components.xml-backup + + + Copy /etc/cloud/management/components.xml.rpmnew to create + a new /etc/cloud/management/components.xml: + # cp -ap /etc/cloud/management/components.xml.rpmnew /etc/cloud/management/components.xml + + + Merge your changes from the backup file into the new + components.xml. + # vi /etc/cloud/management/components.xml + + + + If you have more than one management server node, repeat the upgrade steps on each + node. + + + + Start the first Management Server. Do not start any other Management Server nodes + yet. + # service cloud-management start + Wait until the databases are upgraded. Ensure that the database upgrade is complete. + After confirmation, start the other Management Servers one at a time by running the same + command on each node. + + Failing to restart the Management Server indicates a problem in the upgrade. + Having the Management Server restarted without any issues indicates that the upgrade + is successfully completed. + + + + Start all Usage Servers (if they were running on your previous version). Perform + this on each Usage Server host. + # service cloud-usage start + + + + Additional steps are required for each KVM host. These steps will not affect + running guests in the cloud. These steps are required only for clouds using KVM as + hosts and only on the KVM hosts. + + + + Configure a yum or apt respository containing the &PRODUCT; packages as outlined + in the Installation Guide. + + + Stop the running agent. + # service cloud-agent stop + + + Update the agent software with one of the following command sets as appropriate + for your environment. + # yum update cloud-* + # apt-get update + # apt-get upgrade cloud-* + + + Start the agent. + # service cloud-agent start + + + Edit /etc/cloud/agent/agent.properties to change the + resource parameter from + "com.cloud.agent.resource.computing.LibvirtComputingResource" to + "com.cloud.hypervisor.kvm.resource.LibvirtComputingResource". + + + Start the cloud agent and cloud management services. + + + When the Management Server is up and running, log in to the CloudStack UI and + restart the virtual router for proper functioning of all the features. + + + + + Log in to the CloudStack UI as administrator, and check the status of the hosts. All + hosts should come to Up state (except those that you know to be offline). You may need + to wait 20 or 30 minutes, depending on the number of hosts. + + Troubleshooting: If login fails, clear your browser cache and reload the + page. + + + Do not proceed to the next step until the hosts show in Up state. + + + If you are upgrading from 3.0.2, perform the following: + + + Ensure that the admin port is set to 8096 by using the "integration.api.port" + global parameter. + This port is used by the cloud-sysvmadm script at the end of the upgrade + procedure. For information about how to set this parameter, see "Setting Global + Configuration Parameters" in the Installation Guide. + + + Restart the Management Server. + + If you don't want the admin port to remain open, you can set it to null after + the upgrade is done and restart the management server. + + + + + + Run the cloud-sysvmadm script to stop, then start, all Secondary + Storage VMs, Console Proxy VMs, and virtual routers. Run the script once on each + management server. Substitute your own IP address of the MySQL instance, the MySQL user + to connect as, and the password to use for that user. In addition to those parameters, + provide the -c and -r arguments. For + example: + # nohup cloud-sysvmadm -d 192.168.1.5 -u cloud -p password -c -r > + sysvm.log 2>&1 & + # tail -f sysvm.log + This might take up to an hour or more to run, depending on the number of accounts in + the system. + + + If needed, upgrade all Citrix XenServer hypervisor hosts in your cloud to a version + supported by CloudStack 4.1.0. The supported versions are XenServer 5.6 SP2 + and 6.0.2. Instructions for upgrade can be found in the CloudStack 4.1.0 + Installation Guide under "Upgrading XenServer Versions." + + + Now apply the XenServer hotfix XS602E003 (and any other needed hotfixes) to + XenServer v6.0.2 hypervisor hosts. + + + Disconnect the XenServer cluster from CloudStack. + In the left navigation bar of the CloudStack UI, select Infrastructure. Under + Clusters, click View All. Select the XenServer cluster and click Actions - + Unmanage. + This may fail if there are hosts not in one of the states Up, Down, + Disconnected, or Alert. You may need to fix that before unmanaging this + cluster. + Wait until the status of the cluster has reached Unmanaged. Use the CloudStack + UI to check on the status. When the cluster is in the unmanaged state, there is no + connection to the hosts in the cluster. + + + To clean up the VLAN, log in to one XenServer host and run: + /opt/xensource/bin/cloud-clean-vlan.sh + + + Now prepare the upgrade by running the following on one XenServer host: + /opt/xensource/bin/cloud-prepare-upgrade.sh + If you see a message like "can't eject CD", log in to the VM and unmount the CD, + then run this script again. + + + Upload the hotfix to the XenServer hosts. Always start with the Xen pool master, + then the slaves. Using your favorite file copy utility (e.g. WinSCP), copy the + hotfixes to the host. Place them in a temporary folder such as /tmp. + On the Xen pool master, upload the hotfix with this command: + xe patch-upload file-name=XS602E003.xsupdate + Make a note of the output from this command, which is a UUID for the hotfix + file. You'll need it in another step later. + + (Optional) If you are applying other hotfixes as well, you can repeat the + commands in this section with the appropriate hotfix number. For example, + XS602E004.xsupdate. + + + + Manually live migrate all VMs on this host to another host. First, get a list of + the VMs on this host: + # xe vm-list + Then use this command to migrate each VM. Replace the example host name and VM + name with your own: + # xe vm-migrate live=true host=host-name + vm=VM-name + + Troubleshooting + If you see a message like "You attempted an operation on a VM which requires + PV drivers to be installed but the drivers were not detected," run: + /opt/xensource/bin/make_migratable.sh + b6cf79c8-02ee-050b-922f-49583d9f1a14. + + + + Apply the hotfix. First, get the UUID of this host: + # xe host-list + Then use the following command to apply the hotfix. Replace the example host + UUID with the current host ID, and replace the hotfix UUID with the output from the + patch-upload command you ran on this machine earlier. You can also get the hotfix + UUID by running xe patch-list. + xe patch-apply host-uuid=host-uuid uuid=hotfix-uuid + + + Copy the following files from the CloudStack Management Server to the + host. + + + + + + + Copy from here... + ...to here + + + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/xenserver60/NFSSR.py + /opt/xensource/sm/NFSSR.py + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/setupxenserver.sh + /opt/xensource/bin/setupxenserver.sh + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/make_migratable.sh + /opt/xensource/bin/make_migratable.sh + + + + + + + (Only for hotfixes XS602E005 and XS602E007) You need to apply a new Cloud + Support Pack. + + + Download the CSP software onto the XenServer host from one of the following + links: + For hotfix XS602E005: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E005/56710/xe-phase-2/xenserver-cloud-supp.tgz + For hotfix XS602E007: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E007/57824/xe-phase-2/xenserver-cloud-supp.tgz + + + Extract the file: + # tar xf xenserver-cloud-supp.tgz + + + Run the following script: + # xe-install-supplemental-pack xenserver-cloud-supp.iso + + + If the XenServer host is part of a zone that uses basic networking, disable + Open vSwitch (OVS): + # xe-switch-network-backend bridge + + + + + Reboot this XenServer host. + + + Run the following: + /opt/xensource/bin/setupxenserver.sh + + If the message "mv: cannot stat `/etc/cron.daily/logrotate': No such file or + directory" appears, you can safely ignore it. + + + + Run the following: + for pbd in `xe pbd-list currently-attached=false| grep ^uuid | awk '{print $NF}'`; do xe pbd-plug uuid=$pbd ; + + + On each slave host in the Xen pool, repeat these steps, starting from "manually + live migrate VMs." + + + + + + Troubleshooting Tip + If passwords which you know to be valid appear not to work after upgrade, or other UI + issues are seen, try clearing your browser cache and reloading the UI page. + +
+
+ Upgrade from 2.2.14 to 4.1.0 + + + Ensure that you query your IPaddress usage records and process them; for example, + issue invoices for any usage that you have not yet billed users for. + Starting in 3.0.2, the usage record format for IP addresses is the same as the rest + of the usage types. Instead of a single record with the assignment and release dates, + separate records are generated per aggregation period with start and end dates. After + upgrading to 4.1.0, any existing IP address usage records in the old format + will no longer be available. + + + If you are using version 2.2.0 - 2.2.13, first upgrade to 2.2.14 by using the + instructions in the 2.2.14 Release Notes. + + KVM Hosts + If KVM hypervisor is used in your cloud, be sure you completed the step to insert + a valid username and password into the host_details table on each KVM node as + described in the 2.2.14 Release Notes. This step is critical, as the database will be + encrypted after the upgrade to 4.1.0. + + + + While running the 2.2.14 system, log in to the UI as root administrator. + + + Using the UI, add a new System VM template for each hypervisor type that is used in + your cloud. In each zone, add a system VM template for each hypervisor used in that + zone + + + In the left navigation bar, click Templates. + + + In Select view, click Templates. + + + Click Register template. + The Register template dialog box is displayed. + + + In the Register template dialog box, specify the following values depending on + the hypervisor type (do not change these): + + + + + + + Hypervisor + Description + + + + + XenServer + Name: systemvm-xenserver-4.1.0 + Description: systemvm-xenserver-4.1.0 + URL: + http://download.cloud.com/templates/acton/acton-systemvm-02062012.vhd.bz2 + Zone: Choose the zone where this hypervisor is used + Hypervisor: XenServer + Format: VHD + OS Type: Debian GNU/Linux 5.0 (32-bit) + Extractable: no + Password Enabled: no + Public: no + Featured: no + + + + KVM + Name: systemvm-kvm-4.1.0 + Description: systemvm-kvm-4.1.0 + URL: + http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 + Zone: Choose the zone where this hypervisor is used + Hypervisor: KVM + Format: QCOW2 + OS Type: Debian GNU/Linux 5.0 (32-bit) + Extractable: no + Password Enabled: no + Public: no + Featured: no + + + + VMware + Name: systemvm-vmware-4.1.0 + Description: systemvm-vmware-4.1.0 + URL: + http://download.cloud.com/templates/burbank/burbank-systemvm-08012012.ova + Zone: Choose the zone where this hypervisor is used + Hypervisor: VMware + Format: OVA + OS Type: Debian GNU/Linux 5.0 (32-bit) + Extractable: no + Password Enabled: no + Public: no + Featured: no + + + + + + + + + + Watch the screen to be sure that the template downloads successfully and enters the + READY state. Do not proceed until this is successful + + + WARNING: If you use more than one type of + hypervisor in your cloud, be sure you have repeated these steps to download the system + VM template for each hypervisor type. Otherwise, the upgrade will fail. + + + Stop all Usage Servers if running. Run this on all Usage Server hosts. + # service cloud-usage stop + + + Stop the Management Servers. Run this on all Management Server hosts. + # service cloud-management stop + + + On the MySQL master, take a backup of the MySQL databases. We recommend performing + this step even in test upgrades. If there is an issue, this will assist with + debugging. + In the following commands, it is assumed that you have set the root password on the + database, which is a CloudStack recommended best practice. Substitute your own MySQL + root password. + # mysqldump -u root -pmysql_password cloud > cloud-backup.dmp + # mysqldump -u root -pmysql_password cloud_usage > cloud-usage-backup.dmp + + + + Either build RPM/DEB packages as detailed in the Installation Guide, or use one of + the community provided yum/apt repositories to gain access to the &PRODUCT; binaries. + + + + After you have configured an appropriate yum or apt repository, you may execute the + one of the following commands as appropriate for your environment in order to upgrade + &PRODUCT;: # yum update cloud-* + # apt-get update + # apt-get upgrade cloud-* + + You will, of course, have to agree to the changes suggested by Yum or APT. + + + If you have made changes to your existing copy of the file components.xml in your + previous-version CloudStack installation, the changes will be preserved in the upgrade. + However, you need to do the following steps to place these changes in a new version of + the file which is compatible with version 4.0.0-incubating. + + How will you know whether you need to do this? If the upgrade output in the + previous step included a message like the following, then some custom content was + found in your old components.xml, and you need to merge the two files: + + warning: /etc/cloud/management/components.xml created as /etc/cloud/management/components.xml.rpmnew + + + Make a backup copy of your + /etc/cloud/management/components.xml file. For + example: + # mv /etc/cloud/management/components.xml /etc/cloud/management/components.xml-backup + + + Copy /etc/cloud/management/components.xml.rpmnew to create + a new /etc/cloud/management/components.xml: + # cp -ap /etc/cloud/management/components.xml.rpmnew /etc/cloud/management/components.xml + + + Merge your changes from the backup file into the new components.xml file. + # vi /etc/cloud/management/components.xml + + + + + + If you have made changes to your existing copy of the + /etc/cloud/management/db.properties file in your previous-version + CloudStack installation, the changes will be preserved in the upgrade. However, you need + to do the following steps to place these changes in a new version of the file which is + compatible with version 4.0.0-incubating. + + + Make a backup copy of your file + /etc/cloud/management/db.properties. For example: + # mv /etc/cloud/management/db.properties /etc/cloud/management/db.properties-backup + + + Copy /etc/cloud/management/db.properties.rpmnew to create a + new /etc/cloud/management/db.properties: + # cp -ap /etc/cloud/management/db.properties.rpmnew etc/cloud/management/db.properties + + + Merge your changes from the backup file into the new db.properties file. + # vi /etc/cloud/management/db.properties + + + + + On the management server node, run the following command. It is recommended that you + use the command-line flags to provide your own encryption keys. See Password and Key + Encryption in the Installation Guide. + # cloud-setup-encryption -e encryption_type -m management_server_key -k database_key + When used without arguments, as in the following example, the default encryption + type and keys will be used: + + + (Optional) For encryption_type, use file or web to indicate the technique used + to pass in the database encryption password. Default: file. + + + (Optional) For management_server_key, substitute the default key that is used to + encrypt confidential parameters in the properties file. Default: password. It is + highly recommended that you replace this with a more secure value + + + (Optional) For database_key, substitute the default key that is used to encrypt + confidential parameters in the CloudStack database. Default: password. It is highly + recommended that you replace this with a more secure value. + + + + + Repeat steps 10 - 14 on every management server node. If you provided your own + encryption key in step 14, use the same key on all other management servers. + + + Start the first Management Server. Do not start any other Management Server nodes + yet. + # service cloud-management start + Wait until the databases are upgraded. Ensure that the database upgrade is complete. + You should see a message like "Complete! Done." After confirmation, start the other + Management Servers one at a time by running the same command on each node. + + + Start all Usage Servers (if they were running on your previous version). Perform + this on each Usage Server host. + # service cloud-usage start + + + (KVM only) Additional steps are required for each KVM host. These steps will not + affect running guests in the cloud. These steps are required only for clouds using KVM + as hosts and only on the KVM hosts. + + + Configure your CloudStack package repositories as outlined in the Installation + Guide + + + Stop the running agent. + # service cloud-agent stop + + + Update the agent software with one of the following command sets as + appropriate. + # yum update cloud-* + + # apt-get update + # apt-get upgrade cloud-* + + + + Start the agent. + # service cloud-agent start + + + Copy the contents of the agent.properties file to the new + agent.properties file by using the following command + sed -i 's/com.cloud.agent.resource.computing.LibvirtComputingResource/com.cloud.hypervisor.kvm.resource.LibvirtComputingResource/g' /etc/cloud/agent/agent.properties + + + Start the cloud agent and cloud management services. + + + When the Management Server is up and running, log in to the CloudStack UI and + restart the virtual router for proper functioning of all the features. + + + + + Log in to the CloudStack UI as admin, and check the status of the hosts. All hosts + should come to Up state (except those that you know to be offline). You may need to wait + 20 or 30 minutes, depending on the number of hosts. + Do not proceed to the next step until the hosts show in the Up state. If the hosts + do not come to the Up state, contact support. + + + Run the following script to stop, then start, all Secondary Storage VMs, Console + Proxy VMs, and virtual routers. + + + Run the command once on one management server. Substitute your own IP address of + the MySQL instance, the MySQL user to connect as, and the password to use for that + user. In addition to those parameters, provide the "-c" and "-r" arguments. For + example: + # nohup cloud-sysvmadm -d 192.168.1.5 -u cloud -p password -c -r > sysvm.log 2>&1 & + # tail -f sysvm.log + This might take up to an hour or more to run, depending on the number of + accounts in the system. + + + After the script terminates, check the log to verify correct execution: + # tail -f sysvm.log + The content should be like the following: + + Stopping and starting 1 secondary storage vm(s)... + Done stopping and starting secondary storage vm(s) + Stopping and starting 1 console proxy vm(s)... + Done stopping and starting console proxy vm(s). + Stopping and starting 4 running routing vm(s)... + Done restarting router(s). + + + + + + If you would like additional confirmation that the new system VM templates were + correctly applied when these system VMs were rebooted, SSH into the System VM and check + the version. + Use one of the following techniques, depending on the hypervisor. + + XenServer or KVM: + SSH in by using the link local IP address of the system VM. For example, in the + command below, substitute your own path to the private key used to log in to the + system VM and your own link local IP. + + Run the following commands on the XenServer or KVM host on which the system VM is + present: + # ssh -i private-key-path link-local-ip -p 3922 + # cat /etc/cloudstack-release + The output should be like the following: + Cloudstack Release 4.0.0-incubating Mon Oct 9 15:10:04 PST 2012 + + ESXi + SSH in using the private IP address of the system VM. For example, in the command + below, substitute your own path to the private key used to log in to the system VM and + your own private IP. + + Run the following commands on the Management Server: + # ssh -i private-key-path private-ip -p 3922 + # cat /etc/cloudstack-release + + The output should be like the following: + Cloudstack Release 4.0.0-incubating Mon Oct 9 15:10:04 PST 2012 + + + If needed, upgrade all Citrix XenServer hypervisor hosts in your cloud to a version + supported by CloudStack 4.0.0-incubating. The supported versions are XenServer 5.6 SP2 + and 6.0.2. Instructions for upgrade can be found in the CloudStack 4.0.0-incubating + Installation Guide. + + + Apply the XenServer hotfix XS602E003 (and any other needed hotfixes) to XenServer + v6.0.2 hypervisor hosts. + + + Disconnect the XenServer cluster from CloudStack. + In the left navigation bar of the CloudStack UI, select Infrastructure. Under + Clusters, click View All. Select the XenServer cluster and click Actions - + Unmanage. + This may fail if there are hosts not in one of the states Up, Down, + Disconnected, or Alert. You may need to fix that before unmanaging this + cluster. + Wait until the status of the cluster has reached Unmanaged. Use the CloudStack + UI to check on the status. When the cluster is in the unmanaged state, there is no + connection to the hosts in the cluster. + + + To clean up the VLAN, log in to one XenServer host and run: + /opt/xensource/bin/cloud-clean-vlan.sh + + + Prepare the upgrade by running the following on one XenServer host: + /opt/xensource/bin/cloud-prepare-upgrade.sh + If you see a message like "can't eject CD", log in to the VM and umount the CD, + then run this script again. + + + Upload the hotfix to the XenServer hosts. Always start with the Xen pool master, + then the slaves. Using your favorite file copy utility (e.g. WinSCP), copy the + hotfixes to the host. Place them in a temporary folder such as /root or /tmp. + On the Xen pool master, upload the hotfix with this command: + xe patch-upload file-name=XS602E003.xsupdate + Make a note of the output from this command, which is a UUID for the hotfix + file. You'll need it in another step later. + + (Optional) If you are applying other hotfixes as well, you can repeat the + commands in this section with the appropriate hotfix number. For example, + XS602E004.xsupdate. + + + + Manually live migrate all VMs on this host to another host. First, get a list of + the VMs on this host: + # xe vm-list + Then use this command to migrate each VM. Replace the example host name and VM + name with your own: + # xe vm-migrate live=true host=host-name vm=VM-name + + Troubleshooting + If you see a message like "You attempted an operation on a VM which requires + PV drivers to be installed but the drivers were not detected," run: + /opt/xensource/bin/make_migratable.sh + b6cf79c8-02ee-050b-922f-49583d9f1a14. + + + + Apply the hotfix. First, get the UUID of this host: + # xe host-list + Then use the following command to apply the hotfix. Replace the example host + UUID with the current host ID, and replace the hotfix UUID with the output from the + patch-upload command you ran on this machine earlier. You can also get the hotfix + UUID by running xe patch-list. + xe patch-apply host-uuid=host-uuid + uuid=hotfix-uuid + + + Copy the following files from the CloudStack Management Server to the + host. + + + + + + + Copy from here... + ...to here + + + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/xenserver60/NFSSR.py + /opt/xensource/sm/NFSSR.py + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/setupxenserver.sh + /opt/xensource/bin/setupxenserver.sh + + + /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/make_migratable.sh + /opt/xensource/bin/make_migratable.sh + + + + + + + (Only for hotfixes XS602E005 and XS602E007) You need to apply a new Cloud + Support Pack. + + + Download the CSP software onto the XenServer host from one of the following + links: + For hotfix XS602E005: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E005/56710/xe-phase-2/xenserver-cloud-supp.tgz + For hotfix XS602E007: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E007/57824/xe-phase-2/xenserver-cloud-supp.tgz + + + Extract the file: + # tar xf xenserver-cloud-supp.tgz + + + Run the following script: + # xe-install-supplemental-pack + xenserver-cloud-supp.iso + + + If the XenServer host is part of a zone that uses basic networking, disable + Open vSwitch (OVS): + # xe-switch-network-backend bridge + + + + + Reboot this XenServer host. + + + Run the following: + /opt/xensource/bin/setupxenserver.sh + + If the message "mv: cannot stat `/etc/cron.daily/logrotate': No such file or + directory" appears, you can safely ignore it. + + + + Run the following: + for pbd in `xe pbd-list currently-attached=false| grep ^uuid | awk + '{print $NF}'`; do xe pbd-plug uuid=$pbd ; + + + + On each slave host in the Xen pool, repeat these steps, starting from "manually + live migrate VMs." + + + + +
+
+ + Version 4.1.0 +
+ What’s New in 4.1 + Apache CloudStack 4.1.0 includes many new features. This section covers the most prominent new features and changes. +
+
+ Issues Fixed in 4.1.0 + Apache CloudStack uses Jira + to track its issues. All new features and bugs for 4.1.0 have been tracked in Jira, and have + a standard naming convention of "CLOUDSTACK-NNNN" where "NNNN" is the issue number. + This section includes a summary of known issues against 4.0.0 that were fixed in 4.1.0. + - - - - - Field - Value - - - - - Name - systemvm-vmware-4.0 - - - Description - systemvm-vmware-4.0 - - - URL - http://download.cloud.com/templates/burbank/burbank-systemvm-08012012.ova - - - Zone - Choose the zone where this hypervisor is used - - - Hypervisor - VMware - - - Format - OVA - - - OS Type - Debian GNU/Linux 5.0 (32-bit) - - - Extractable - no - - - Password Enabled - no - - - Public - no - - - Featured - no - - + + + + + + Defect + + + Description + + + + + + CS-16135 + Creating volumes after upgrading from snapshot taken in 2.2.14 no longer + deletes the snapshot physically from the secondary storage. + + - - - - Watch the screen to be sure that the template downloads successfully and enters - the READY state. Do not proceed until this is successful. - - - - - Stop all Usage Servers if running. Run this on all Usage Server hosts. - # service cloud-usage stop - - - Stop the Management Servers. Run this on all Management Server hosts. - # service cloud-management stop - - - On the MySQL master, take a backup of the MySQL databases. We recommend performing - this step even in test upgrades. If there is an issue, this will assist with - debugging. - In the following commands, it is assumed that you have set the root password on the - database, which is a CloudStack recommended best practice. Substitute your own MySQL - root password. - # mysqldump -u root -pmysql_password cloud > cloud-backup.dmp -# mysqldump -u root -pmysql_password cloud_usage > cloud-usage-backup.dmp - - - Either build RPM/DEB packages as detailed in the Installation Guide, or use one of - the community provided yum/apt repositories to gain access to the &PRODUCT; - binaries. - - - After you have configured an appropriate yum or apt repository, you may execute the - one of the following commands as appropriate for your environment in order to upgrade - &PRODUCT;: # yum update cloud-* - # apt-get update -# apt-get upgrade cloud-* - - You will, of course, have to agree to the changes suggested by Yum or APT. - - If the upgrade output includes a message similar to the following, then some - custom content was found in your old components.xml, and you need to merge the two - files: - warning: /etc/cloud/management/components.xml created as /etc/cloud/management/components.xml.rpmnew - Instructions follow in the next step. - - - - If you have made changes to your copy of - /etc/cloud/management/components.xml the changes will be - preserved in the upgrade. However, you need to do the following steps to place these - changes in a new version of the file which is compatible with version - 4.0.0-incubating. - - - Make a backup copy of /etc/cloud/management/components.xml. - For example: - # mv /etc/cloud/management/components.xml /etc/cloud/management/components.xml-backup - - - Copy /etc/cloud/management/components.xml.rpmnew to create - a new /etc/cloud/management/components.xml: - # cp -ap /etc/cloud/management/components.xml.rpmnew /etc/cloud/management/components.xml - - - Merge your changes from the backup file into the new - components.xml. - # vi /etc/cloud/management/components.xml - - - - If you have more than one management server node, repeat the upgrade steps on each - node. - - - - Start the first Management Server. Do not start any other Management Server nodes - yet. - # service cloud-management start - Wait until the databases are upgraded. Ensure that the database upgrade is complete. - After confirmation, start the other Management Servers one at a time by running the same - command on each node. - - Failing to restart the Management Server indicates a problem in the upgrade. - Having the Management Server restarted without any issues indicates that the upgrade - is successfully completed. - - - - Start all Usage Servers (if they were running on your previous version). Perform - this on each Usage Server host. - # service cloud-usage start - - - - Additional steps are required for each KVM host. These steps will not affect - running guests in the cloud. These steps are required only for clouds using KVM as - hosts and only on the KVM hosts. - - - - Configure a yum or apt respository containing the &PRODUCT; packages as outlined - in the Installation Guide. - - - Stop the running agent. - # service cloud-agent stop - - - Update the agent software with one of the following command sets as appropriate - for your environment. - # yum update cloud-* - # apt-get update - # apt-get upgrade cloud-* - - - Start the agent. - # service cloud-agent start - - - Edit /etc/cloud/agent/agent.properties to change the - resource parameter from - "com.cloud.agent.resource.computing.LibvirtComputingResource" to - "com.cloud.hypervisor.kvm.resource.LibvirtComputingResource". - - - Start the cloud agent and cloud management services. - - - When the Management Server is up and running, log in to the CloudStack UI and - restart the virtual router for proper functioning of all the features. - - - - - Log in to the CloudStack UI as administrator, and check the status of the hosts. All - hosts should come to Up state (except those that you know to be offline). You may need - to wait 20 or 30 minutes, depending on the number of hosts. - - Troubleshooting: If login fails, clear your browser cache and reload the - page. - - - Do not proceed to the next step until the hosts show in Up state. - - - If you are upgrading from 3.0.2, perform the following: - - - Ensure that the admin port is set to 8096 by using the "integration.api.port" - global parameter. - This port is used by the cloud-sysvmadm script at the end of the upgrade - procedure. For information about how to set this parameter, see "Setting Global - Configuration Parameters" in the Installation Guide. - - - Restart the Management Server. - - If you don't want the admin port to remain open, you can set it to null after - the upgrade is done and restart the management server. - - - - - - Run the cloud-sysvmadm script to stop, then start, all Secondary - Storage VMs, Console Proxy VMs, and virtual routers. Run the script once on each - management server. Substitute your own IP address of the MySQL instance, the MySQL user - to connect as, and the password to use for that user. In addition to those parameters, - provide the -c and -r arguments. For - example: - # nohup cloud-sysvmadm -d 192.168.1.5 -u cloud -p password -c -r > - sysvm.log 2>&1 & - # tail -f sysvm.log - This might take up to an hour or more to run, depending on the number of accounts in - the system. - - - If needed, upgrade all Citrix XenServer hypervisor hosts in your cloud to a version - supported by CloudStack 4.0.0-incubating. The supported versions are XenServer 5.6 SP2 - and 6.0.2. Instructions for upgrade can be found in the CloudStack 4.0.0-incubating - Installation Guide. - - - Now apply the XenServer hotfix XS602E003 (and any other needed hotfixes) to - XenServer v6.0.2 hypervisor hosts. - - - Disconnect the XenServer cluster from CloudStack. - In the left navigation bar of the CloudStack UI, select Infrastructure. Under - Clusters, click View All. Select the XenServer cluster and click Actions - - Unmanage. - This may fail if there are hosts not in one of the states Up, Down, - Disconnected, or Alert. You may need to fix that before unmanaging this - cluster. - Wait until the status of the cluster has reached Unmanaged. Use the CloudStack - UI to check on the status. When the cluster is in the unmanaged state, there is no - connection to the hosts in the cluster. - - - To clean up the VLAN, log in to one XenServer host and run: - /opt/xensource/bin/cloud-clean-vlan.sh - - - Now prepare the upgrade by running the following on one XenServer host: - /opt/xensource/bin/cloud-prepare-upgrade.sh - If you see a message like "can't eject CD", log in to the VM and unmount the CD, - then run this script again. - - - Upload the hotfix to the XenServer hosts. Always start with the Xen pool master, - then the slaves. Using your favorite file copy utility (e.g. WinSCP), copy the - hotfixes to the host. Place them in a temporary folder such as /tmp. - On the Xen pool master, upload the hotfix with this command: - xe patch-upload file-name=XS602E003.xsupdate - Make a note of the output from this command, which is a UUID for the hotfix - file. You'll need it in another step later. - - (Optional) If you are applying other hotfixes as well, you can repeat the - commands in this section with the appropriate hotfix number. For example, - XS602E004.xsupdate. - - - - Manually live migrate all VMs on this host to another host. First, get a list of - the VMs on this host: - # xe vm-list - Then use this command to migrate each VM. Replace the example host name and VM - name with your own: - # xe vm-migrate live=true host=host-name - vm=VM-name - - Troubleshooting - If you see a message like "You attempted an operation on a VM which requires - PV drivers to be installed but the drivers were not detected," run: - /opt/xensource/bin/make_migratable.sh - b6cf79c8-02ee-050b-922f-49583d9f1a14. - - - - Apply the hotfix. First, get the UUID of this host: - # xe host-list - Then use the following command to apply the hotfix. Replace the example host - UUID with the current host ID, and replace the hotfix UUID with the output from the - patch-upload command you ran on this machine earlier. You can also get the hotfix - UUID by running xe patch-list. - xe patch-apply host-uuid=host-uuid uuid=hotfix-uuid - - - Copy the following files from the CloudStack Management Server to the - host. - + +
+
+ Known Issues in 4.1.0 + - - - - - Copy from here... - ...to here - - - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/xenserver60/NFSSR.py - /opt/xensource/sm/NFSSR.py - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/setupxenserver.sh - /opt/xensource/bin/setupxenserver.sh - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/make_migratable.sh - /opt/xensource/bin/make_migratable.sh - - + + + + + + Issue ID + + + Description + + + + + + CLOUDSTACK-1747 + mvn deploydb only creates 4.0 DB, not 4.1 + Due to tooling changes between 4.1 and 4.2, CloudStack's database is created using the 4.0 schema and updated to the 4.1 schema when the management server starts for the first time. It's OK to see the same schema if the management server has not started yet. + + + + CLOUDSTACK-1824 + Service CloudStack-Management is being displayed as cloud-management service + Many scripts and text entries have references to cloud-management rather than cloudstack-management due to the changeover between 4.0 and 4.1 to rename services. This is a minor issue and should be corrected by 4.2. + + + + + CLOUDSTACK-1824 + Service CloudStack-Management is being displayed as cloud-management service + + + + + CLOUDSTACK-1510 + + + NPE when primary storage is added with wrong path + + + + + CLOUDSTACK-1428 + + + [UI] Instance which are created without display name are not visible when added to LB + + + + + CLOUDSTACK-1306 + + + Better Error message when trying to deploy Vm by passing static Ipv4 addresses that are assigned to another VM/IP4 address is outside the iprange. + + + + + CLOUDSTACK-1236 + + + Warning while adding Xen 6.1 host [Unable to create local link network] + + + + + CLOUDSTACK-969 + + + api: zone response lists vlan in it as "vlan range of zone" but the vlan belongs to physical network + + + + + CLOUDSTACK-963 + + + [cloud.utils.AnnotationHelper] class java.lang.Stringdoes not have a Table annotation + + + + + CLOUDSTACK-458 + + + xen:snapshots:Storage gc fail to clean the failed snapshot images from secondarystorage + + + + + CLOUDSTACK-315 + + + Infrastructure view does not show capacity values + + + + + CLOUDSTACK-300 + + + Creation of compute offering allow combination of local storage + HA + + + + + CLOUDSTACK-282 + + + Virtual Routers do not properly resolve DNS SRV Records + + + + + CLOUDSTACK-276 + + + SSVM ID is exposed in the Error Message thrown by AddTrafficType API + + + + + CLOUDSTACK-270 + + + Ui should not ask for a vlan range if the physical network isolation type is not VLAN + + + + + CLOUDSTACK-245 + + + VPC ACLs are not stored and programmed consistently + + + + + CLOUDSTACK-231 + + + Tag creation using special charecters + + + + + CLOUDSTACK-124 + + + NetworkGarbageCollector not cleaning up networks + + + + + CLOUDSTACK-62 + + + console proxy does not support any keymaps besides us, jp + + + - - - - (Only for hotfixes XS602E005 and XS602E007) You need to apply a new Cloud - Support Pack. - - - Download the CSP software onto the XenServer host from one of the following - links: - For hotfix XS602E005: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E005/56710/xe-phase-2/xenserver-cloud-supp.tgz - For hotfix XS602E007: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E007/57824/xe-phase-2/xenserver-cloud-supp.tgz - - - Extract the file: - # tar xf xenserver-cloud-supp.tgz - - - Run the following script: - # xe-install-supplemental-pack xenserver-cloud-supp.iso - - - If the XenServer host is part of a zone that uses basic networking, disable - Open vSwitch (OVS): - # xe-switch-network-backend bridge - - - - - Reboot this XenServer host. - - - Run the following: - /opt/xensource/bin/setupxenserver.sh - - If the message "mv: cannot stat `/etc/cron.daily/logrotate': No such file or - directory" appears, you can safely ignore it. - - - - Run the following: - for pbd in `xe pbd-list currently-attached=false| grep ^uuid | awk '{print $NF}'`; do xe pbd-plug uuid=$pbd ; - - - On each slave host in the Xen pool, repeat these steps, starting from "manually - live migrate VMs." - - - - - - Troubleshooting Tip - If passwords which you know to be valid appear not to work after upgrade, or other UI - issues are seen, try clearing your browser cache and reloading the UI page. - -
-
- Upgrade from 2.2.14 to 4.0.0-incubating - - - Ensure that you query your IPaddress usage records and process them; for example, - issue invoices for any usage that you have not yet billed users for. - Starting in 3.0.2, the usage record format for IP addresses is the same as the rest - of the usage types. Instead of a single record with the assignment and release dates, - separate records are generated per aggregation period with start and end dates. After - upgrading to 4.0.0-incubating, any existing IP address usage records in the old format - will no longer be available. - - - If you are using version 2.2.0 - 2.2.13, first upgrade to 2.2.14 by using the - instructions in the 2.2.14 Release Notes. - - KVM Hosts - If KVM hypervisor is used in your cloud, be sure you completed the step to insert - a valid username and password into the host_details table on each KVM node as - described in the 2.2.14 Release Notes. This step is critical, as the database will be - encrypted after the upgrade to 4.0.0-incubating. - - - - While running the 2.2.14 system, log in to the UI as root administrator. - - - Using the UI, add a new System VM template for each hypervisor type that is used in - your cloud. In each zone, add a system VM template for each hypervisor used in that - zone - - - In the left navigation bar, click Templates. - - - In Select view, click Templates. - - - Click Register template. - The Register template dialog box is displayed. - - - In the Register template dialog box, specify the following values depending on - the hypervisor type (do not change these): - - - - - - - Hypervisor - Description - - - - - XenServer - Name: systemvm-xenserver-3.0.0 - Description: systemvm-xenserver-3.0.0 - URL: - http://download.cloud.com/templates/acton/acton-systemvm-02062012.vhd.bz2 - Zone: Choose the zone where this hypervisor is used - Hypervisor: XenServer - Format: VHD - OS Type: Debian GNU/Linux 5.0 (32-bit) - Extractable: no - Password Enabled: no - Public: no - Featured: no - - - - KVM - Name: systemvm-kvm-4.1.0 - Description: systemvm-kvm-4.1.0 - URL: - http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 - Zone: Choose the zone where this hypervisor is used - Hypervisor: KVM - Format: QCOW2 - OS Type: Debian GNU/Linux 5.0 (32-bit) - Extractable: no - Password Enabled: no - Public: no - Featured: no - - - - VMware - Name: systemvm-vmware-4.1.0 - Description: systemvm-vmware-4.1.0 - URL: - http://download.cloud.com/templates/burbank/burbank-systemvm-08012012.ova - Zone: Choose the zone where this hypervisor is used - Hypervisor: VMware - Format: OVA - OS Type: Debian GNU/Linux 5.0 (32-bit) - Extractable: no - Password Enabled: no - Public: no - Featured: no - - - - - - - - - - Watch the screen to be sure that the template downloads successfully and enters the - READY state. Do not proceed until this is successful - - - WARNING: If you use more than one type of - hypervisor in your cloud, be sure you have repeated these steps to download the system - VM template for each hypervisor type. Otherwise, the upgrade will fail. - - - Stop all Usage Servers if running. Run this on all Usage Server hosts. - # service cloud-usage stop - - - Stop the Management Servers. Run this on all Management Server hosts. - # service cloud-management stop - - - On the MySQL master, take a backup of the MySQL databases. We recommend performing - this step even in test upgrades. If there is an issue, this will assist with - debugging. - In the following commands, it is assumed that you have set the root password on the - database, which is a CloudStack recommended best practice. Substitute your own MySQL - root password. - # mysqldump -u root -pmysql_password cloud > cloud-backup.dmp -# mysqldump -u root -pmysql_password cloud_usage > cloud-usage-backup.dmp - - - - Either build RPM/DEB packages as detailed in the Installation Guide, or use one of - the community provided yum/apt repositories to gain access to the &PRODUCT; binaries. - - - - After you have configured an appropriate yum or apt repository, you may execute the - one of the following commands as appropriate for your environment in order to upgrade - &PRODUCT;: # yum update cloud-* - # apt-get update -# apt-get upgrade cloud-* - - You will, of course, have to agree to the changes suggested by Yum or APT. - - - If you have made changes to your existing copy of the file components.xml in your - previous-version CloudStack installation, the changes will be preserved in the upgrade. - However, you need to do the following steps to place these changes in a new version of - the file which is compatible with version 4.0.0-incubating. - - How will you know whether you need to do this? If the upgrade output in the - previous step included a message like the following, then some custom content was - found in your old components.xml, and you need to merge the two files: - - warning: /etc/cloud/management/components.xml created as /etc/cloud/management/components.xml.rpmnew - - - Make a backup copy of your - /etc/cloud/management/components.xml file. For - example: - # mv /etc/cloud/management/components.xml /etc/cloud/management/components.xml-backup - - - Copy /etc/cloud/management/components.xml.rpmnew to create - a new /etc/cloud/management/components.xml: - # cp -ap /etc/cloud/management/components.xml.rpmnew /etc/cloud/management/components.xml - - - Merge your changes from the backup file into the new components.xml file. - # vi /etc/cloud/management/components.xml - - - - - - If you have made changes to your existing copy of the - /etc/cloud/management/db.properties file in your previous-version - CloudStack installation, the changes will be preserved in the upgrade. However, you need - to do the following steps to place these changes in a new version of the file which is - compatible with version 4.0.0-incubating. - - - Make a backup copy of your file - /etc/cloud/management/db.properties. For example: - # mv /etc/cloud/management/db.properties /etc/cloud/management/db.properties-backup - - - Copy /etc/cloud/management/db.properties.rpmnew to create a - new /etc/cloud/management/db.properties: - # cp -ap /etc/cloud/management/db.properties.rpmnew etc/cloud/management/db.properties - - - Merge your changes from the backup file into the new db.properties file. - # vi /etc/cloud/management/db.properties - - - - - On the management server node, run the following command. It is recommended that you - use the command-line flags to provide your own encryption keys. See Password and Key - Encryption in the Installation Guide. - # cloud-setup-encryption -e encryption_type -m management_server_key -k database_key - When used without arguments, as in the following example, the default encryption - type and keys will be used: - - - (Optional) For encryption_type, use file or web to indicate the technique used - to pass in the database encryption password. Default: file. - - - (Optional) For management_server_key, substitute the default key that is used to - encrypt confidential parameters in the properties file. Default: password. It is - highly recommended that you replace this with a more secure value - - - (Optional) For database_key, substitute the default key that is used to encrypt - confidential parameters in the CloudStack database. Default: password. It is highly - recommended that you replace this with a more secure value. - - - - - Repeat steps 10 - 14 on every management server node. If you provided your own - encryption key in step 14, use the same key on all other management servers. - - - Start the first Management Server. Do not start any other Management Server nodes - yet. - # service cloud-management start - Wait until the databases are upgraded. Ensure that the database upgrade is complete. - You should see a message like "Complete! Done." After confirmation, start the other - Management Servers one at a time by running the same command on each node. - - - Start all Usage Servers (if they were running on your previous version). Perform - this on each Usage Server host. - # service cloud-usage start - - - (KVM only) Additional steps are required for each KVM host. These steps will not - affect running guests in the cloud. These steps are required only for clouds using KVM - as hosts and only on the KVM hosts. - - - Configure your CloudStack package repositories as outlined in the Installation - Guide - - - Stop the running agent. - # service cloud-agent stop - - - Update the agent software with one of the following command sets as - appropriate. - # yum update cloud-* - - # apt-get update -# apt-get upgrade cloud-* - - - - Start the agent. - # service cloud-agent start - - - Copy the contents of the agent.properties file to the new - agent.properties file by using the following command - sed -i 's/com.cloud.agent.resource.computing.LibvirtComputingResource/com.cloud.hypervisor.kvm.resource.LibvirtComputingResource/g' /etc/cloud/agent/agent.properties - - - Start the cloud agent and cloud management services. - - - When the Management Server is up and running, log in to the CloudStack UI and - restart the virtual router for proper functioning of all the features. - - - - - Log in to the CloudStack UI as admin, and check the status of the hosts. All hosts - should come to Up state (except those that you know to be offline). You may need to wait - 20 or 30 minutes, depending on the number of hosts. - Do not proceed to the next step until the hosts show in the Up state. If the hosts - do not come to the Up state, contact support. - - - Run the following script to stop, then start, all Secondary Storage VMs, Console - Proxy VMs, and virtual routers. - - - Run the command once on one management server. Substitute your own IP address of - the MySQL instance, the MySQL user to connect as, and the password to use for that - user. In addition to those parameters, provide the "-c" and "-r" arguments. For - example: - # nohup cloud-sysvmadm -d 192.168.1.5 -u cloud -p password -c -r > sysvm.log 2>&1 & -# tail -f sysvm.log - This might take up to an hour or more to run, depending on the number of - accounts in the system. - - - After the script terminates, check the log to verify correct execution: - # tail -f sysvm.log - The content should be like the following: - -Stopping and starting 1 secondary storage vm(s)... -Done stopping and starting secondary storage vm(s) -Stopping and starting 1 console proxy vm(s)... -Done stopping and starting console proxy vm(s). -Stopping and starting 4 running routing vm(s)... -Done restarting router(s). - - - - - - If you would like additional confirmation that the new system VM templates were - correctly applied when these system VMs were rebooted, SSH into the System VM and check - the version. - Use one of the following techniques, depending on the hypervisor. - - XenServer or KVM: - SSH in by using the link local IP address of the system VM. For example, in the - command below, substitute your own path to the private key used to log in to the - system VM and your own link local IP. - - Run the following commands on the XenServer or KVM host on which the system VM is - present: - # ssh -i private-key-path link-local-ip -p 3922 -# cat /etc/cloudstack-release - The output should be like the following: - Cloudstack Release 4.0.0-incubating Mon Oct 9 15:10:04 PST 2012 - - ESXi - SSH in using the private IP address of the system VM. For example, in the command - below, substitute your own path to the private key used to log in to the system VM and - your own private IP. - - Run the following commands on the Management Server: - # ssh -i private-key-path private-ip -p 3922 -# cat /etc/cloudstack-release - - The output should be like the following: - Cloudstack Release 4.0.0-incubating Mon Oct 9 15:10:04 PST 2012 - - - If needed, upgrade all Citrix XenServer hypervisor hosts in your cloud to a version - supported by CloudStack 4.0.0-incubating. The supported versions are XenServer 5.6 SP2 - and 6.0.2. Instructions for upgrade can be found in the CloudStack 4.0.0-incubating - Installation Guide. - - - Apply the XenServer hotfix XS602E003 (and any other needed hotfixes) to XenServer - v6.0.2 hypervisor hosts. - - - Disconnect the XenServer cluster from CloudStack. - In the left navigation bar of the CloudStack UI, select Infrastructure. Under - Clusters, click View All. Select the XenServer cluster and click Actions - - Unmanage. - This may fail if there are hosts not in one of the states Up, Down, - Disconnected, or Alert. You may need to fix that before unmanaging this - cluster. - Wait until the status of the cluster has reached Unmanaged. Use the CloudStack - UI to check on the status. When the cluster is in the unmanaged state, there is no - connection to the hosts in the cluster. - - - To clean up the VLAN, log in to one XenServer host and run: - /opt/xensource/bin/cloud-clean-vlan.sh - - - Prepare the upgrade by running the following on one XenServer host: - /opt/xensource/bin/cloud-prepare-upgrade.sh - If you see a message like "can't eject CD", log in to the VM and umount the CD, - then run this script again. - - - Upload the hotfix to the XenServer hosts. Always start with the Xen pool master, - then the slaves. Using your favorite file copy utility (e.g. WinSCP), copy the - hotfixes to the host. Place them in a temporary folder such as /root or /tmp. - On the Xen pool master, upload the hotfix with this command: - xe patch-upload file-name=XS602E003.xsupdate - Make a note of the output from this command, which is a UUID for the hotfix - file. You'll need it in another step later. - - (Optional) If you are applying other hotfixes as well, you can repeat the - commands in this section with the appropriate hotfix number. For example, - XS602E004.xsupdate. - - - - Manually live migrate all VMs on this host to another host. First, get a list of - the VMs on this host: - # xe vm-list - Then use this command to migrate each VM. Replace the example host name and VM - name with your own: - # xe vm-migrate live=true host=host-name vm=VM-name - - Troubleshooting - If you see a message like "You attempted an operation on a VM which requires - PV drivers to be installed but the drivers were not detected," run: - /opt/xensource/bin/make_migratable.sh - b6cf79c8-02ee-050b-922f-49583d9f1a14. - - - - Apply the hotfix. First, get the UUID of this host: - # xe host-list - Then use the following command to apply the hotfix. Replace the example host - UUID with the current host ID, and replace the hotfix UUID with the output from the - patch-upload command you ran on this machine earlier. You can also get the hotfix - UUID by running xe patch-list. - xe patch-apply host-uuid=host-uuid - uuid=hotfix-uuid - - - Copy the following files from the CloudStack Management Server to the - host. - - - - - - - Copy from here... - ...to here - - - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/xenserver60/NFSSR.py - /opt/xensource/sm/NFSSR.py - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/setupxenserver.sh - /opt/xensource/bin/setupxenserver.sh - - - /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver/make_migratable.sh - /opt/xensource/bin/make_migratable.sh - - - - - - - (Only for hotfixes XS602E005 and XS602E007) You need to apply a new Cloud - Support Pack. - - - Download the CSP software onto the XenServer host from one of the following - links: - For hotfix XS602E005: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E005/56710/xe-phase-2/xenserver-cloud-supp.tgz - For hotfix XS602E007: http://coltrane.eng.hq.xensource.com/release/XenServer-6.x/XS-6.0.2/hotfixes/XS602E007/57824/xe-phase-2/xenserver-cloud-supp.tgz - - - Extract the file: - # tar xf xenserver-cloud-supp.tgz - - - Run the following script: - # xe-install-supplemental-pack - xenserver-cloud-supp.iso - - - If the XenServer host is part of a zone that uses basic networking, disable - Open vSwitch (OVS): - # xe-switch-network-backend bridge - - - - - Reboot this XenServer host. - - - Run the following: - /opt/xensource/bin/setupxenserver.sh - - If the message "mv: cannot stat `/etc/cron.daily/logrotate': No such file or - directory" appears, you can safely ignore it. - - - - Run the following: - for pbd in `xe pbd-list currently-attached=false| grep ^uuid | awk - '{print $NF}'`; do xe pbd-plug uuid=$pbd ; - - - - On each slave host in the Xen pool, repeat these steps, starting from "manually - live migrate VMs." - - - - -
-
- - Version 4.1.0 -
- What’s New in 4.1 - Apache CloudStack 4.1.0 includes many new features. This section covers the most prominent new features and changes. -
-
- Issues Fixed in 4.1.0 - Apache CloudStack uses Jira - to track its issues. All new features and bugs for 4.1.0 have been tracked in Jira, and have - a standard naming convention of "CLOUDSTACK-NNNN" where "NNNN" is the issue number. - This section includes a summary of known issues against 4.0.0 that were fixed in 4.1.0. - - - - - - - - Defect - - - Description - - - - - - CS-16135 - Creating volumes after upgrading from snapshot taken in 2.2.14 no longer - deletes the snapshot physically from the secondary storage. - - - - -
-
- Known Issues in 4.1.0 - - - - - - - - Issue ID - - - Description - - - - - - CLOUDSTACK-1747 - mvn deploydb only creates 4.0 DB, not 4.1 - Due to tooling changes between 4.1 and 4.2, CloudStack's database is created using the 4.0 schema and updated to the 4.1 schema when the management server starts for the first time. It's OK to see the same schema if the management server has not started yet. - - - - CLOUDSTACK-1824 - Service CloudStack-Management is being displayed as cloud-management service - Many scripts and text entries have references to cloud-management rather than cloudstack-management due to the changeover between 4.0 and 4.1 to rename services. This is a minor issue and should be corrected by 4.2. - - - - - -
-
- +
+
+
+
From d6ed8d7cb53da08cacd04c3ac7435da5601e7f06 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Thu, 4 Apr 2013 21:24:41 +0530 Subject: [PATCH 12/81] Dedicate Public IP range --- .../configuration/ConfigurationService.java | 6 + api/src/com/cloud/event/EventTypes.java | 2 + .../admin/vlan/DedicatePublicIpRangeCmd.java | 116 +++++ .../admin/vlan/ReleasePublicIpRangeCmd.java | 77 ++++ client/tomcatconf/commands.properties.in | 2 + .../configuration/ConfigurationManager.java | 7 +- .../ConfigurationManagerImpl.java | 180 ++++++-- .../com/cloud/network/NetworkManagerImpl.java | 46 +- .../com/cloud/network/NetworkServiceImpl.java | 9 - .../cloud/server/ManagementServerImpl.java | 2 + .../com/cloud/user/AccountManagerImpl.java | 6 +- .../ConfigurationManagerTest.java | 400 ++++++++++++++++++ .../vpc/MockConfigurationManagerImpl.java | 32 +- .../component/test_public_ip_range.py | 149 +++++++ tools/marvin/marvin/integration/lib/base.py | 11 + 15 files changed, 997 insertions(+), 48 deletions(-) create mode 100644 api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java create mode 100644 server/test/com/cloud/configuration/ConfigurationManagerTest.java create mode 100644 test/integration/component/test_public_ip_range.py diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index e63fcece525..6937d0b64de 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -35,7 +35,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -234,6 +236,10 @@ public interface ConfigurationService { boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd); + Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException; + + boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd); + NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd); NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd); diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 704a1bfc02c..0dc16f3de7a 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -223,6 +223,8 @@ public class EventTypes { // VLANs/IP ranges public static final String EVENT_VLAN_IP_RANGE_CREATE = "VLAN.IP.RANGE.CREATE"; public static final String EVENT_VLAN_IP_RANGE_DELETE = "VLAN.IP.RANGE.DELETE"; + public static final String EVENT_VLAN_IP_RANGE_DEDICATE = "VLAN.IP.RANGE.DEDICATE"; + public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; public static final String EVENT_STORAGE_IP_RANGE_CREATE = "STORAGE.IP.RANGE.CREATE"; public static final String EVENT_STORAGE_IP_RANGE_DELETE = "STORAGE.IP.RANGE.DELETE"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java new file mode 100644 index 00000000000..c62857ca964 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java @@ -0,0 +1,116 @@ +// 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.api.command.admin.vlan; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import com.cloud.dc.Vlan; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + +@APICommand(name = "dedicatePublicIpRange", description="Dedicates a Public IP range to an account", responseObject=VlanIpRangeResponse.class) +public class DedicatePublicIpRangeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DedicatePublicIpRangeCmd.class.getName()); + + private static final String s_name = "dedicatepubliciprangeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, + required=true, description="the id of the VLAN IP range") + private Long id; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, + description="account who will own the VLAN") + private String accountName; + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, + description="project who will own the VLAN") + private Long projectId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType = DomainResponse.class, + required=true, description="domain ID of the account owning a VLAN") + private Long domainId; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + required=true, description="the Zone ID of the VLAN IP range") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public Long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException { + Vlan result = _configService.dedicatePublicIpRange(this); + if (result != null) { + VlanIpRangeResponse response = _responseGenerator.createVlanIpRangeResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate vlan ip range"); + } + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java new file mode 100644 index 00000000000..91cc7d33da9 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java @@ -0,0 +1,77 @@ +// 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.api.command.admin.vlan; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.log4j.Logger; + +import com.cloud.user.Account; + +@APICommand(name = "releasePublicIpRange", description="Releases a Public IP range back to the system pool", responseObject=SuccessResponse.class) +public class ReleasePublicIpRangeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ReleasePublicIpRangeCmd.class.getName()); + + private static final String s_name = "releasepubliciprangeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, + required=true, description="the id of the Public IP range") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = _configService.releasePublicIpRange(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release public ip range"); + } + } +} diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 163c2cee861..3e556726e64 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -124,6 +124,8 @@ listDiskOfferings=15 createVlanIpRange=1 deleteVlanIpRange=1 listVlanIpRanges=1 +dedicatePublicIpRange=1 +releasePublicIpRange=1 #### address commands associateIpAddress=15 diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java index 20e98845ac0..52b32f4d917 100644 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -30,6 +30,7 @@ import com.cloud.dc.Vlan; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -149,6 +150,10 @@ public interface ConfigurationManager extends ConfigurationService, Manager { */ boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); + boolean releasePublicIpRange(long userId, long vlanDbId, Account caller); + + Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, Long domainId, Long zoneId, Long projectId) throws ResourceAllocationException; + /** * Converts a comma separated list of tags to a List * @@ -210,7 +215,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager { ClusterVO getCluster(long id); - boolean deleteAccountSpecificVirtualRanges(long accountId); + boolean releaseAccountSpecificVirtualRanges(long accountId); /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 1526fb0e125..86d30b417e4 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -56,7 +56,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -2306,9 +2308,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Gateway, netmask and zoneId have to be passed in for virtual and direct untagged networks"); } - // if it's an account specific range, associate ip address list to the account - boolean associateIpRangeToAccount = false; - if (forVirtualNetwork) { if (vlanOwner != null) { @@ -2316,8 +2315,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //check resource limits _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountIpRange); - - associateIpRangeToAccount = true; } } @@ -2332,21 +2329,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); txn.commit(); - if (associateIpRangeToAccount) { - _networkMgr.associateIpAddressListToAccount(userId, vlanOwner.getId(), zoneId, vlan.getId(), null); - } - - // Associate ips to the network - if (associateIpRangeToAccount) { - if (network.getState() == Network.State.Implemented) { - s_logger.debug("Applying ip associations for vlan id=" + vlanId + " in network " + network); - if (!_networkMgr.applyIpAssociations(network, false)) { - s_logger.warn("Failed to apply ip associations for vlan id=1 as a part of add vlan range for account id=" + vlanOwner.getId()); - } - } else { - s_logger.trace("Network id=" + network.getId() + " is not Implemented, no need to apply ipAssociations"); - } - } return vlan; } @@ -2698,6 +2680,156 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + @Override + @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DEDICATE, eventDescription = "dedicating vlan ip range", async = false) + public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException { + Long vlanDbId = cmd.getId(); + String accountName = cmd.getAccountName(); + Long domainId = cmd.getDomainId(); + Long zoneId = cmd.getZoneId(); + Long projectId = cmd.getProjectId(); + + Vlan vlan = dedicatePublicIpRange(vlanDbId, accountName, domainId, zoneId, projectId); + + return vlan; + } + + @Override + @DB + public Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, Long domainId, Long zoneId, Long projectId) throws ResourceAllocationException { + // Check if account is valid + Account vlanOwner = null; + if (projectId != null) { + if (accountName != null) { + throw new InvalidParameterValueException("accountName and projectId are mutually exclusive"); + } + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + vlanOwner = _accountMgr.getAccount(project.getProjectAccountId()); + } + + if ((accountName != null) && (domainId != null)) { + vlanOwner = _accountDao.findActiveAccount(accountName, domainId); + if (vlanOwner == null) { + throw new InvalidParameterValueException("Please specify a valid account"); + } + } + + // Check if range is valid + VlanVO vlan = _vlanDao.findById(vlanDbId); + if (vlan == null) { + throw new InvalidParameterValueException("Please specify a valid Public IP range id"); + } + + // Check if range has already been dedicated + List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); + if (maps != null && !maps.isEmpty()) { + throw new InvalidParameterValueException("Specified Public IP range has already been dedicated"); + } + + // Verify that zone exists and is advanced + DataCenterVO zone = _zoneDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); + } + if (zone.getNetworkType() == NetworkType.Basic) { + throw new InvalidParameterValueException("Public IP range can be dedicated to an account only in the zone of type " + NetworkType.Advanced); + } + + // Check Public IP resource limits + int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false); + _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange); + + // Check if any of the Public IP addresses is allocated to another account + List ips = _publicIpAddressDao.listByVlanId(vlanDbId); + for (IPAddressVO ip : ips) { + Long allocatedToAccountId = ip.getAllocatedToAccountId(); + if (allocatedToAccountId != null) { + Account accountAllocatedTo = _accountMgr.getActiveAccountById(allocatedToAccountId); + if (!accountAllocatedTo.getAccountName().equalsIgnoreCase(accountName)) + throw new InvalidParameterValueException("Public IP address in range is already allocated to another account"); + } + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + // Create an AccountVlanMapVO entry + AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId()); + _accountVlanMapDao.persist(accountVlanMapVO); + + txn.commit(); + + return vlan; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_RELEASE, eventDescription = "releasing a public ip range", async = false) + public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { + Long vlanDbId = cmd.getId(); + + VlanVO vlan = _vlanDao.findById(vlanDbId); + if (vlan == null) { + throw new InvalidParameterValueException("Please specify a valid IP range id."); + } + + return releasePublicIpRange(vlanDbId, UserContext.current().getCallerUserId(), UserContext.current().getCaller()); + } + + @Override + @DB + public boolean releasePublicIpRange(long vlanDbId, long userId, Account caller) { + VlanVO vlan = _vlanDao.findById(vlanDbId); + + List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); + // Verify range is dedicated + if (acctVln == null || acctVln.isEmpty()) { + throw new InvalidParameterValueException("Can't release Public IP range " + vlanDbId + " as it not dedicated to any account"); + } + + // Check if range has any allocated public IPs + long allocIpCount = _publicIpAddressDao.countIPs(vlan.getDataCenterId(), vlanDbId, true); + boolean success = true; + if (allocIpCount > 0) { + try { + vlan = _vlanDao.acquireInLockTable(vlanDbId, 30); + if (vlan == null) { + throw new CloudRuntimeException("Unable to acquire vlan configuration: " + vlanDbId); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("lock vlan " + vlanDbId + " is acquired"); + } + List ips = _publicIpAddressDao.listByVlanId(vlanDbId); + for (IPAddressVO ip : ips) { + // Disassociate allocated IP's that are not in use + if ( !ip.isOneToOneNat() && !(ip.isSourceNat() && _networkModel.getNetwork(ip.getAssociatedWithNetworkId()) != null) && + !(_firewallDao.countRulesByIpId(ip.getId()) > 0) ) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Releasing Public IP addresses" + ip +" of vlan " + vlanDbId + " as part of Public IP" + + " range release to the system pool"); + } + success = success && _networkMgr.disassociatePublicIpAddress(ip.getId(), userId, caller); + } + } + if (!success) { + s_logger.warn("Some Public IP addresses that were not in use failed to be released as a part of" + + " vlan " + vlanDbId + "release to the system pool"); + } + } finally { + _vlanDao.releaseFromLockTable(vlanDbId); + } + } + + // A Public IP range can only be dedicated to one account at a time + if (_accountVlanMapDao.remove(acctVln.get(0).getId())) { + return true; + } else { + return false; + } + } + @Override public List csvTagsToList(String tags) { List tagsList = new ArrayList(); @@ -3957,14 +4089,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB - public boolean deleteAccountSpecificVirtualRanges(long accountId) { + public boolean releaseAccountSpecificVirtualRanges(long accountId) { List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId); boolean result = true; if (maps != null && !maps.isEmpty()) { Transaction txn = Transaction.currentTxn(); txn.start(); for (AccountVlanMapVO map : maps) { - if (!deleteVlanAndPublicIpRange(_accountMgr.getSystemUser().getId(), map.getVlanDbId(), + if (!releasePublicIpRange(map.getVlanDbId(), _accountMgr.getSystemUser().getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM))) { result = false; } @@ -3972,10 +4104,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (result) { txn.commit(); } else { - s_logger.error("Failed to delete account specific virtual ip ranges for account id=" + accountId); + s_logger.error("Failed to release account specific virtual ip ranges for account id=" + accountId); } } else { - s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to delete"); + s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to release"); } return result; } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 62960116dce..43a098877df 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -188,6 +188,7 @@ import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.*; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; @@ -348,7 +349,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } @DB - public PublicIp fetchNewPublicIp(long dcId, Long podId, Long vlanDbId, Account owner, VlanType vlanUse, + public PublicIp fetchNewPublicIp(long dcId, Long podId, List vlanDbIds, Account owner, VlanType vlanUse, Long guestNetworkId, boolean sourceNat, boolean assign, String requestedIp, boolean isSystem, Long vpcId) throws InsufficientAddressCapacityException { StringBuilder errorMessage = new StringBuilder("Unable to get ip adress in "); @@ -364,9 +365,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L errorMessage.append(" zone id=" + dcId); } - if (vlanDbId != null) { - sc.addAnd("vlanId", SearchCriteria.Op.EQ, vlanDbId); - errorMessage.append(", vlanId id=" + vlanDbId); + if ( vlanDbIds != null && !vlanDbIds.isEmpty() ) { + sc.setParameters("vlanId", vlanDbIds.toArray()); + errorMessage.append(", vlanId id=" + vlanDbIds.toArray()); } sc.setParameters("dc", dcId); @@ -526,14 +527,14 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } // If account has Account specific ip ranges, try to allocate ip from there - Long vlanId = null; + List vlanIds = new ArrayList(); List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ownerId); if (maps != null && !maps.isEmpty()) { - vlanId = maps.get(0).getVlanDbId(); + vlanIds.add(maps.get(0).getVlanDbId()); } - ip = fetchNewPublicIp(dcId, null, vlanId, owner, VlanType.VirtualNetwork, guestNtwkId, + ip = fetchNewPublicIp(dcId, null, vlanIds, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, false, null, false, vpcId); IPAddressVO publicIp = ip.ip(); @@ -669,6 +670,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L VlanType vlanType = VlanType.VirtualNetwork; boolean assign = false; + boolean allocateFromDedicatedRange = false; if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // zone is of type DataCenter. See DataCenterVO.java. @@ -702,8 +704,32 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L txn.start(); - ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, - false, assign, null, isSystem, null); + // If account has dedicated Public IP ranges, allocate IP from the dedicated range + List vlanDbIds = new ArrayList(); + List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ipOwner.getId()); + for (AccountVlanMapVO map : maps) { + vlanDbIds.add(map.getVlanDbId()); + } + if (vlanDbIds != null && !vlanDbIds.isEmpty()) { + allocateFromDedicatedRange = true; + } + + try { + if (allocateFromDedicatedRange) { + ip = fetchNewPublicIp(zone.getId(), null, vlanDbIds, ipOwner, vlanType, null, + false, assign, null, isSystem, null); + } + } catch(InsufficientAddressCapacityException e) { + s_logger.warn("All IPs dedicated to account " + ipOwner.getId() + " has been acquired." + + " Now acquiring from the system pool"); + txn.close(); + allocateFromDedicatedRange = false; + } + + if (!allocateFromDedicatedRange) { + ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, + isSystem, null); + } if (ip == null) { @@ -1070,7 +1096,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L AssignIpAddressSearch = _ipAddressDao.createSearchBuilder(); AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ); AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL); - AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.EQ); + AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN); SearchBuilder vlanSearch = _vlanDao.createSearchBuilder(); vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ); vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ); diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 4eb620c4243..a8cbaa75017 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -697,15 +697,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new IllegalArgumentException("only ip addresses that belong to a virtual network may be disassociated."); } - // Check for account wide pool. It will have an entry for account_vlan_map. - if (_accountVlanMapDao.findAccountVlanMap(ipVO.getAllocatedToAccountId(), ipVO.getVlanId()) != null) { - //see IPaddressVO.java - InvalidParameterValueException ex = new InvalidParameterValueException("Sepcified IP address uuid belongs to" + - " Account wide IP pool and cannot be disassociated"); - ex.addProxyObject("user_ip_address", ipAddressId, "ipAddressId"); - throw ex; - } - // don't allow releasing system ip address if (ipVO.getSystem()) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't release system IP address with specified id"); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index af77ba5645f..b89de9e122f 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -2090,6 +2090,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(CreateVlanIpRangeCmd.class); cmdList.add(DeleteVlanIpRangeCmd.class); cmdList.add(ListVlanIpRangesCmd.class); + cmdList.add(DedicatePublicIpRangeCmd.class); + cmdList.add(ReleasePublicIpRangeCmd.class); cmdList.add(AssignVMCmd.class); cmdList.add(MigrateVMCmd.class); cmdList.add(RecoverVMCmd.class); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 52ca79d5a60..417ec3f4523 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -683,13 +683,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M accountCleanupNeeded = true; } - // delete account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned + // release account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned // up successfully if (networksDeleted) { - if (!_configMgr.deleteAccountSpecificVirtualRanges(accountId)) { + if (!_configMgr.releaseAccountSpecificVirtualRanges(accountId)) { accountCleanupNeeded = true; } else { - s_logger.debug("Account specific Virtual IP ranges " + " are successfully deleted as a part of account id=" + accountId + " cleanup."); + s_logger.debug("Account specific Virtual IP ranges " + " are successfully released as a part of account id=" + accountId + " cleanup."); } } diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java new file mode 100644 index 00000000000..eeb10737a63 --- /dev/null +++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java @@ -0,0 +1,400 @@ +package com.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.lang.reflect.Field; + +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.AccountVlanMapVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.AccountVlanMapDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.network.NetworkManager; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.projects.ProjectManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.net.Ip; + +import junit.framework.Assert; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doNothing; + +public class ConfigurationManagerTest { + + private static final Logger s_logger = Logger.getLogger(ConfigurationManagerTest.class); + + ConfigurationManagerImpl configurationMgr = new ConfigurationManagerImpl(); + + DedicatePublicIpRangeCmd dedicatePublicIpRangesCmd = new DedicatePublicIpRangeCmdExtn(); + Class _dedicatePublicIpRangeClass = dedicatePublicIpRangesCmd.getClass().getSuperclass(); + + ReleasePublicIpRangeCmd releasePublicIpRangesCmd = new ReleasePublicIpRangeCmdExtn(); + Class _releasePublicIpRangeClass = releasePublicIpRangesCmd.getClass().getSuperclass(); + + @Mock AccountManager _accountMgr; + @Mock ProjectManager _projectMgr; + @Mock ResourceLimitService _resourceLimitMgr; + @Mock NetworkManager _networkMgr; + @Mock AccountDao _accountDao; + @Mock VlanDao _vlanDao; + @Mock AccountVlanMapDao _accountVlanMapDao; + @Mock IPAddressDao _publicIpAddressDao; + @Mock DataCenterDao _zoneDao; + @Mock FirewallRulesDao _firewallDao; + + VlanVO vlan = new VlanVO(); + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + configurationMgr._accountMgr = _accountMgr; + configurationMgr._projectMgr = _projectMgr; + configurationMgr._resourceLimitMgr = _resourceLimitMgr; + configurationMgr._networkMgr = _networkMgr; + configurationMgr._accountDao = _accountDao; + configurationMgr._vlanDao = _vlanDao; + configurationMgr._accountVlanMapDao = _accountVlanMapDao; + configurationMgr._publicIpAddressDao = _publicIpAddressDao; + configurationMgr._zoneDao = _zoneDao; + configurationMgr._firewallDao = _firewallDao; + + Account account = (Account) new AccountVO("testaccount", 1, "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(configurationMgr._accountMgr.getAccount(anyLong())).thenReturn(account); + when(configurationMgr._accountDao.findActiveAccount(anyString(), anyLong())).thenReturn(account); + when(configurationMgr._accountMgr.getActiveAccountById(anyLong())).thenReturn(account); + + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); + + doNothing().when(configurationMgr._resourceLimitMgr).checkResourceLimit(any(Account.class), + any(ResourceType.class), anyLong()); + + when(configurationMgr._accountVlanMapDao.persist(any(AccountVlanMapVO.class))).thenReturn(new AccountVlanMapVO()); + + when(configurationMgr._vlanDao.acquireInLockTable(anyLong(), anyInt())).thenReturn(vlan); + + UserContext.registerContext(1, account, null, true); + + Field dedicateIdField = _dedicatePublicIpRangeClass.getDeclaredField("id"); + dedicateIdField.setAccessible(true); + dedicateIdField.set(dedicatePublicIpRangesCmd, 1L); + + Field accountNameField = _dedicatePublicIpRangeClass.getDeclaredField("accountName"); + accountNameField.setAccessible(true); + accountNameField.set(dedicatePublicIpRangesCmd, "accountname"); + + Field projectIdField = _dedicatePublicIpRangeClass.getDeclaredField("projectId"); + projectIdField.setAccessible(true); + projectIdField.set(dedicatePublicIpRangesCmd, null); + + Field domainIdField = _dedicatePublicIpRangeClass.getDeclaredField("domainId"); + domainIdField.setAccessible(true); + domainIdField.set(dedicatePublicIpRangesCmd, 1L); + + Field zoneIdField = _dedicatePublicIpRangeClass.getDeclaredField("zoneId"); + zoneIdField.setAccessible(true); + zoneIdField.set(dedicatePublicIpRangesCmd, 1L); + + Field releaseIdField = _releasePublicIpRangeClass.getDeclaredField("id"); + releaseIdField.setAccessible(true); + releaseIdField.set(releasePublicIpRangesCmd, 1L); + } + + @Test + public void testDedicatePublicIpRange() throws Exception { + + s_logger.info("Running tests for DedicatePublicIpRange API"); + + /* + * TEST 1: given valid parameters DedicatePublicIpRange should succeed + */ + runDedicatePublicIpRangePostiveTest(); + + /* + * TEST 2: given invalid public ip range DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeInvalidRange(); + /* + * TEST 3: given public IP range that is already dedicated to a different account DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeDedicatedRange(); + + /* + * TEST 4: given zone is of type Basic DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeInvalidZone(); + + /* + * TEST 5: given range is already allocated to a different account DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeIPAdressAllocated(); + } + + @Test + public void testReleasePublicIpRange() throws Exception { + + s_logger.info("Running tests for DedicatePublicIpRange API"); + + /* + * TEST 1: given valid parameters and no allocated public ip's in the range ReleasePublicIpRange should succeed + */ + runReleasePublicIpRangePostiveTest1(); + + /* + * TEST 2: given valid parameters ReleasePublicIpRange should succeed + */ + runReleasePublicIpRangePostiveTest2(); + + /* + * TEST 3: given range doesn't exist + */ + runReleasePublicIpRangeInvalidIpRange(); + + /* + * TEST 4: given range is not dedicated to any account + */ + runReleaseNonDedicatedPublicIpRange(); + } + + void runDedicatePublicIpRangePostiveTest() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangePostiveTest"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + Vlan result = configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing runDedicatePublicIpRangePostiveTest message: " + e.toString()); + } finally { + txn.close("runDedicatePublicIpRangePostiveTest"); + } + } + + void runDedicatePublicIpRangeInvalidRange() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Please specify a valid Public IP range id")); + } finally { + txn.close("runDedicatePublicIpRangeInvalidRange"); + } + } + + void runDedicatePublicIpRangeDedicatedRange() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeDedicatedRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + // public ip range is already dedicated + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP range has already been dedicated")); + } finally { + txn.close("runDedicatePublicIpRangePublicIpRangeDedicated"); + } + } + + void runDedicatePublicIpRangeInvalidZone() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidZone"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); + + // public ip range belongs to zone of type basic + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Basic, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP range can be dedicated to an account only in the zone of type Advanced")); + } finally { + txn.close("runDedicatePublicIpRangeInvalidZone"); + } + } + + void runDedicatePublicIpRangeIPAdressAllocated() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeIPAdressAllocated"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + // one of the ip addresses of the range is allocated to different account + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddress.setAllocatedToAccountId(1L); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP address in range is already allocated to another account")); + } finally { + txn.close("runDedicatePublicIpRangeIPAdressAllocated"); + } + } + + void runReleasePublicIpRangePostiveTest1() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest1"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + // no allocated ip's + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(0); + + when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); + try { + Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + Assert.assertTrue(result); + } catch (Exception e) { + s_logger.info("exception in testing runReleasePublicIpRangePostiveTest1 message: " + e.toString()); + } finally { + txn.close("runReleasePublicIpRangePostiveTest1"); + } + } + + void runReleasePublicIpRangePostiveTest2() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest2"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + when(configurationMgr._firewallDao.countRulesByIpId(anyLong())).thenReturn(0L); + + when(configurationMgr._networkMgr.disassociatePublicIpAddress(anyLong(), anyLong(), any(Account.class))).thenReturn(true); + + when(configurationMgr._vlanDao.releaseFromLockTable(anyLong())).thenReturn(true); + + when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); + try { + Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + Assert.assertTrue(result); + } catch (Exception e) { + s_logger.info("exception in testing runReleasePublicIpRangePostiveTest2 message: " + e.toString()); + } finally { + txn.close("runReleasePublicIpRangePostiveTest2"); + } + } + + void runReleasePublicIpRangeInvalidIpRange() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangeInvalidIpRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); + try { + configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Please specify a valid IP range id")); + } finally { + txn.close("runReleasePublicIpRangeInvalidIpRange"); + } + } + + void runReleaseNonDedicatedPublicIpRange() throws Exception { + Transaction txn = Transaction.open("runReleaseNonDedicatedPublicIpRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); + try { + configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("as it not dedicated to any account")); + } finally { + txn.close("runReleaseNonDedicatedPublicIpRange"); + } + } + + + public class DedicatePublicIpRangeCmdExtn extends DedicatePublicIpRangeCmd { + public long getEntityOwnerId() { + return 1; + } + } + + public class ReleasePublicIpRangeCmdExtn extends ReleasePublicIpRangeCmd { + public long getEntityOwnerId() { + return 1; + } + } +} \ No newline at end of file diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index d96e831cfeb..f781e61e671 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -40,7 +40,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -544,7 +546,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu * @see com.cloud.configuration.ConfigurationManager#deleteAccountSpecificVirtualRanges(long) */ @Override - public boolean deleteAccountSpecificVirtualRanges(long accountId) { + public boolean releaseAccountSpecificVirtualRanges(long accountId) { // TODO Auto-generated method stub return false; } @@ -613,5 +615,33 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } + @Override + public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) + throws ResourceAllocationException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean releasePublicIpRange(long userId, long vlanDbId, + Account caller) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, + Long domainId, Long zoneId, Long projectId) + throws ResourceAllocationException { + // TODO Auto-generated method stub + return null; + } + } diff --git a/test/integration/component/test_public_ip_range.py b/test/integration/component/test_public_ip_range.py new file mode 100644 index 00000000000..1cb2e48acd7 --- /dev/null +++ b/test/integration/component/test_public_ip_range.py @@ -0,0 +1,149 @@ +# 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 Dedicating Public IP addresses +""" +#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 * +import datetime + +class Services: + """Test Dedicating Public IP addresses + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "gateway": "10.102.196.1", + "netmask": "255.255.255.0", + "forvirtualnetwork": "true", + "startip": "10.102.196.70", + "endip": "10.102.196.73", + "zoneid": "1", + "podid": "", + "vlan": "101", + "sleep": 60, + "timeout": 10, + } + +class TesDedicatePublicIPRange(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TesDedicatePublicIPRange, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup = [ + #cls.account, + ] + 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.cleanup = [] + return + + def tearDown(self): + try: + # Clean up + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags = ["publiciprange", "dedicate"]) + def test_createPublicIpRange(self): + """Test create public IP range + """ + + + # Validate the following: + # 1. Create public IP range. + # 2. Created IP range should be present, verify with listVlanIpRanges + + self.debug("Creating Public IP range") + self.public_ip_range = PublicIpRange.create( + self.api_client, + self.services + ) + list_public_ip_range_response = PublicIpRange.list( + self.apiclient, + id=self.public_ip_range.vlan.id + ) + self.debug( + "Verify listPublicIpRanges response for public ip ranges: %s" \ + % self.public_ip_range.vlan.id + ) + self.assertEqual( + isinstance(list_public_ip_range_response, list), + True, + "Check for list Public IP range response" + ) + public_ip_response = list_public_ip_range_response[0] + self.assertEqual( + public_ip_response.id, + self.public_ip_range.vlan.id, + "Check public ip range response id is in listVlanIpRanges" + ) + self.debug("Dedicating Public IP range"); + self.debug("Vlan id %s" % self.public_ip_range.vlan.id); + self.debug("Zone id %s" % self.zone.id); + self.debug("Account name %s" % self.account.account.name); + self.debug("Domain id %s" % self.account.account.domainid); + dedicate_public_ip_range_response = PublicIpRange.dedicate( + self.apiclient, + self.public_ip_range.vlan.id, + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + return diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index f3370eb3190..0e117bbdfc2 100644 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -1882,6 +1882,17 @@ class PublicIpRange: [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listVlanIpRanges(cmd)) + @classmethod + def dedicate(cls, apiclient, id, zoneid, account=None, domainid=None, projectid=None): + """Dedicate VLAN IP range""" + + cmd = dedicatePublicIpRange.dedicatePublicIpRangeCmd() + cmd.id = id + cmd.account = account + cmd.domainid = domainid + cmd.projectid = projectid + cmd.zoneid = zoneid + return PublicIpRange(apiclient.dedicatePublicIpRange(cmd).__dict__) class SecondaryStorage: """Manage Secondary storage""" From bf72a36ea809ec0f9b354bf6a72dd2a09e1fcf6f Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 8 Apr 2013 14:39:18 -0700 Subject: [PATCH 13/81] CLOUDSTACK-1974: cloudstack UI - Infrastructure menu - zone detail - public traffic type - IP Ranges tab - add new action "Release from account", "Add Account" for existing IP Ranges. --- ui/scripts/system.js | 78 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8d08584dc60..d89f6b69dcd 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -588,7 +588,83 @@ } }); } - } + }, + /* + releaseFromAccount: { + label: 'Release from Account', + action: function(args) { + $.ajax({ + url: createURL('releasePublicIpRange'), + data: { + id: args.context.multiRule[0].id + }, + success: function(json) { + args.response.success({ + notification: { + label: 'release from account', + poll: function(args) { + args.complete(); + } + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + }, + addAccount: { + label: 'Add Account', + createForm: { + title: 'Add Account', + fields: { + account: { label: 'Account' }, + domainid: { + label: 'Domain', + select: function(args) { + $.ajax({ + url: createURL('listDomains'), + data: { listAll: true }, + success: function(json) { + args.response.success({ + data: $.map(json.listdomainsresponse.domain, function(domain) { + return { + id: domain.id, + description: domain.path + }; + }) + }); + } + }); + } + } + } + }, + action: function(args) { + var data = { + id: args.context.multiRule[0].id + }; + $.ajax({ + url: createURL('dedicatePublicIpRange'), + data: data, + success: function(json) { + args.response.success({ + notification: { + label: 'Add Account', + poll: function(args) { + args.complete(); + } + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + */ }, dataProvider: function(args) { $.ajax({ From 3cbbe2bb04da75147b8060955cbfb32e222f6780 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 8 Apr 2013 16:51:10 -0700 Subject: [PATCH 14/81] RAT check broken by d6ed8d7cb. Fix --- .../ConfigurationManagerTest.java | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java index eeb10737a63..321f605a1cc 100644 --- a/server/test/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java @@ -1,9 +1,36 @@ +// 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.configuration; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.lang.reflect.Field; + +import junit.framework.Assert; import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; @@ -15,10 +42,10 @@ import org.mockito.MockitoAnnotations; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.AccountVlanMapVO; +import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Vlan; import com.cloud.dc.VlanVO; -import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; @@ -36,12 +63,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.Ip; -import junit.framework.Assert; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.doNothing; - public class ConfigurationManagerTest { private static final Logger s_logger = Logger.getLogger(ConfigurationManagerTest.class); From 67a99cbbc3f0c415a7a6179e395e7a8b5655f6f0 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 8 Apr 2013 17:07:14 -0700 Subject: [PATCH 15/81] CLOUDSTACK-524: in some cases (especially the built-in CentOS template), the template downloader does not use the configured http proxy The DownloadProgress command is used to restart failed or stuck download jobs -- It would not include the proxy information, unlike the DownloadCommand which always did Signed-off-by: Chiradeep Vittal --- .../cloud/storage/download/DownloadMonitorImpl.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index 7ed6f3f27d6..ee8df745fc7 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -37,21 +37,17 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; - import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.DownloadCommand; - import com.cloud.agent.api.storage.DownloadCommand.Proxy; import com.cloud.agent.api.storage.DownloadCommand.ResourceType; -import com.cloud.agent.api.storage.DownloadProgressCommand.RequestType; - import com.cloud.agent.api.storage.DownloadProgressCommand; +import com.cloud.agent.api.storage.DownloadProgressCommand.RequestType; import com.cloud.agent.api.storage.ListTemplateAnswer; import com.cloud.agent.api.storage.ListTemplateCommand; import com.cloud.agent.api.storage.ListVolumeAnswer; import com.cloud.agent.api.storage.ListVolumeCommand; - import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.configuration.Config; @@ -72,13 +68,12 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; import com.cloud.storage.Storage.ImageFormat; - import com.cloud.storage.StorageManager; import com.cloud.storage.SwiftVO; import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; -import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.VolumeHostVO; import com.cloud.storage.VolumeVO; @@ -91,7 +86,6 @@ import com.cloud.storage.dao.VMTemplateSwiftDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeHostDao; - import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.swift.SwiftManager; import com.cloud.storage.template.TemplateConstants; @@ -284,10 +278,10 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor String sourceChecksum = this.templateMgr.getChecksum(srcTmpltHost.getHostId(), srcTmpltHost.getInstallPath()); DownloadCommand dcmd = new DownloadCommand(destServer.getStorageUrl(), url, template, TemplateConstants.DEFAULT_HTTP_AUTH_USER, _copyAuthPasswd, maxTemplateSizeInBytes); - dcmd.setProxy(getHttpProxy()); if (downloadJobExists) { dcmd = new DownloadProgressCommand(dcmd, destTmpltHost.getJobId(), RequestType.GET_OR_RESTART); } + dcmd.setProxy(getHttpProxy()); dcmd.setChecksum(sourceChecksum); // We need to set the checksum as the source template might be a compressed url and have cksum for compressed image. Bug #10775 HostVO ssAhost = _ssvmMgr.pickSsvmHost(destServer); if( ssAhost == null ) { From 98bc240d2a7f62a680a569b3789a65923906cd00 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 8 Apr 2013 17:15:24 -0700 Subject: [PATCH 16/81] Revert "Dedicate Public IP range" This reverts commit d6ed8d7cb53da08cacd04c3ac7435da5601e7f06. Conflicts: server/test/com/cloud/configuration/ConfigurationManagerTest.java --- .../configuration/ConfigurationService.java | 6 - api/src/com/cloud/event/EventTypes.java | 2 - .../admin/vlan/DedicatePublicIpRangeCmd.java | 116 ----- .../admin/vlan/ReleasePublicIpRangeCmd.java | 77 ---- client/tomcatconf/commands.properties.in | 2 - .../configuration/ConfigurationManager.java | 7 +- .../ConfigurationManagerImpl.java | 180 +------- .../com/cloud/network/NetworkManagerImpl.java | 46 +- .../com/cloud/network/NetworkServiceImpl.java | 9 + .../cloud/server/ManagementServerImpl.java | 2 - .../com/cloud/user/AccountManagerImpl.java | 6 +- .../ConfigurationManagerTest.java | 421 ------------------ .../vpc/MockConfigurationManagerImpl.java | 32 +- .../component/test_public_ip_range.py | 149 ------- tools/marvin/marvin/integration/lib/base.py | 11 - 15 files changed, 48 insertions(+), 1018 deletions(-) delete mode 100644 api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java delete mode 100644 api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java delete mode 100644 server/test/com/cloud/configuration/ConfigurationManagerTest.java delete mode 100644 test/integration/component/test_public_ip_range.py diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index 6937d0b64de..e63fcece525 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -35,9 +35,7 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -236,10 +234,6 @@ public interface ConfigurationService { boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd); - Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException; - - boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd); - NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd); NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd); diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 0dc16f3de7a..704a1bfc02c 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -223,8 +223,6 @@ public class EventTypes { // VLANs/IP ranges public static final String EVENT_VLAN_IP_RANGE_CREATE = "VLAN.IP.RANGE.CREATE"; public static final String EVENT_VLAN_IP_RANGE_DELETE = "VLAN.IP.RANGE.DELETE"; - public static final String EVENT_VLAN_IP_RANGE_DEDICATE = "VLAN.IP.RANGE.DEDICATE"; - public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; public static final String EVENT_STORAGE_IP_RANGE_CREATE = "STORAGE.IP.RANGE.CREATE"; public static final String EVENT_STORAGE_IP_RANGE_DELETE = "STORAGE.IP.RANGE.DELETE"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java deleted file mode 100644 index c62857ca964..00000000000 --- a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java +++ /dev/null @@ -1,116 +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 org.apache.cloudstack.api.command.admin.vlan; - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.VlanIpRangeResponse; -import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.log4j.Logger; - -import com.cloud.dc.Vlan; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.user.Account; - -@APICommand(name = "dedicatePublicIpRange", description="Dedicates a Public IP range to an account", responseObject=VlanIpRangeResponse.class) -public class DedicatePublicIpRangeCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(DedicatePublicIpRangeCmd.class.getName()); - - private static final String s_name = "dedicatepubliciprangeresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, - required=true, description="the id of the VLAN IP range") - private Long id; - - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, - description="account who will own the VLAN") - private String accountName; - - @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, - description="project who will own the VLAN") - private Long projectId; - - @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType = DomainResponse.class, - required=true, description="domain ID of the account owning a VLAN") - private Long domainId; - - @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, - required=true, description="the Zone ID of the VLAN IP range") - private Long zoneId; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - public String getAccountName() { - return accountName; - } - - public Long getDomainId() { - return domainId; - } - - public Long getProjectId() { - return projectId; - } - - public Long getZoneId() { - return zoneId; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } - - @Override - public void execute() throws ResourceUnavailableException, ResourceAllocationException { - Vlan result = _configService.dedicatePublicIpRange(this); - if (result != null) { - VlanIpRangeResponse response = _responseGenerator.createVlanIpRangeResponse(result); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate vlan ip range"); - } - } - -} diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java deleted file mode 100644 index 91cc7d33da9..00000000000 --- a/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java +++ /dev/null @@ -1,77 +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 org.apache.cloudstack.api.command.admin.vlan; - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.SuccessResponse; -import org.apache.cloudstack.api.response.VlanIpRangeResponse; -import org.apache.log4j.Logger; - -import com.cloud.user.Account; - -@APICommand(name = "releasePublicIpRange", description="Releases a Public IP range back to the system pool", responseObject=SuccessResponse.class) -public class ReleasePublicIpRangeCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(ReleasePublicIpRangeCmd.class.getName()); - - private static final String s_name = "releasepubliciprangeresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, - required=true, description="the id of the Public IP range") - private Long id; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; - } - - @Override - public void execute(){ - boolean result = _configService.releasePublicIpRange(this); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release public ip range"); - } - } -} diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 3e556726e64..163c2cee861 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -124,8 +124,6 @@ listDiskOfferings=15 createVlanIpRange=1 deleteVlanIpRange=1 listVlanIpRanges=1 -dedicatePublicIpRange=1 -releasePublicIpRange=1 #### address commands associateIpAddress=15 diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java index 52b32f4d917..20e98845ac0 100644 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -30,7 +30,6 @@ import com.cloud.dc.Vlan; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -150,10 +149,6 @@ public interface ConfigurationManager extends ConfigurationService, Manager { */ boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); - boolean releasePublicIpRange(long userId, long vlanDbId, Account caller); - - Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, Long domainId, Long zoneId, Long projectId) throws ResourceAllocationException; - /** * Converts a comma separated list of tags to a List * @@ -215,7 +210,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager { ClusterVO getCluster(long id); - boolean releaseAccountSpecificVirtualRanges(long accountId); + boolean deleteAccountSpecificVirtualRanges(long accountId); /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 86d30b417e4..1526fb0e125 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -56,9 +56,7 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -2308,6 +2306,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Gateway, netmask and zoneId have to be passed in for virtual and direct untagged networks"); } + // if it's an account specific range, associate ip address list to the account + boolean associateIpRangeToAccount = false; + if (forVirtualNetwork) { if (vlanOwner != null) { @@ -2315,6 +2316,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //check resource limits _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountIpRange); + + associateIpRangeToAccount = true; } } @@ -2329,6 +2332,21 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); txn.commit(); + if (associateIpRangeToAccount) { + _networkMgr.associateIpAddressListToAccount(userId, vlanOwner.getId(), zoneId, vlan.getId(), null); + } + + // Associate ips to the network + if (associateIpRangeToAccount) { + if (network.getState() == Network.State.Implemented) { + s_logger.debug("Applying ip associations for vlan id=" + vlanId + " in network " + network); + if (!_networkMgr.applyIpAssociations(network, false)) { + s_logger.warn("Failed to apply ip associations for vlan id=1 as a part of add vlan range for account id=" + vlanOwner.getId()); + } + } else { + s_logger.trace("Network id=" + network.getId() + " is not Implemented, no need to apply ipAssociations"); + } + } return vlan; } @@ -2680,156 +2698,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - @Override - @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DEDICATE, eventDescription = "dedicating vlan ip range", async = false) - public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException { - Long vlanDbId = cmd.getId(); - String accountName = cmd.getAccountName(); - Long domainId = cmd.getDomainId(); - Long zoneId = cmd.getZoneId(); - Long projectId = cmd.getProjectId(); - - Vlan vlan = dedicatePublicIpRange(vlanDbId, accountName, domainId, zoneId, projectId); - - return vlan; - } - - @Override - @DB - public Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, Long domainId, Long zoneId, Long projectId) throws ResourceAllocationException { - // Check if account is valid - Account vlanOwner = null; - if (projectId != null) { - if (accountName != null) { - throw new InvalidParameterValueException("accountName and projectId are mutually exclusive"); - } - Project project = _projectMgr.getProject(projectId); - if (project == null) { - throw new InvalidParameterValueException("Unable to find project by id " + projectId); - } - vlanOwner = _accountMgr.getAccount(project.getProjectAccountId()); - } - - if ((accountName != null) && (domainId != null)) { - vlanOwner = _accountDao.findActiveAccount(accountName, domainId); - if (vlanOwner == null) { - throw new InvalidParameterValueException("Please specify a valid account"); - } - } - - // Check if range is valid - VlanVO vlan = _vlanDao.findById(vlanDbId); - if (vlan == null) { - throw new InvalidParameterValueException("Please specify a valid Public IP range id"); - } - - // Check if range has already been dedicated - List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); - if (maps != null && !maps.isEmpty()) { - throw new InvalidParameterValueException("Specified Public IP range has already been dedicated"); - } - - // Verify that zone exists and is advanced - DataCenterVO zone = _zoneDao.findById(zoneId); - if (zone == null) { - throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); - } - if (zone.getNetworkType() == NetworkType.Basic) { - throw new InvalidParameterValueException("Public IP range can be dedicated to an account only in the zone of type " + NetworkType.Advanced); - } - - // Check Public IP resource limits - int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false); - _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange); - - // Check if any of the Public IP addresses is allocated to another account - List ips = _publicIpAddressDao.listByVlanId(vlanDbId); - for (IPAddressVO ip : ips) { - Long allocatedToAccountId = ip.getAllocatedToAccountId(); - if (allocatedToAccountId != null) { - Account accountAllocatedTo = _accountMgr.getActiveAccountById(allocatedToAccountId); - if (!accountAllocatedTo.getAccountName().equalsIgnoreCase(accountName)) - throw new InvalidParameterValueException("Public IP address in range is already allocated to another account"); - } - } - - Transaction txn = Transaction.currentTxn(); - txn.start(); - - // Create an AccountVlanMapVO entry - AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId()); - _accountVlanMapDao.persist(accountVlanMapVO); - - txn.commit(); - - return vlan; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_RELEASE, eventDescription = "releasing a public ip range", async = false) - public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { - Long vlanDbId = cmd.getId(); - - VlanVO vlan = _vlanDao.findById(vlanDbId); - if (vlan == null) { - throw new InvalidParameterValueException("Please specify a valid IP range id."); - } - - return releasePublicIpRange(vlanDbId, UserContext.current().getCallerUserId(), UserContext.current().getCaller()); - } - - @Override - @DB - public boolean releasePublicIpRange(long vlanDbId, long userId, Account caller) { - VlanVO vlan = _vlanDao.findById(vlanDbId); - - List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); - // Verify range is dedicated - if (acctVln == null || acctVln.isEmpty()) { - throw new InvalidParameterValueException("Can't release Public IP range " + vlanDbId + " as it not dedicated to any account"); - } - - // Check if range has any allocated public IPs - long allocIpCount = _publicIpAddressDao.countIPs(vlan.getDataCenterId(), vlanDbId, true); - boolean success = true; - if (allocIpCount > 0) { - try { - vlan = _vlanDao.acquireInLockTable(vlanDbId, 30); - if (vlan == null) { - throw new CloudRuntimeException("Unable to acquire vlan configuration: " + vlanDbId); - } - if (s_logger.isDebugEnabled()) { - s_logger.debug("lock vlan " + vlanDbId + " is acquired"); - } - List ips = _publicIpAddressDao.listByVlanId(vlanDbId); - for (IPAddressVO ip : ips) { - // Disassociate allocated IP's that are not in use - if ( !ip.isOneToOneNat() && !(ip.isSourceNat() && _networkModel.getNetwork(ip.getAssociatedWithNetworkId()) != null) && - !(_firewallDao.countRulesByIpId(ip.getId()) > 0) ) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Releasing Public IP addresses" + ip +" of vlan " + vlanDbId + " as part of Public IP" + - " range release to the system pool"); - } - success = success && _networkMgr.disassociatePublicIpAddress(ip.getId(), userId, caller); - } - } - if (!success) { - s_logger.warn("Some Public IP addresses that were not in use failed to be released as a part of" + - " vlan " + vlanDbId + "release to the system pool"); - } - } finally { - _vlanDao.releaseFromLockTable(vlanDbId); - } - } - - // A Public IP range can only be dedicated to one account at a time - if (_accountVlanMapDao.remove(acctVln.get(0).getId())) { - return true; - } else { - return false; - } - } - @Override public List csvTagsToList(String tags) { List tagsList = new ArrayList(); @@ -4089,14 +3957,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB - public boolean releaseAccountSpecificVirtualRanges(long accountId) { + public boolean deleteAccountSpecificVirtualRanges(long accountId) { List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId); boolean result = true; if (maps != null && !maps.isEmpty()) { Transaction txn = Transaction.currentTxn(); txn.start(); for (AccountVlanMapVO map : maps) { - if (!releasePublicIpRange(map.getVlanDbId(), _accountMgr.getSystemUser().getId(), + if (!deleteVlanAndPublicIpRange(_accountMgr.getSystemUser().getId(), map.getVlanDbId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM))) { result = false; } @@ -4104,10 +3972,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (result) { txn.commit(); } else { - s_logger.error("Failed to release account specific virtual ip ranges for account id=" + accountId); + s_logger.error("Failed to delete account specific virtual ip ranges for account id=" + accountId); } } else { - s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to release"); + s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to delete"); } return result; } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 43a098877df..62960116dce 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -188,7 +188,6 @@ import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.*; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; @@ -349,7 +348,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } @DB - public PublicIp fetchNewPublicIp(long dcId, Long podId, List vlanDbIds, Account owner, VlanType vlanUse, + public PublicIp fetchNewPublicIp(long dcId, Long podId, Long vlanDbId, Account owner, VlanType vlanUse, Long guestNetworkId, boolean sourceNat, boolean assign, String requestedIp, boolean isSystem, Long vpcId) throws InsufficientAddressCapacityException { StringBuilder errorMessage = new StringBuilder("Unable to get ip adress in "); @@ -365,9 +364,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L errorMessage.append(" zone id=" + dcId); } - if ( vlanDbIds != null && !vlanDbIds.isEmpty() ) { - sc.setParameters("vlanId", vlanDbIds.toArray()); - errorMessage.append(", vlanId id=" + vlanDbIds.toArray()); + if (vlanDbId != null) { + sc.addAnd("vlanId", SearchCriteria.Op.EQ, vlanDbId); + errorMessage.append(", vlanId id=" + vlanDbId); } sc.setParameters("dc", dcId); @@ -527,14 +526,14 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } // If account has Account specific ip ranges, try to allocate ip from there - List vlanIds = new ArrayList(); + Long vlanId = null; List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ownerId); if (maps != null && !maps.isEmpty()) { - vlanIds.add(maps.get(0).getVlanDbId()); + vlanId = maps.get(0).getVlanDbId(); } - ip = fetchNewPublicIp(dcId, null, vlanIds, owner, VlanType.VirtualNetwork, guestNtwkId, + ip = fetchNewPublicIp(dcId, null, vlanId, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, false, null, false, vpcId); IPAddressVO publicIp = ip.ip(); @@ -670,7 +669,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L VlanType vlanType = VlanType.VirtualNetwork; boolean assign = false; - boolean allocateFromDedicatedRange = false; if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // zone is of type DataCenter. See DataCenterVO.java. @@ -704,32 +702,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L txn.start(); - // If account has dedicated Public IP ranges, allocate IP from the dedicated range - List vlanDbIds = new ArrayList(); - List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ipOwner.getId()); - for (AccountVlanMapVO map : maps) { - vlanDbIds.add(map.getVlanDbId()); - } - if (vlanDbIds != null && !vlanDbIds.isEmpty()) { - allocateFromDedicatedRange = true; - } - - try { - if (allocateFromDedicatedRange) { - ip = fetchNewPublicIp(zone.getId(), null, vlanDbIds, ipOwner, vlanType, null, - false, assign, null, isSystem, null); - } - } catch(InsufficientAddressCapacityException e) { - s_logger.warn("All IPs dedicated to account " + ipOwner.getId() + " has been acquired." + - " Now acquiring from the system pool"); - txn.close(); - allocateFromDedicatedRange = false; - } - - if (!allocateFromDedicatedRange) { - ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, - isSystem, null); - } + ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, + false, assign, null, isSystem, null); if (ip == null) { @@ -1096,7 +1070,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L AssignIpAddressSearch = _ipAddressDao.createSearchBuilder(); AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ); AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL); - AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN); + AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.EQ); SearchBuilder vlanSearch = _vlanDao.createSearchBuilder(); vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ); vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ); diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index a8cbaa75017..4eb620c4243 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -697,6 +697,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new IllegalArgumentException("only ip addresses that belong to a virtual network may be disassociated."); } + // Check for account wide pool. It will have an entry for account_vlan_map. + if (_accountVlanMapDao.findAccountVlanMap(ipVO.getAllocatedToAccountId(), ipVO.getVlanId()) != null) { + //see IPaddressVO.java + InvalidParameterValueException ex = new InvalidParameterValueException("Sepcified IP address uuid belongs to" + + " Account wide IP pool and cannot be disassociated"); + ex.addProxyObject("user_ip_address", ipAddressId, "ipAddressId"); + throw ex; + } + // don't allow releasing system ip address if (ipVO.getSystem()) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't release system IP address with specified id"); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index b89de9e122f..af77ba5645f 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -2090,8 +2090,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(CreateVlanIpRangeCmd.class); cmdList.add(DeleteVlanIpRangeCmd.class); cmdList.add(ListVlanIpRangesCmd.class); - cmdList.add(DedicatePublicIpRangeCmd.class); - cmdList.add(ReleasePublicIpRangeCmd.class); cmdList.add(AssignVMCmd.class); cmdList.add(MigrateVMCmd.class); cmdList.add(RecoverVMCmd.class); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 417ec3f4523..52ca79d5a60 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -683,13 +683,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M accountCleanupNeeded = true; } - // release account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned + // delete account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned // up successfully if (networksDeleted) { - if (!_configMgr.releaseAccountSpecificVirtualRanges(accountId)) { + if (!_configMgr.deleteAccountSpecificVirtualRanges(accountId)) { accountCleanupNeeded = true; } else { - s_logger.debug("Account specific Virtual IP ranges " + " are successfully released as a part of account id=" + accountId + " cleanup."); + s_logger.debug("Account specific Virtual IP ranges " + " are successfully deleted as a part of account id=" + accountId + " cleanup."); } } diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java deleted file mode 100644 index 321f605a1cc..00000000000 --- a/server/test/com/cloud/configuration/ConfigurationManagerTest.java +++ /dev/null @@ -1,421 +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.configuration; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import junit.framework.Assert; - -import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; -import org.apache.log4j.Logger; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import com.cloud.configuration.Resource.ResourceType; -import com.cloud.dc.AccountVlanMapVO; -import com.cloud.dc.DataCenter.NetworkType; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.Vlan; -import com.cloud.dc.VlanVO; -import com.cloud.dc.dao.AccountVlanMapDao; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.dc.dao.VlanDao; -import com.cloud.network.NetworkManager; -import com.cloud.network.dao.FirewallRulesDao; -import com.cloud.network.dao.IPAddressDao; -import com.cloud.network.dao.IPAddressVO; -import com.cloud.projects.ProjectManager; -import com.cloud.user.Account; -import com.cloud.user.AccountManager; -import com.cloud.user.AccountVO; -import com.cloud.user.ResourceLimitService; -import com.cloud.user.UserContext; -import com.cloud.user.dao.AccountDao; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.net.Ip; - -public class ConfigurationManagerTest { - - private static final Logger s_logger = Logger.getLogger(ConfigurationManagerTest.class); - - ConfigurationManagerImpl configurationMgr = new ConfigurationManagerImpl(); - - DedicatePublicIpRangeCmd dedicatePublicIpRangesCmd = new DedicatePublicIpRangeCmdExtn(); - Class _dedicatePublicIpRangeClass = dedicatePublicIpRangesCmd.getClass().getSuperclass(); - - ReleasePublicIpRangeCmd releasePublicIpRangesCmd = new ReleasePublicIpRangeCmdExtn(); - Class _releasePublicIpRangeClass = releasePublicIpRangesCmd.getClass().getSuperclass(); - - @Mock AccountManager _accountMgr; - @Mock ProjectManager _projectMgr; - @Mock ResourceLimitService _resourceLimitMgr; - @Mock NetworkManager _networkMgr; - @Mock AccountDao _accountDao; - @Mock VlanDao _vlanDao; - @Mock AccountVlanMapDao _accountVlanMapDao; - @Mock IPAddressDao _publicIpAddressDao; - @Mock DataCenterDao _zoneDao; - @Mock FirewallRulesDao _firewallDao; - - VlanVO vlan = new VlanVO(); - - @Before - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - configurationMgr._accountMgr = _accountMgr; - configurationMgr._projectMgr = _projectMgr; - configurationMgr._resourceLimitMgr = _resourceLimitMgr; - configurationMgr._networkMgr = _networkMgr; - configurationMgr._accountDao = _accountDao; - configurationMgr._vlanDao = _vlanDao; - configurationMgr._accountVlanMapDao = _accountVlanMapDao; - configurationMgr._publicIpAddressDao = _publicIpAddressDao; - configurationMgr._zoneDao = _zoneDao; - configurationMgr._firewallDao = _firewallDao; - - Account account = (Account) new AccountVO("testaccount", 1, "networkdomain", (short) 0, UUID.randomUUID().toString()); - when(configurationMgr._accountMgr.getAccount(anyLong())).thenReturn(account); - when(configurationMgr._accountDao.findActiveAccount(anyString(), anyLong())).thenReturn(account); - when(configurationMgr._accountMgr.getActiveAccountById(anyLong())).thenReturn(account); - - when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); - - doNothing().when(configurationMgr._resourceLimitMgr).checkResourceLimit(any(Account.class), - any(ResourceType.class), anyLong()); - - when(configurationMgr._accountVlanMapDao.persist(any(AccountVlanMapVO.class))).thenReturn(new AccountVlanMapVO()); - - when(configurationMgr._vlanDao.acquireInLockTable(anyLong(), anyInt())).thenReturn(vlan); - - UserContext.registerContext(1, account, null, true); - - Field dedicateIdField = _dedicatePublicIpRangeClass.getDeclaredField("id"); - dedicateIdField.setAccessible(true); - dedicateIdField.set(dedicatePublicIpRangesCmd, 1L); - - Field accountNameField = _dedicatePublicIpRangeClass.getDeclaredField("accountName"); - accountNameField.setAccessible(true); - accountNameField.set(dedicatePublicIpRangesCmd, "accountname"); - - Field projectIdField = _dedicatePublicIpRangeClass.getDeclaredField("projectId"); - projectIdField.setAccessible(true); - projectIdField.set(dedicatePublicIpRangesCmd, null); - - Field domainIdField = _dedicatePublicIpRangeClass.getDeclaredField("domainId"); - domainIdField.setAccessible(true); - domainIdField.set(dedicatePublicIpRangesCmd, 1L); - - Field zoneIdField = _dedicatePublicIpRangeClass.getDeclaredField("zoneId"); - zoneIdField.setAccessible(true); - zoneIdField.set(dedicatePublicIpRangesCmd, 1L); - - Field releaseIdField = _releasePublicIpRangeClass.getDeclaredField("id"); - releaseIdField.setAccessible(true); - releaseIdField.set(releasePublicIpRangesCmd, 1L); - } - - @Test - public void testDedicatePublicIpRange() throws Exception { - - s_logger.info("Running tests for DedicatePublicIpRange API"); - - /* - * TEST 1: given valid parameters DedicatePublicIpRange should succeed - */ - runDedicatePublicIpRangePostiveTest(); - - /* - * TEST 2: given invalid public ip range DedicatePublicIpRange should fail - */ - runDedicatePublicIpRangeInvalidRange(); - /* - * TEST 3: given public IP range that is already dedicated to a different account DedicatePublicIpRange should fail - */ - runDedicatePublicIpRangeDedicatedRange(); - - /* - * TEST 4: given zone is of type Basic DedicatePublicIpRange should fail - */ - runDedicatePublicIpRangeInvalidZone(); - - /* - * TEST 5: given range is already allocated to a different account DedicatePublicIpRange should fail - */ - runDedicatePublicIpRangeIPAdressAllocated(); - } - - @Test - public void testReleasePublicIpRange() throws Exception { - - s_logger.info("Running tests for DedicatePublicIpRange API"); - - /* - * TEST 1: given valid parameters and no allocated public ip's in the range ReleasePublicIpRange should succeed - */ - runReleasePublicIpRangePostiveTest1(); - - /* - * TEST 2: given valid parameters ReleasePublicIpRange should succeed - */ - runReleasePublicIpRangePostiveTest2(); - - /* - * TEST 3: given range doesn't exist - */ - runReleasePublicIpRangeInvalidIpRange(); - - /* - * TEST 4: given range is not dedicated to any account - */ - runReleaseNonDedicatedPublicIpRange(); - } - - void runDedicatePublicIpRangePostiveTest() throws Exception { - Transaction txn = Transaction.open("runDedicatePublicIpRangePostiveTest"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); - - DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", - null, null, NetworkType.Advanced, null, null, true, true, null, null); - when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); - - List ipAddressList = new ArrayList(); - IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); - ipAddressList.add(ipAddress); - when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); - - try { - Vlan result = configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); - Assert.assertNotNull(result); - } catch (Exception e) { - s_logger.info("exception in testing runDedicatePublicIpRangePostiveTest message: " + e.toString()); - } finally { - txn.close("runDedicatePublicIpRangePostiveTest"); - } - } - - void runDedicatePublicIpRangeInvalidRange() throws Exception { - Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidRange"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); - try { - configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("Please specify a valid Public IP range id")); - } finally { - txn.close("runDedicatePublicIpRangeInvalidRange"); - } - } - - void runDedicatePublicIpRangeDedicatedRange() throws Exception { - Transaction txn = Transaction.open("runDedicatePublicIpRangeDedicatedRange"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - // public ip range is already dedicated - List accountVlanMaps = new ArrayList(); - AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); - accountVlanMaps.add(accountVlanMap); - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); - - DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", - null, null, NetworkType.Advanced, null, null, true, true, null, null); - when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); - - List ipAddressList = new ArrayList(); - IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); - ipAddressList.add(ipAddress); - when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); - - try { - configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("Public IP range has already been dedicated")); - } finally { - txn.close("runDedicatePublicIpRangePublicIpRangeDedicated"); - } - } - - void runDedicatePublicIpRangeInvalidZone() throws Exception { - Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidZone"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); - - // public ip range belongs to zone of type basic - DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", - null, null, NetworkType.Basic, null, null, true, true, null, null); - when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); - - List ipAddressList = new ArrayList(); - IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); - ipAddressList.add(ipAddress); - when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); - - try { - configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("Public IP range can be dedicated to an account only in the zone of type Advanced")); - } finally { - txn.close("runDedicatePublicIpRangeInvalidZone"); - } - } - - void runDedicatePublicIpRangeIPAdressAllocated() throws Exception { - Transaction txn = Transaction.open("runDedicatePublicIpRangeIPAdressAllocated"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); - - DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", - null, null, NetworkType.Advanced, null, null, true, true, null, null); - when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); - - // one of the ip addresses of the range is allocated to different account - List ipAddressList = new ArrayList(); - IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); - ipAddress.setAllocatedToAccountId(1L); - ipAddressList.add(ipAddress); - when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); - - try { - configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("Public IP address in range is already allocated to another account")); - } finally { - txn.close("runDedicatePublicIpRangeIPAdressAllocated"); - } - } - - void runReleasePublicIpRangePostiveTest1() throws Exception { - Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest1"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - List accountVlanMaps = new ArrayList(); - AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); - accountVlanMaps.add(accountVlanMap); - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); - - // no allocated ip's - when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(0); - - when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); - try { - Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); - Assert.assertTrue(result); - } catch (Exception e) { - s_logger.info("exception in testing runReleasePublicIpRangePostiveTest1 message: " + e.toString()); - } finally { - txn.close("runReleasePublicIpRangePostiveTest1"); - } - } - - void runReleasePublicIpRangePostiveTest2() throws Exception { - Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest2"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - List accountVlanMaps = new ArrayList(); - AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); - accountVlanMaps.add(accountVlanMap); - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); - - when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); - - List ipAddressList = new ArrayList(); - IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); - ipAddressList.add(ipAddress); - when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); - - when(configurationMgr._firewallDao.countRulesByIpId(anyLong())).thenReturn(0L); - - when(configurationMgr._networkMgr.disassociatePublicIpAddress(anyLong(), anyLong(), any(Account.class))).thenReturn(true); - - when(configurationMgr._vlanDao.releaseFromLockTable(anyLong())).thenReturn(true); - - when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); - try { - Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); - Assert.assertTrue(result); - } catch (Exception e) { - s_logger.info("exception in testing runReleasePublicIpRangePostiveTest2 message: " + e.toString()); - } finally { - txn.close("runReleasePublicIpRangePostiveTest2"); - } - } - - void runReleasePublicIpRangeInvalidIpRange() throws Exception { - Transaction txn = Transaction.open("runReleasePublicIpRangeInvalidIpRange"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); - try { - configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("Please specify a valid IP range id")); - } finally { - txn.close("runReleasePublicIpRangeInvalidIpRange"); - } - } - - void runReleaseNonDedicatedPublicIpRange() throws Exception { - Transaction txn = Transaction.open("runReleaseNonDedicatedPublicIpRange"); - - when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); - - when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); - try { - configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); - } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("as it not dedicated to any account")); - } finally { - txn.close("runReleaseNonDedicatedPublicIpRange"); - } - } - - - public class DedicatePublicIpRangeCmdExtn extends DedicatePublicIpRangeCmd { - public long getEntityOwnerId() { - return 1; - } - } - - public class ReleasePublicIpRangeCmdExtn extends ReleasePublicIpRangeCmd { - public long getEntityOwnerId() { - return 1; - } - } -} \ No newline at end of file diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index f781e61e671..d96e831cfeb 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -40,9 +40,7 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -546,7 +544,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu * @see com.cloud.configuration.ConfigurationManager#deleteAccountSpecificVirtualRanges(long) */ @Override - public boolean releaseAccountSpecificVirtualRanges(long accountId) { + public boolean deleteAccountSpecificVirtualRanges(long accountId) { // TODO Auto-generated method stub return false; } @@ -615,33 +613,5 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } - @Override - public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) - throws ResourceAllocationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean releasePublicIpRange(long userId, long vlanDbId, - Account caller) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Vlan dedicatePublicIpRange(Long vlanDbId, String accountName, - Long domainId, Long zoneId, Long projectId) - throws ResourceAllocationException { - // TODO Auto-generated method stub - return null; - } - } diff --git a/test/integration/component/test_public_ip_range.py b/test/integration/component/test_public_ip_range.py deleted file mode 100644 index 1cb2e48acd7..00000000000 --- a/test/integration/component/test_public_ip_range.py +++ /dev/null @@ -1,149 +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. -""" P1 tests for Dedicating Public IP addresses -""" -#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 * -import datetime - -class Services: - """Test Dedicating Public IP addresses - """ - - def __init__(self): - self.services = { - "domain": { - "name": "Domain", - }, - "account": { - "email": "test@test.com", - "firstname": "Test", - "lastname": "User", - "username": "test", - # Random characters are appended for unique - # username - "password": "password", - }, - "gateway": "10.102.196.1", - "netmask": "255.255.255.0", - "forvirtualnetwork": "true", - "startip": "10.102.196.70", - "endip": "10.102.196.73", - "zoneid": "1", - "podid": "", - "vlan": "101", - "sleep": 60, - "timeout": 10, - } - -class TesDedicatePublicIPRange(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - cls.api_client = super(TesDedicatePublicIPRange, cls).getClsTestClient().getApiClient() - cls.services = Services().services - # Get Zone, Domain - cls.domain = get_domain(cls.api_client, cls.services) - cls.zone = get_zone(cls.api_client, cls.services) - - # Create Account, VMs etc - cls.account = Account.create( - cls.api_client, - cls.services["account"], - domainid=cls.domain.id - ) - cls._cleanup = [ - #cls.account, - ] - 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.cleanup = [] - return - - def tearDown(self): - try: - # Clean up - cleanup_resources(self.apiclient, self.cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - @attr(tags = ["publiciprange", "dedicate"]) - def test_createPublicIpRange(self): - """Test create public IP range - """ - - - # Validate the following: - # 1. Create public IP range. - # 2. Created IP range should be present, verify with listVlanIpRanges - - self.debug("Creating Public IP range") - self.public_ip_range = PublicIpRange.create( - self.api_client, - self.services - ) - list_public_ip_range_response = PublicIpRange.list( - self.apiclient, - id=self.public_ip_range.vlan.id - ) - self.debug( - "Verify listPublicIpRanges response for public ip ranges: %s" \ - % self.public_ip_range.vlan.id - ) - self.assertEqual( - isinstance(list_public_ip_range_response, list), - True, - "Check for list Public IP range response" - ) - public_ip_response = list_public_ip_range_response[0] - self.assertEqual( - public_ip_response.id, - self.public_ip_range.vlan.id, - "Check public ip range response id is in listVlanIpRanges" - ) - self.debug("Dedicating Public IP range"); - self.debug("Vlan id %s" % self.public_ip_range.vlan.id); - self.debug("Zone id %s" % self.zone.id); - self.debug("Account name %s" % self.account.account.name); - self.debug("Domain id %s" % self.account.account.domainid); - dedicate_public_ip_range_response = PublicIpRange.dedicate( - self.apiclient, - self.public_ip_range.vlan.id, - zoneid=self.zone.id, - account=self.account.account.name, - domainid=self.account.account.domainid - ) - return diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 0e117bbdfc2..f3370eb3190 100644 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -1882,17 +1882,6 @@ class PublicIpRange: [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listVlanIpRanges(cmd)) - @classmethod - def dedicate(cls, apiclient, id, zoneid, account=None, domainid=None, projectid=None): - """Dedicate VLAN IP range""" - - cmd = dedicatePublicIpRange.dedicatePublicIpRangeCmd() - cmd.id = id - cmd.account = account - cmd.domainid = domainid - cmd.projectid = projectid - cmd.zoneid = zoneid - return PublicIpRange(apiclient.dedicatePublicIpRange(cmd).__dict__) class SecondaryStorage: """Manage Secondary storage""" From c6e226da74e53259c632bce687b12ce05f8408ca Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 8 Apr 2013 17:29:54 -0700 Subject: [PATCH 17/81] CLOUDSTACK-524: in some cases (especially the built-in CentOS template), the template downloader does not use the configured http proxy The DownloadProgress command is used to restart failed or stuck download jobs -- and it would not include the proxy information, unlike the DownloadCommand which always did Signed-off-by: Chiradeep Vittal --- .../src/com/cloud/storage/download/DownloadMonitorImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index ee8df745fc7..cfb92d5ccde 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -363,10 +363,10 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor start(); DownloadCommand dcmd = new DownloadCommand(secUrl, template, maxTemplateSizeInBytes); - dcmd.setProxy(getHttpProxy()); if (downloadJobExists) { dcmd = new DownloadProgressCommand(dcmd, vmTemplateHost.getJobId(), RequestType.GET_OR_RESTART); } + dcmd.setProxy(getHttpProxy()); if (vmTemplateHost.isCopy()) { dcmd.setCreds(TemplateConstants.DEFAULT_HTTP_AUTH_USER, _copyAuthPasswd); } @@ -452,12 +452,11 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor if(volumeHost != null) { start(); DownloadCommand dcmd = new DownloadCommand(secUrl, volume, maxVolumeSizeInBytes, checkSum, url, format); - dcmd.setProxy(getHttpProxy()); if (downloadJobExists) { dcmd = new DownloadProgressCommand(dcmd, volumeHost.getJobId(), RequestType.GET_OR_RESTART); dcmd.setResourceType(ResourceType.VOLUME); } - + dcmd.setProxy(getHttpProxy()); HostVO ssvm = _ssvmMgr.pickSsvmHost(sserver); if( ssvm == null ) { s_logger.warn("There is no secondary storage VM for secondary storage host " + sserver.getName()); From 301c683c099a568412f2db2ad24a9bb8441951ca Mon Sep 17 00:00:00 2001 From: David Grizzanti Date: Mon, 8 Apr 2013 16:53:11 -0400 Subject: [PATCH 18/81] CLOUDSTACK-1598: update global-config.xml doc to include new section "About Global Configuration Settings" Signed-off-by: Joe Brockmeier --- docs/en-US/global-config.xml | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/docs/en-US/global-config.xml b/docs/en-US/global-config.xml index 2f6ad105c0d..11952c382ac 100644 --- a/docs/en-US/global-config.xml +++ b/docs/en-US/global-config.xml @@ -19,6 +19,8 @@ under the License. --> + Global Configuration Parameters +
Setting Global Configuration Parameters &PRODUCT; provides parameters that you can set to control many aspects of the cloud. When &PRODUCT; is first installed, and periodically thereafter, you might need to modify these @@ -51,4 +53,81 @@ must click the name of the hypervisor first to display the editing screen. +
+
+ About Global Configuration Parameters + &PRODUCT; provides a variety of settings you can use to set limits, configure features, + and enable or disable features in the cloud. Once your Management Server is running, you might + need to set some of these global configuration parameters, depending on what optional features + you are setting up. + To modify global configuration parameters, use the steps in "Setting Global Configuration + Parameters." + The documentation for each &PRODUCT; feature should direct you to the names of the applicable + parameters. Many of them are discussed in the &PRODUCT; Administration Guide. The following table + shows a few of the more useful parameters. + + + + + + + Field + Value + + + + + management.network.cidr + A CIDR that describes the network that the management CIDRs reside on. This + variable must be set for deployments that use vSphere. It is recommended to be set for + other deployments as well. Example: 192.168.3.0/24. + + + xen.setup.multipath + For XenServer nodes, this is a true/false variable that instructs CloudStack to + enable iSCSI multipath on the XenServer Hosts when they are added. This defaults to false. + Set it to true if you would like CloudStack to enable multipath. + If this is true for a NFS-based deployment multipath will still be enabled on the + XenServer host. However, this does not impact NFS operation and is harmless. + + + secstorage.allowed.internal.sites + This is used to protect your internal network from rogue attempts to download + arbitrary files using the template download feature. This is a comma-separated list of CIDRs. + If a requested URL matches any of these CIDRs the Secondary Storage VM will use the private + network interface to fetch the URL. Other URLs will go through the public interface. + We suggest you set this to 1 or 2 hardened internal machines where you keep your templates. + For example, set it to 192.168.1.66/32. + + + use.local.storage + Determines whether CloudStack will use storage that is local to the Host for data + disks, templates, and snapshots. By default CloudStack will not use this storage. You should + change this to true if you want to use local storage and you understand the reliability and + feature drawbacks to choosing local storage. + + + host + This is the IP address of the Management Server. If you are using multiple + Management Servers you should enter a load balanced IP address that is reachable via + the private network. + + + default.page.size + Maximum number of items per page that can be returned by a CloudStack API command. + The limit applies at the cloud level and can vary from cloud to cloud. You can override this + with a lower value on a particular API call by using the page and pagesize API command parameters. + For more information, see the Developer's Guide. Default: 500. + + + ha.tag + The label you want to use throughout the cloud to designate certain hosts as dedicated + HA hosts. These hosts will be used only for HA-enabled VMs that are restarting due to the failure + of another host. For example, you could set this to ha_host. Specify the ha.tag value as a host tag + when you add a new host to the cloud. + + + + +
From c522841716e77066f387410ca42d0e8132f96459 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 1 Apr 2013 14:56:58 -0400 Subject: [PATCH 19/81] CLOUDSTACK-1814: Add LXC documentation Signed-off-by: Joe Brockmeier --- .../hypervisor-host-install-firewall.xml | 4 +- .../en-US/hypervisor-host-install-network.xml | 8 +- docs/en-US/hypervisor-installation.xml | 1 + docs/en-US/lxc-install.xml | 110 ++++++++++++++++++ docs/en-US/lxc-topology-req.xml | 24 ++++ .../management-server-install-systemvm.xml | 4 + docs/en-US/minimum-system-requirements.xml | 1 + docs/en-US/prepare-system-vm-template.xml | 4 + docs/en-US/topology-req.xml | 3 +- 9 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 docs/en-US/lxc-install.xml create mode 100644 docs/en-US/lxc-topology-req.xml diff --git a/docs/en-US/hypervisor-host-install-firewall.xml b/docs/en-US/hypervisor-host-install-firewall.xml index ae82fc47afa..c6658731819 100644 --- a/docs/en-US/hypervisor-host-install-firewall.xml +++ b/docs/en-US/hypervisor-host-install-firewall.xml @@ -34,7 +34,7 @@ 49152 - 49216 (libvirt live migration) It depends on the firewall you are using how to open these ports. Below you'll find examples how to open these ports in RHEL/CentOS and Ubuntu. -
+
Open ports in RHEL/CentOS RHEL and CentOS use iptables for firewalling the system, you can open extra ports by executing the following iptable commands: $ iptables -I INPUT -p tcp -m tcp --dport 22 -j ACCEPT @@ -45,7 +45,7 @@ These iptable settings are not persistent accross reboots, we have to save them first. $ iptables-save > /etc/sysconfig/iptables
-
+
Open ports in Ubuntu The default firewall under Ubuntu is UFW (Uncomplicated FireWall), which is a Python wrapper around iptables. To open the required ports, execute the following commands: diff --git a/docs/en-US/hypervisor-host-install-network.xml b/docs/en-US/hypervisor-host-install-network.xml index 3a6dfac48bd..54cdc27c450 100644 --- a/docs/en-US/hypervisor-host-install-network.xml +++ b/docs/en-US/hypervisor-host-install-network.xml @@ -29,7 +29,7 @@ In order to forward traffic to your instances you will need at least two bridges: public and private. By default these bridges are called cloudbr0 and cloudbr1, but you do have to make sure they are available on each hypervisor. The most important factor is that you keep the configuration consistent on all your hypervisors. -
+
Network example There are many ways to configure your network. In the Basic networking mode you should have two (V)LAN's, one for your private network and one for the public network. We assume that the hypervisor has one NIC (eth0) with three tagged VLAN's: @@ -41,11 +41,11 @@ On VLAN 100 we give the Hypervisor the IP-Address 192.168.42.11/24 with the gateway 192.168.42.1 The Hypervisor and Management server don't have to be in the same subnet!
-
+
Configuring the network bridges It depends on the distribution you are using how to configure these, below you'll find examples for RHEL/CentOS and Ubuntu. The goal is to have two bridges called 'cloudbr0' and 'cloudbr1' after this section. This should be used as a guideline only. The exact configuration will depend on your network layout. -
+
Configure in RHEL or CentOS The required packages were installed when libvirt was installed, we can proceed to configuring the network. First we configure eth0 @@ -111,7 +111,7 @@ STP=yes]]> With this configuration you should be able to restart the network, although a reboot is recommended to see if everything works properly. Make sure you have an alternative way like IPMI or ILO to reach the machine in case you made a configuration error and the network stops functioning!
-
+
Configure in Ubuntu All the required packages were installed when you installed libvirt, so we only have to configure the network. vi /etc/network/interfaces diff --git a/docs/en-US/hypervisor-installation.xml b/docs/en-US/hypervisor-installation.xml index b0fc9f46ddb..5ee7dea696a 100644 --- a/docs/en-US/hypervisor-installation.xml +++ b/docs/en-US/hypervisor-installation.xml @@ -28,4 +28,5 @@ + diff --git a/docs/en-US/lxc-install.xml b/docs/en-US/lxc-install.xml new file mode 100644 index 00000000000..a80c18afdd6 --- /dev/null +++ b/docs/en-US/lxc-install.xml @@ -0,0 +1,110 @@ + + + %BOOK_ENTITIES; + ]> + + + +
+ LXC Installation and Configuration +
+ System Requirements for LXC Hosts + LXC requires the Linux kernel cgroups functionality which is available starting 2.6.24. Although you are not required to run these distributions, the following are recommended: + + CentOS / RHEL: 6.3 + Ubuntu: 12.04(.1) + + The main requirement for LXC hypervisors is the libvirt and Qemu version. No matter what + Linux distribution you are using, make sure the following requirements are met: + + libvirt: 1.0.0 or higher + Qemu/KVM: 1.0 or higher + + The default bridge in &PRODUCT; is the Linux native bridge implementation (bridge module). &PRODUCT; includes an option to work with OpenVswitch, the requirements are listed below + + libvirt: 1.0.0 or higher + openvswitch: 1.7.1 or higher + + In addition, the following hardware requirements apply: + + Within a single cluster, the hosts must be of the same distribution version. + All hosts within a cluster must be homogenous. The CPUs must be of the same type, count, and feature flags. + Must support HVM (Intel-VT or AMD-V enabled) + 64-bit x86 CPU (more cores results in better performance) + 4 GB of memory + At least 1 NIC + When you deploy &PRODUCT;, the hypervisor host must not have any VMs already running + +
+
+ LXC Installation Overview + LXC does not have any native system VMs, instead KVM will be used to run system VMs. This means that your host will need to support both LXC and KVM, thus most of the installation and configuration will be identical to the KVM installation. The material in this section doesn't duplicate KVM installation docs. It provides the &PRODUCT;-specific steps that are needed to prepare a KVM host to work with &PRODUCT;. + Before continuing, make sure that you have applied the latest updates to your host. + It is NOT recommended to run services on this host not controlled by &PRODUCT;. + The procedure for installing an LXC Host is: + + Prepare the Operating System + Install and configure libvirt + Configure Security Policies (AppArmor and SELinux) + Install and configure the Agent + +
+
+ +
+
+ Install and configure the Agent + To manage LXC instances on the host &PRODUCT; uses a Agent. This Agent communicates with the Management server and controls all the instances on the host. + First we start by installing the agent: + In RHEL or CentOS: + $ yum install cloud-agent + In Ubuntu: + $ apt-get install cloud-agent + Next step is to update the Agent configuration setttings. The settings are in /etc/cloudstack/agent/agent.properties + + + Set the Agent to run in LXC mode: + hypervisor.type=lxc + + + Optional: If you would like to use direct networking (instead of the default bridge networking), configure these lines: + libvirt.vif.driver=com.cloud.hypervisor.kvm.resource.DirectVifDriver + network.direct.source.mode=private + network.direct.device=eth0 + + + The host is now ready to be added to a cluster. This is covered in a later section, see . It is recommended that you continue to read the documentation before adding the host! +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
diff --git a/docs/en-US/lxc-topology-req.xml b/docs/en-US/lxc-topology-req.xml new file mode 100644 index 00000000000..315863dd34c --- /dev/null +++ b/docs/en-US/lxc-topology-req.xml @@ -0,0 +1,24 @@ + + +%BOOK_ENTITIES; +]> + +
+ LXC Topology Requirements + The Management Servers communicate with LXC hosts on port 22 (ssh). +
diff --git a/docs/en-US/management-server-install-systemvm.xml b/docs/en-US/management-server-install-systemvm.xml index 8dc73deb992..928b95618fa 100644 --- a/docs/en-US/management-server-install-systemvm.xml +++ b/docs/en-US/management-server-install-systemvm.xml @@ -53,6 +53,10 @@ For KVM: # /usr/lib64/cloud/common/scripts/storage/secondary/cloud-install-sys-tmplt -m /mnt/secondary -u http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 -h kvm -s <optional-management-server-secret-key> -F + + For LXC: + # /usr/lib64/cloud/common/scripts/storage/secondary/cloud-install-sys-tmplt -m /mnt/secondary -u http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 -h lxc -s <optional-management-server-secret-key> -F + On Ubuntu, use the following path instead: # /usr/lib/cloud/common/scripts/storage/secondary/cloud-install-sys-tmplt diff --git a/docs/en-US/minimum-system-requirements.xml b/docs/en-US/minimum-system-requirements.xml index 0e497dd33f1..de1bc222023 100644 --- a/docs/en-US/minimum-system-requirements.xml +++ b/docs/en-US/minimum-system-requirements.xml @@ -68,6 +68,7 @@ +
diff --git a/docs/en-US/prepare-system-vm-template.xml b/docs/en-US/prepare-system-vm-template.xml index b53a509b4a1..35cc7e979bc 100644 --- a/docs/en-US/prepare-system-vm-template.xml +++ b/docs/en-US/prepare-system-vm-template.xml @@ -60,6 +60,10 @@ For KVM: # /usr/lib64/cloud/common/scripts/storage/secondary/cloud-install-sys-tmplt -m /mnt/secondary -u http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 -h kvm -s <optional-management-server-secret-key> -F + + For LXC: + # /usr/lib64/cloud/common/scripts/storage/secondary/cloud-install-sys-tmplt -m /mnt/secondary -u http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2 -h lxc -s <optional-management-server-secret-key> -F + diff --git a/docs/en-US/topology-req.xml b/docs/en-US/topology-req.xml index 65c9c2ea5c6..75fe69b41a4 100644 --- a/docs/en-US/topology-req.xml +++ b/docs/en-US/topology-req.xml @@ -28,4 +28,5 @@ -
+ +
From 8a435f5b43bb013b73875ba4b8cf4a0b537036d4 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 9 Apr 2013 10:49:28 +0530 Subject: [PATCH 20/81] reverting to submit as patch on review board --- docs/en-US/Book_Info.xml | 39 +- docs/en-US/Installation_Guide.xml | 2 + docs/en-US/add-clusters-vsphere.xml | 103 +--- docs/en-US/images/add-cluster.png | Bin 35697 -> 46302 bytes docs/en-US/images/dvswitch-config.png | Bin 41955 -> 0 bytes docs/en-US/images/dvswitchconfig.png | Bin 38642 -> 0 bytes docs/en-US/vmware-cluster-config-dvswitch.xml | 192 ------- docs/en-US/vmware-install.xml | 515 ++++++------------ 8 files changed, 220 insertions(+), 631 deletions(-) delete mode 100644 docs/en-US/images/dvswitch-config.png delete mode 100644 docs/en-US/images/dvswitchconfig.png delete mode 100644 docs/en-US/vmware-cluster-config-dvswitch.xml diff --git a/docs/en-US/Book_Info.xml b/docs/en-US/Book_Info.xml index be8bcdd7544..c125ab8de2b 100644 --- a/docs/en-US/Book_Info.xml +++ b/docs/en-US/Book_Info.xml @@ -22,23 +22,26 @@ specific language governing permissions and limitations under the License. --> + - &PRODUCT; Guide - Revised August 9, 2012 10:48 pm Pacific - Apache CloudStack - 4.0.0 - 1 - - - Complete technical documentation of &PRODUCT;. - - - - - - - - - - + &PRODUCT; Guide + Revised August 9, 2012 10:48 pm Pacific + Apache CloudStack + 4.0.0-incubating + 1 + + + + Complete technical documentation of &PRODUCT;. + + + + + + + + + + + diff --git a/docs/en-US/Installation_Guide.xml b/docs/en-US/Installation_Guide.xml index 28df071d42f..6ce5527e86c 100644 --- a/docs/en-US/Installation_Guide.xml +++ b/docs/en-US/Installation_Guide.xml @@ -11,7 +11,9 @@ 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 diff --git a/docs/en-US/add-clusters-vsphere.xml b/docs/en-US/add-clusters-vsphere.xml index ca36ee108c4..6b2dff2a595 100644 --- a/docs/en-US/add-clusters-vsphere.xml +++ b/docs/en-US/add-clusters-vsphere.xml @@ -73,105 +73,36 @@ Provide the following information in the dialog. The fields below make reference to values from vCenter. - - - - - - addcluster.png: add a cluster - - - There might be a slight delay while the cluster is provisioned. It will automatically - display in the UI. - Cluster Name: Enter the name of the cluster you - created in vCenter. For example, "cloud.cluster.2.2.1" + Cluster Name. Enter the name of the cluster you created in vCenter. For example, + "cloud.cluster.2.2.1" - vCenter Username: Enter the username that &PRODUCT; - should use to connect to vCenter. This user must have all the administrative - privileges. + vCenter Host. Enter the hostname or IP address of the vCenter server. - CPU overcommit ratio: Enter the CPU overcommit - ratio for the cluster. The value you enter determines the CPU consumption of each VM in - the selected cluster. By increasing the over-provisioning ratio, more resource capacity - will be used. If no value is specified, the value is defaulted to 1, which implies no - over-provisioning is done. + vCenter Username. Enter the username that &PRODUCT; should use to connect to + vCenter. This user must have all administrative privileges. - RAM overcommit ratio: Enter the RAM overcommit - ratio for the cluster. The value you enter determines the memory consumption of each VM - in the selected cluster. By increasing the over-provisioning ratio, more resource - capacity will be used. If no value is specified, the value is defaulted to 1, which - implies no over-provisioning is done. + vCenter Password. Enter the password for the user named above - vCenter Host: Enter the hostname or IP address of - the vCenter server. - - - vCenter Password: Enter the password for the user - named above. - - - vCenter Datacenter: Enter the vCenter datacenter - that the cluster is in. For example, "cloud.dc.VM". - - - Override Public Traffic: Enable this option to - override the zone-wide public traffic for the cluster you are creating. - - - Public Traffic vSwitch Type: This option is - displayed only if you enable the Override Public Traffic option. Select a desirable - switch. If the vmware.use.dvswitch global parameter is true, the default option will be - VMware vNetwork Distributed Virtual Switch. - If you have enabled Nexus dvSwitch in the environment, the following parameters for - dvSwitch configuration are displayed: - - - Nexus dvSwitch IP Address: The IP address of the Nexus VSM appliance. - - - Nexus dvSwitch Username: The username required to access the Nexus VSM - applicance. - - - Nexus dvSwitch Password: The password associated with the username specified - above. - - - - - Override Guest Traffic: Enable this option to - override the zone-wide guest traffic for the cluster you are creating. - - - Guest Traffic vSwitch Type: This option is - displayed only if you enable the Override Guest Traffic option. Select a desirable - switch. If the vmware.use.dvswitch global parameter is true, the default option will be - VMware vNetwork Distributed Virtual Switch. - If you have enabled Nexus dvSwitch in the environment, the following parameters for - dvSwitch configuration are displayed: - - - Nexus dvSwitch IP Address: The IP address of the Nexus VSM appliance. - - - Nexus dvSwitch Username: The username required to access the Nexus VSM - applicance. - - - Nexus dvSwitch Password: The password associated with the username specified - above. - - + vCenter Datacenter. Enter the vCenter datacenter that the cluster is in. For + example, "cloud.dc.VM". + + + + + + addcluster.png: add cluster + + There might be a slight delay while the cluster is provisioned. It will - automatically display in the UI. + automatically display in the UI diff --git a/docs/en-US/images/add-cluster.png b/docs/en-US/images/add-cluster.png index 4b24ec721d8ef25968245184a30bc5a0eef5d62c..383f375ebedd62d9b294a56f777ed4b8c0d92e10 100644 GIT binary patch literal 46302 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 literal 35697 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 B26g}d diff --git a/docs/en-US/images/dvswitchconfig.png b/docs/en-US/images/dvswitchconfig.png deleted file mode 100644 index 55b1ef7daf38aeb023fb22b42aa5060d2e8a9a95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38642 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 O00K`}KbLh*2~7Z_Kb=+p diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml deleted file mode 100644 index 3e1e37c0941..00000000000 --- a/docs/en-US/vmware-cluster-config-dvswitch.xml +++ /dev/null @@ -1,192 +0,0 @@ - - -%BOOK_ENTITIES; -]> - - -
- Configuring a vSphere Cluster with VMware Distributed Virtual Switch - &PRODUCT;supports VMware vNetwork Distributed Switch (VDS) for virtual network configuration - in a VMware vSphere environment. This section helps you configure VMware VDS in a &PRODUCT; - deployment. Each vCenter server instance can support up to 128 VDS instances and each VDS - instance can manage up to 500 VMware hosts. -
- About VMware Distributed Virtual Switch - VMware VDS is an aggregation of host-level virtual switches on a VMware vCenter server. - VDS abstracts the configuration of individual virtual switches that span across a large number - of hosts, and enables centralized provisioning, administration, and monitoring for your entire - datacenter from a centralized interface. In effect, a VDS acts as a single virtual switch at - the datacenter level and manages networking for a number of hosts in a datacenter from a - centralized VMware vCenter server. Each VDS maintains network runtime state for VMs as they - move across multiple hosts, enabling inline monitoring and centralized firewall services. A - VDS can be deployed with or without Virtual Standard Switch and a Nexus 1000V virtual - switch. -
-
- Prerequisites and Guidelines - - - Do not attempt to configure VDS by altering VMware traffic label when configuring - physical networks. This will only work for Standard Virtual Switch and should not be - distributed. - - - VMware VDS does not support multiple VDS per traffic type. If a user has many VDS - switches, only one can be used for Guest traffic and one for Public traffic. - - - Management and Storage network does not support VDS and use Standard Switch for these - networks. - - -
-
- Enabling Virtual Distributed Switch in &PRODUCT; - To make a &PRODUCT; deployment VDS enabled, set the vmware.use.dvswitch parameter to true - by using the Global Settings page in the &PRODUCT; UI and restart the Management Server. - Unless you enable the vmware.use.dvswitch parameter, you cannot see any UI options specific to - VDS, and &PRODUCT; ignores the VDS-specific parameters given in the AddClusterCmd API call. - Additionally, &PRODUCT; uses VDS for virtual network infrastructure if the value of - vmware.use.dvswitch parameter is true and the value of vmware.use.nexus.dvswitch parameter is - false. - &PRODUCT; supports configuring virtual networks in a deployment with a mix of Virtual - Distributed Switch, Standard Virtual Switch and Nexus 1000v Virtual Switch. -
-
- Configuring Distributed Virtual Switch in &PRODUCT; - You can configure VDS by adding the necessary resources while a zone is created. - - - - - - dvSwitchConfig.png: Configuring dvSwitch - - - Alternatively, you can create an additional cluster with VDS enabled in the existing zone. - Use the Add Cluster option. For information as given in . - In both these cases, you must specify the following parameters to configure VDS: - - - - - - - Parameters - Description - - - - - Cluster Name - Enter the name of the cluster you created in vCenter. For example, - "cloud.cluster". - - - vCenter Host - Enter the host name or the IP address of the vCenter host where you have - deployed the Nexus virtual switch. - - - vCenter User name - Enter the username that &PRODUCT; should use to connect to vCenter. This - user must have all administrative privileges. - - - vCenter Password - Enter the password for the user named above. - - - vCenter Datacenter - Enter the vCenter datacenter that the cluster is in. For example, - "cloud.dc.VM". - - - Override Public Traffic - Enable this option to override the zone-wide public traffic for the cluster - you are creating. - - - Public Traffic vSwitch Type - This option is displayed only if you enable the Override Public Traffic - option. Select VMware vNetwork Distributed Virtual Switch. If the - vmware.use.dvswitch global parameter is true, the default option will be VMware - vNetwork Distributed Virtual Switch. - - - Public Traffic vSwitch Name - Specify a name to identify the switch. - - - Override Guest Traffic - Enable the option to override the zone-wide guest traffic for the cluster - you are creating. - - - Guest Traffic vSwitch Type - This option is displayed only if you enable the Override Guest Traffic - option. Select VMware vNetwork Distributed Virtual Switch. If the - vmware.use.dvswitch global parameter is true, the default option will be VMware - vNetwork Distributed Virtual Switch. - - - Guest Traffic vSwitch Name - Specify a name to identify the switch. - - - - -
-
- Removing Nexus Virtual Switch - - - In the vCenter datacenter that is served by the VMware dvSwitch, ensure that you - delete all the hosts in the corresponding cluster. - - - Log in with Admin permissions to the &PRODUCT; administrator UI. - - - In the left navigation bar, select Infrastructure. - - - In the Infrastructure page, click View all under Clusters. - - - Select the cluster where you want to remove the virtual switch. - - - In the VMware dvSwitch tab, click the name of the virtual switch. - - - In the Details page, click Delete VMware dvSwitch icon. - - - - - DeleteButton.png: button to delete dvSwitch - - - - Click Yes in the confirmation dialog box. - - -
-
diff --git a/docs/en-US/vmware-install.xml b/docs/en-US/vmware-install.xml index 81c9a49b038..467e1358638 100644 --- a/docs/en-US/vmware-install.xml +++ b/docs/en-US/vmware-install.xml @@ -1,5 +1,5 @@ - %BOOK_ENTITIES; ]> @@ -11,7 +11,9 @@ 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 @@ -325,439 +327,282 @@ esxcfg-firewall -o 59000-60000,tcp,out,vncextras guide.
-
+
Storage Preparation for vSphere (iSCSI only) Use of iSCSI requires preparatory work in vCenter. You must add an iSCSI target and create From c51587396adc1e179eec8a84569d2d7221380d00 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 9 Apr 2013 13:51:33 +0530 Subject: [PATCH 21/81] CLOUDSTACK-1893:[AWS Style Health Checks] UI hangs when one tries to create health check policy on a LB rule with VR as LB service provider --- ui/scripts/ui-custom/healthCheck.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/scripts/ui-custom/healthCheck.js b/ui/scripts/ui-custom/healthCheck.js index ebb7e5a8903..4b42fa7724c 100644 --- a/ui/scripts/ui-custom/healthCheck.js +++ b/ui/scripts/ui-custom/healthCheck.js @@ -164,7 +164,15 @@ } }); }, g_queryAsyncJobResultInterval); - } + }, + + error:function(json){ + + cloudStack.dialog.notice({message: _s(json.responseText)}); //Error message in the API needs to be improved + $healthCheckDialog.dialog('close'); + $('.overlay').remove(); + } + }); } } From 47dc989675343a9dedc59edc64bb57e6977f49e8 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 9 Apr 2013 17:02:01 +0530 Subject: [PATCH 22/81] CLOUDSTACK-1428:[UI] Instance which are created without display name are not visible when added to LB: Using the name property instead in the data flow --- ui/scripts/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 65be302c0c9..cc1aad34927 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -2943,7 +2943,7 @@ }); $.extend(item, { - _itemName: 'displayname', + _itemName: 'name', _itemData: lbInstances, _maxLength: { name: 7 From 9180bd599015ce248e894a0ef476a4604b1533e4 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 9 Apr 2013 17:45:19 +0530 Subject: [PATCH 23/81] CLOUDSTACK-1834: Events are not generated for registerUserKeys() --- api/src/com/cloud/event/EventTypes.java | 3 +++ server/src/com/cloud/user/AccountManagerImpl.java | 1 + 2 files changed, 4 insertions(+) diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 704a1bfc02c..5671f995c70 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -142,6 +142,9 @@ public class EventTypes { //registering SSH keypair events public static final String EVENT_REGISTER_SSH_KEYPAIR = "REGISTER.SSH.KEYPAIR"; + //register for user API and secret keys + public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY"; + // Template Events public static final String EVENT_TEMPLATE_CREATE = "TEMPLATE.CREATE"; public static final String EVENT_TEMPLATE_DELETE = "TEMPLATE.DELETE"; diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 52ca79d5a60..9736aa122e6 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -1984,6 +1984,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } @Override @DB + @ActionEvent(eventType = EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY, eventDescription = "register for the developer API keys") public String[] createApiKeyAndSecretKey(RegisterCmd cmd) { Long userId = cmd.getId(); From 120d834186c750e53d0fe55fc0c039b88b64626c Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 9 Apr 2013 19:19:34 +0530 Subject: [PATCH 24/81] Fixing the regression introduced by spring refactor to stream line oss and non oss components. sincle the GSLB service impl has different configuration, moving it out the bean definition from applicationContext to ComponentContext and non-ossComponentContext --- client/tomcatconf/applicationContext.xml.in | 1 - client/tomcatconf/nonossComponentContext.xml.in | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 0b4ba82f630..3b51a83e6ad 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -733,7 +733,6 @@ - diff --git a/client/tomcatconf/nonossComponentContext.xml.in b/client/tomcatconf/nonossComponentContext.xml.in index 187446c44f3..fc8a9cd5409 100644 --- a/client/tomcatconf/nonossComponentContext.xml.in +++ b/client/tomcatconf/nonossComponentContext.xml.in @@ -43,7 +43,12 @@ --> - + + + + + + From ef2983cc12e909696be1c0c390c14d22cd4bebe1 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 9 Apr 2013 19:23:13 +0530 Subject: [PATCH 25/81] region id is not Long, its integer changing the regionId parameter in ListGlobalLoadBalancerRuleCmd.java --- .../user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java index dae861a35b1..d75de57f7d1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/ListGlobalLoadBalancerRuleCmd.java @@ -45,7 +45,7 @@ public class ListGlobalLoadBalancerRuleCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GlobalLoadBalancerResponse.class, description = "the ID of the global load balancer rule") private Long id; - @Parameter(name = ApiConstants.REGION_ID, type = CommandType.UUID, entityType = RegionResponse.class, description = "region ID") + @Parameter(name = ApiConstants.REGION_ID, type = CommandType.INTEGER, entityType = RegionResponse.class, description = "region ID") private Integer regionId; // /////////////////////////////////////////////////// From d5d167cb97b95f5622c0e34fe4546642484016f6 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Tue, 9 Apr 2013 16:52:15 +0200 Subject: [PATCH 26/81] CLOUDSTACK-1980: Add cloudstack-sysvmadm and cloudstack-setup-encryption To the management server package. cloudstack-setup-bridge should be a part of the cloudstack-awsapi package --- debian/cloudstack-management.install | 2 ++ debian/rules | 2 ++ 2 files changed, 4 insertions(+) diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index ed7744b2afb..5a682d45862 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -29,4 +29,6 @@ /usr/bin/cloudstack-setup-management /usr/bin/cloudstack-setup-databases /usr/bin/cloudstack-migrate-databases +/usr/bin/cloudstack-setup-encryption +/usr/bin/cloudstack-sysvmadm /usr/share/cloudstack-management/* diff --git a/debian/rules b/debian/rules index c50214a30a4..4e55c71048c 100755 --- a/debian/rules +++ b/debian/rules @@ -126,6 +126,8 @@ install: install -D client/target/utilities/bin/cloud-set-guest-sshkey $(DESTDIR)/usr/bin/cloudstack-set-guest-sshkey install -D client/target/utilities/bin/cloud-setup-databases $(DESTDIR)/usr/bin/cloudstack-setup-databases install -D client/target/utilities/bin/cloud-setup-management $(DESTDIR)/usr/bin/cloudstack-setup-management + install -D client/target/utilities/bin/cloud-setup-encryption $(DESTDIR)/usr/bin/cloudstack-setup-encryption + install -D client/target/utilities/bin/cloud-sysvmadm $(DESTDIR)/usr/bin/cloudstack-sysvmadm install -D services/console-proxy/server/dist/systemvm.iso $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/systemvm.iso # We need jasypt for cloud-install-sys-tmplt, so this is a nasty hack to get it into the right place install -D agent/target/dependencies/jasypt-1.9.0.jar $(DESTDIR)/usr/share/$(PACKAGE)-common/lib From 6c9fcb8c8f6739c9bad93fea8c28be3a768ca694 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Tue, 29 Jan 2013 08:29:25 +0100 Subject: [PATCH 27/81] Make the VpcManager check a list of supported providers --- server/src/com/cloud/network/vpc/VpcManagerImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 3948f2ecf73..dbd36ae0cf7 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -179,6 +179,7 @@ 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); int _cleanupInterval; int _maxNetworks; @@ -1054,9 +1055,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis //1) in current release, only vpc provider is supported by Vpc offering List providers = _ntwkModel.getNtwkOffDistinctProviders(guestNtwkOff.getId()); for (Provider provider : providers) { - if (provider != Provider.VPCVirtualRouter) { - throw new InvalidParameterValueException("Only provider of type " + Provider.VPCVirtualRouter.getName() - + " is supported for network offering that can be used in VPC"); + if (!supportedProviders.contains(provider) ) { + throw new InvalidParameterValueException("Provider of type " + provider.getName() + + " is not supported for network offerings that can be used in VPC"); } } From 57fea8f90cda8aa39363d519e5d35d87ab82072e Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Tue, 9 Apr 2013 12:38:29 -0500 Subject: [PATCH 28/81] CLOUDSTACK-1837: Documenting steps from this bug for upgrade procedure from 4.0 to 4.1. Unsure how these translate for earlier CloudStack versions. --- docs/en-US/Release_Notes.xml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index b8465ed2e82..00cbc49c881 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -54,6 +54,26 @@ under the License. Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. # mysqldump -u root -p cloud > cloudstack-backup.sql + + Whether you're upgrading a Red Hat/CentOS based system or Ubuntu based system, you're going to need to stop the CloudStack management server before proceeding. + # service cloud-management stop + + + If you have made changes to /etc/cloud/management/components.xml, you'll need to carry these over manually to the new file, /etc/cloudstack/management/componentContext.xml. This is not done automatically. (If you're unsure, we recommend making a backup of the original components.xml to be on the safe side. + + + For AWS API Users Only + This step is only necessary if you're using the AWS APIs with &PRODUCT;. If not, you can skip this step. + + The file /etc/cloud/management/db.properties will be carried over to etc/cloudstack/management/db.properties, but the parameters for the AWS API are not carried over. You'll need to add these parameters to the new file manually: + +db.awsapi.username= +db.awsapi.password= +db.awsapi.host= +db.awsapi.port= + + For the AWS API queries to work, you'll need to copy those over to /etc/cloudstack/management/db.properties (with the values you have currently). + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . Community Packages @@ -74,7 +94,8 @@ under the License. $ sudo apt-get update - ### upgrade steps for Debian on master servers goes here. + Now that you have the repository configured, it's time to install the cloudstack-management package. This will pull in any other dependencies you need. + $ sudo apt-get install cloudstack-management You will need to manually install the cloudstack-agent package: From 1c33a3ee80d1172d300f06b4738cdbc46dfe4bc3 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 5 Apr 2013 11:51:59 -0700 Subject: [PATCH 29/81] Make apidoc generation less verbose -- makes it hard to figure out what is going on with the rest of the build Signed-off-by: Chiradeep Vittal --- tools/apidoc/build-apidoc.sh | 2 +- tools/apidoc/pom.xml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/apidoc/build-apidoc.sh b/tools/apidoc/build-apidoc.sh index 14d6459d0c6..d048a1b2d2a 100755 --- a/tools/apidoc/build-apidoc.sh +++ b/tools/apidoc/build-apidoc.sh @@ -19,7 +19,7 @@ # cloud-build-api-doc.sh -- builds api documentation. #set -x -set -u +#set -u TARGETJARDIR="$1" shift DEPSDIR="$1" diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml index b324ad4b567..f7570ba13ed 100644 --- a/tools/apidoc/pom.xml +++ b/tools/apidoc/pom.xml @@ -43,7 +43,6 @@ bash - -x ./build-apidoc.sh ${client.config.jars} ${client.config.jars} @@ -63,7 +62,7 @@ target tar - -cvjf + -cjf apidoc.tar.bz2 xmldoc From 1a5162f4d2357c46d1d21687f7c47007b4690604 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Tue, 9 Apr 2013 13:54:35 -0500 Subject: [PATCH 30/81] The defaults.cfg for the Publican branding was out of date. Changed to current URLs. --- docs/publican-cloudstack/defaults.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/publican-cloudstack/defaults.cfg b/docs/publican-cloudstack/defaults.cfg index 9e27bdd309d..b288b33af47 100644 --- a/docs/publican-cloudstack/defaults.cfg +++ b/docs/publican-cloudstack/defaults.cfg @@ -16,6 +16,6 @@ # specific language governing permissions and limitations # under the License. -doc_url: "http://incubator.apache.org/cloudstack/docs" -prod_url: "http://cloudstack.org" +doc_url: "http://cloudstack.apache.org/docs" +prod_url: "http://cloudstack.apache.org" From 6a1d38476101429ad149617b684a31f80afcc0f7 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 8 Apr 2013 17:14:03 -0700 Subject: [PATCH 31/81] multiEdit: Support createForm for actions If 'createForm' block is specified in a multi-edit action, show createForm and pass fields to action. --- ui/scripts/ui/widgets/multiEdit.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 0a055916649..2cb0414707a 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -323,6 +323,7 @@ var $expandable = $dataItem.find('.expandable-listing'); var isDestroy = $target.hasClass('destroy'); var isEdit = $target.hasClass('edit'); + var createForm = action.createForm; if (isDestroy) { var $loading = _medit.loadingItem($multi, _l('label.removing') + '...'); @@ -340,7 +341,19 @@ } if (!isEdit) { - performAction(); + if (createForm) { + cloudStack.dialog.createForm({ + form: createForm, + after: function(args) { + var $loading = $('
').addClass('loading-overlay').prependTo($dataItem); + performAction({ data: args.data, complete: function() { + $multi.trigger('refresh'); + } }); + } + }); + } else { + performAction(); + } } else { // Get editable fields var editableFields = {}; From 685a8a72ceea51d06bac39ec81e5716288185787 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 9 Apr 2013 12:50:02 -0700 Subject: [PATCH 32/81] multiEdit, action pre-filter: pass 'actions' in options object --- ui/scripts/ui/widgets/multiEdit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 2cb0414707a..27b14d16e61 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -240,6 +240,7 @@ // Action filter var allowedActions = options.preFilter ? options.preFilter({ + actions: $.map(actions, function(value, key) { return key; }), context: $.extend(true, {}, options.context, { multiRule: [data], actions: $.map(actions, function(value, key) { return key; }) From d9d6d21b5c04c5c9d210453ffc9f51c8aea1080a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 9 Apr 2013 14:15:29 -0700 Subject: [PATCH 33/81] VM multiple NICs: Fix broken UI due to API call changes --- ui/scripts/network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index cc1aad34927..6f6a073812f 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1186,7 +1186,7 @@ virtualmachineid: args.context.instances[0].id }, success: function(json) { - var ips = json.listnics.nic ? json.listnics.nic[0].secondaryip : []; + var ips = json.listnicsresponse.nic ? json.listnicsresponse.nic[0].secondaryip : []; args.response.success({ data: $(ips).map(function(index, ip) { @@ -1252,7 +1252,7 @@ virtualmachineid: args.context.instances[0].id }, success: function(json) { - var ips = json.listnics.nic[0].secondaryip + var ips = json.listnicsresponse.nic[0].secondaryip args.response.success({ data: $.grep($(ips).map(function(index, ip) { From 4fd3fca84876b9caf863bfc4be0d075f503096c3 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 9 Apr 2013 14:16:01 -0700 Subject: [PATCH 34/81] List view UI: Fix 'no data to show' message not appearing in some cases --- ui/scripts/ui/widgets/listView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 4b88647e6f1..1c74056c75b 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -863,7 +863,7 @@ var uiCustom = listViewArgs.uiCustom; var subselect = uiCustom ? listViewArgs.listView.subselect : null; - if (!data || ($.isArray(data) && !data.length)) { + if (!data || !data.length) { if (!$tbody.find('tr').size()) { return [ $('').addClass('empty').append( From e7983b25cccc69abe0304a0b8d720c4c85c4561a Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Sun, 24 Mar 2013 23:50:02 -0700 Subject: [PATCH 35/81] QuickCloud: Enable secondary storage daemon to run outside the system vm --- agent/src/com/cloud/agent/AgentShell.java | 3 +- .../cloud/resource/AgentStorageResource.java | 2 +- .../PremiumSecondaryStorageResource.java | 2 + ...VmwareSecondaryStorageResourceHandler.java | 1 + scripts/vm/systemvm/injectkeys.sh | 26 ++- server/pom.xml | 5 + .../secondary/SecondaryStorageDiscoverer.java | 4 +- services/pom.xml | 1 + .../secondary-storage/conf/agent.properties | 11 ++ .../conf/environment.properties | 2 + .../secondary-storage/conf/log4j-cloud.xml | 102 ++++++++++ services/secondary-storage/pom.xml | 113 ++++++++++++ services/secondary-storage/scripts/_run.sh | 63 +++++++ .../secondary-storage/scripts/config_auth.sh | 69 +++++++ .../secondary-storage/scripts/config_ssl.sh | 174 ++++++++++++++++++ .../secondary-storage/scripts/ipfirewall.sh | 50 +++++ .../secondary-storage/scripts/run-proxy.sh | 48 +++++ services/secondary-storage/scripts/run.bat | 18 ++ services/secondary-storage/scripts/run.sh | 45 +++++ .../secondary-storage/scripts/ssvm-check.sh | 136 ++++++++++++++ .../CifsSecondaryStorageResource.java | 10 +- .../LocalSecondaryStorageResource.java | 6 +- .../resource/NfsSecondaryStorageResource.java | 148 +++++++++------ .../resource/SecondaryStorageResource.java | 2 +- .../SecondaryStorageResourceHandler.java | 2 +- .../storage/template/DownloadManager.java | 10 +- .../storage/template/DownloadManagerImpl.java | 51 +++-- .../storage/template/UploadManager.java | 6 +- .../storage/template/UploadManagerImpl.java | 7 +- tools/devcloud/pom.xml | 33 ++++ tools/devcloud/quickcloud.cfg | 120 ++++++++++++ tools/marvin/marvin/cloudstackConnection.py | 6 +- tools/marvin/marvin/deployDataCenter.py | 16 +- 33 files changed, 1188 insertions(+), 104 deletions(-) create mode 100644 services/secondary-storage/conf/agent.properties create mode 100644 services/secondary-storage/conf/environment.properties create mode 100644 services/secondary-storage/conf/log4j-cloud.xml create mode 100644 services/secondary-storage/pom.xml create mode 100755 services/secondary-storage/scripts/_run.sh create mode 100755 services/secondary-storage/scripts/config_auth.sh create mode 100755 services/secondary-storage/scripts/config_ssl.sh create mode 100755 services/secondary-storage/scripts/ipfirewall.sh create mode 100644 services/secondary-storage/scripts/run-proxy.sh create mode 100644 services/secondary-storage/scripts/run.bat create mode 100755 services/secondary-storage/scripts/run.sh create mode 100644 services/secondary-storage/scripts/ssvm-check.sh rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/resource/CifsSecondaryStorageResource.java (98%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/resource/LocalSecondaryStorageResource.java (97%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/resource/NfsSecondaryStorageResource.java (96%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/resource/SecondaryStorageResource.java (95%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/resource/SecondaryStorageResourceHandler.java (95%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/template/DownloadManager.java (94%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/template/DownloadManagerImpl.java (95%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/template/UploadManager.java (94%) rename {core/src/com/cloud => services/secondary-storage/src/org/apache/cloudstack}/storage/template/UploadManagerImpl.java (98%) create mode 100644 tools/devcloud/quickcloud.cfg diff --git a/agent/src/com/cloud/agent/AgentShell.java b/agent/src/com/cloud/agent/AgentShell.java index 2af08e9bf21..73b3950e7e4 100644 --- a/agent/src/com/cloud/agent/AgentShell.java +++ b/agent/src/com/cloud/agent/AgentShell.java @@ -38,12 +38,10 @@ import java.util.UUID; import javax.naming.ConfigurationException; -import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; import com.cloud.agent.Agent.ExitStatus; @@ -373,6 +371,7 @@ public class AgentShell implements IAgentShell { throw new ConfigurationException("Unable to find the guid"); } _guid = UUID.randomUUID().toString(); + _properties.setProperty("guid", _guid); } return true; diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentStorageResource.java b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentStorageResource.java index 2d81c2ab705..a012340d088 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentStorageResource.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentStorageResource.java @@ -20,6 +20,7 @@ import java.util.Map; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.SecondaryStorageResource; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -35,7 +36,6 @@ import com.cloud.agent.manager.SimulatorManager; import com.cloud.agent.manager.SimulatorManager.AgentType; import com.cloud.host.Host; import com.cloud.host.Host.Type; -import com.cloud.storage.resource.SecondaryStorageResource; import com.cloud.vm.SecondaryStorageVm; diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/PremiumSecondaryStorageResource.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/PremiumSecondaryStorageResource.java index 856982a8e0e..3966e0242d9 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/PremiumSecondaryStorageResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/PremiumSecondaryStorageResource.java @@ -21,6 +21,8 @@ import java.util.Map; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource; +import org.apache.cloudstack.storage.resource.SecondaryStorageResourceHandler; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index 566e750c3fe..ce42f67bf1d 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -18,6 +18,7 @@ package com.cloud.storage.resource; import java.util.List; +import org.apache.cloudstack.storage.resource.SecondaryStorageResourceHandler; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; diff --git a/scripts/vm/systemvm/injectkeys.sh b/scripts/vm/systemvm/injectkeys.sh index e56eb85b356..49adfb34cb3 100755 --- a/scripts/vm/systemvm/injectkeys.sh +++ b/scripts/vm/systemvm/injectkeys.sh @@ -22,6 +22,7 @@ # $2 = new private key #set -x +set -e TMP=/tmp MOUNTPATH=${HOME}/systemvm_mnt @@ -29,7 +30,7 @@ TMPDIR=${TMP}/cloud/systemvm clean_up() { - sudo umount $MOUNTPATH + $SUDO umount $MOUNTPATH } inject_into_iso() { @@ -39,23 +40,23 @@ inject_into_iso() { local tmpiso=${TMP}/$1 mkdir -p $MOUNTPATH [ ! -f $isofile ] && echo "$(basename $0): Could not find systemvm iso patch file $isofile" && return 1 - sudo mount -o loop $isofile $MOUNTPATH + $SUDO mount -o loop $isofile $MOUNTPATH [ $? -ne 0 ] && echo "$(basename $0): Failed to mount original iso $isofile" && clean_up && return 1 diff -q $MOUNTPATH/authorized_keys $newpubkey &> /dev/null && clean_up && return 0 - sudo cp -b $isofile $backup + $SUDO cp -b $isofile $backup [ $? -ne 0 ] && echo "$(basename $0): Failed to backup original iso $isofile" && clean_up && return 1 rm -rf $TMPDIR mkdir -p $TMPDIR [ ! -d $TMPDIR ] && echo "$(basename $0): Could not find/create temporary dir $TMPDIR" && clean_up && return 1 - sudo cp -fr $MOUNTPATH/* $TMPDIR/ + $SUDO cp -fr $MOUNTPATH/* $TMPDIR/ [ $? -ne 0 ] && echo "$(basename $0): Failed to copy from original iso $isofile" && clean_up && return 1 - sudo cp $newpubkey $TMPDIR/authorized_keys + $SUDO cp $newpubkey $TMPDIR/authorized_keys [ $? -ne 0 ] && echo "$(basename $0): Failed to copy key $newpubkey from original iso to new iso " && clean_up && return 1 mkisofs -quiet -r -o $tmpiso $TMPDIR [ $? -ne 0 ] && echo "$(basename $0): Failed to create new iso $tmpiso from $TMPDIR" && clean_up && return 1 - sudo umount $MOUNTPATH + $SUDO umount $MOUNTPATH [ $? -ne 0 ] && echo "$(basename $0): Failed to unmount old iso from $MOUNTPATH" && return 1 - sudo cp -f $tmpiso $isofile + $SUDO cp -f $tmpiso $isofile [ $? -ne 0 ] && echo "$(basename $0): Failed to overwrite old iso $isofile with $tmpiso" && return 1 rm -rf $TMPDIR } @@ -63,12 +64,17 @@ inject_into_iso() { copy_priv_key() { local newprivkey=$1 diff -q $newprivkey $(dirname $0)/id_rsa.cloud && return 0 - sudo cp -fb $newprivkey $(dirname $0)/id_rsa.cloud - sudo chmod 644 $(dirname $0)/id_rsa.cloud + $SUDO cp -fb $newprivkey $(dirname $0)/id_rsa.cloud + $SUDO chmod 644 $(dirname $0)/id_rsa.cloud return $? } -sudo mkdir -p $MOUNTPATH +if [[ `whoami` == cloud* ]] +then + SUDO=$SUDO +fi + +$SUDO mkdir -p $MOUNTPATH [ $# -ne 3 ] && echo "Usage: $(basename $0) " && exit 3 newpubkey=$1 diff --git a/server/pom.xml b/server/pom.xml index a3971954475..8a6a10c591e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -31,6 +31,11 @@ cloud-core ${project.version} + + org.apache.cloudstack + cloud-secondary-storage + ${project.version} + javax.servlet servlet-api diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java b/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java index 3ca74a351e8..6e66e0de8f1 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java @@ -30,6 +30,8 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.LocalSecondaryStorageResource; +import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -47,8 +49,6 @@ import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.resource.DummySecondaryStorageResource; -import com.cloud.storage.resource.LocalSecondaryStorageResource; -import com.cloud.storage.resource.NfsSecondaryStorageResource; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.net.NfsUtils; import com.cloud.utils.script.Script; diff --git a/services/pom.xml b/services/pom.xml index 35ec2e186ba..54b22bbbf62 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -32,5 +32,6 @@ console-proxy + secondary-storage diff --git a/services/secondary-storage/conf/agent.properties b/services/secondary-storage/conf/agent.properties new file mode 100644 index 00000000000..4a25cbd8611 --- /dev/null +++ b/services/secondary-storage/conf/agent.properties @@ -0,0 +1,11 @@ +#Storage +#Sun Mar 24 22:52:35 PDT 2013 +mount.path=/Users/chiradeep/secondary-storage +eth1ip=192.168.56.1 +name=192.168.56.10 +eth2ip=192.168.56.10 +resource=org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource +piddir=. +instance=SecondaryStorage +developer=true +secondary.storage.vm=false diff --git a/services/secondary-storage/conf/environment.properties b/services/secondary-storage/conf/environment.properties new file mode 100644 index 00000000000..269acad9152 --- /dev/null +++ b/services/secondary-storage/conf/environment.properties @@ -0,0 +1,2 @@ +paths.script=../../scripts/storage/secondary/ +paths.pid=. diff --git a/services/secondary-storage/conf/log4j-cloud.xml b/services/secondary-storage/conf/log4j-cloud.xml new file mode 100644 index 00000000000..7d9d22cfa99 --- /dev/null +++ b/services/secondary-storage/conf/log4j-cloud.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml new file mode 100644 index 00000000000..d8dbf1dd250 --- /dev/null +++ b/services/secondary-storage/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + cloud-secondary-storage + Apache CloudStack Secondary Storage Service + + org.apache.cloudstack + cloud-services + 4.2.0-SNAPSHOT + ../pom.xml + + + + log4j + log4j + ${cs.log4j.version} + + + com.google.code.gson + gson + ${cs.gson.version} + + + commons-codec + commons-codec + ${cs.codec.version} + + + + org.apache.cloudstack + cloud-agent + ${project.version} + + + org.apache.cloudstack + cloud-patches + ${project.version} + pom + + + + install + src + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + + java + + + + + com.cloud.agent.AgentShell + + zone=1 + pod=1 + host=192.168.56.1 + + + + javax.net.ssl.trustStore + certs/realhostip.keystore + + + + + + + + + vmware + + + nonoss + + + + + org.apache.cloudstack + cloud-plugin-hypervisor-vmware + ${project.version} + + + org.apache.cloudstack + cloud-vmware-base + ${project.version} + + + + + + diff --git a/services/secondary-storage/scripts/_run.sh b/services/secondary-storage/scripts/_run.sh new file mode 100755 index 00000000000..e408378afbc --- /dev/null +++ b/services/secondary-storage/scripts/_run.sh @@ -0,0 +1,63 @@ +#!/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. + + + + + +#run.sh runs the console proxy. + +# make sure we delete the old files from the original template +rm console-proxy.jar +rm console-common.jar +rm conf/cloud.properties + +set -x + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done +keyvalues= + +CMDLINE=$(cat /var/cache/cloud/cmdline) + +#CMDLINE="graphical utf8 eth0ip=0.0.0.0 eth0mask=255.255.255.0 eth1ip=192.168.140.40 eth1mask=255.255.255.0 eth2ip=172.24.0.50 eth2mask=255.255.0.0 gateway=172.24.0.1 dns1=72.52.126.11 template=domP dns2=72.52.126.12 host=192.168.1.142 port=8250 mgmtcidr=192.168.1.0/24 localgw=192.168.140.1 zone=5 pod=5" +for i in $CMDLINE + do + KEY=$(echo $i | cut -s -d= -f1) + VALUE=$(echo $i | cut -s -d= -f2) + [ "$KEY" == "" ] && continue + case $KEY in + *) + keyvalues="${keyvalues} $KEY=$VALUE" + esac + done + +tot_mem_k=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}') +let "tot_mem_m=tot_mem_k>>10" +let "eightypcnt=$tot_mem_m*8/10" +let "maxmem=$tot_mem_m-80" + +if [ $maxmem -gt $eightypcnt ] +then + maxmem=$eightypcnt +fi + +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/secondary-storage/scripts/config_auth.sh b/services/secondary-storage/scripts/config_auth.sh new file mode 100755 index 00000000000..4b74f8eb995 --- /dev/null +++ b/services/secondary-storage/scripts/config_auth.sh @@ -0,0 +1,69 @@ +#!/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. + + + + + + +BASE_DIR="/var/www/html/copy/template/" +HTACCESS="$BASE_DIR/.htaccess" + +PASSWDFILE="/etc/httpd/.htpasswd" +if [ -d /etc/apache2 ] +then + PASSWDFILE="/etc/apache2/.htpasswd" +fi + +config_htaccess() { + mkdir -p $BASE_DIR + result=$? + echo "Options -Indexes" > $HTACCESS + let "result=$result+$?" + echo "AuthType Basic" >> $HTACCESS + let "result=$result+$?" + echo "AuthName \"Authentication Required\"" >> $HTACCESS + let "result=$result+$?" + echo "AuthUserFile \"$PASSWDFILE\"" >> $HTACCESS + let "result=$result+$?" + echo "Require valid-user" >> $HTACCESS + let "result=$result+$?" + return $result +} + +write_passwd() { + local user=$1 + local passwd=$2 + htpasswd -bc $PASSWDFILE $user $passwd + return $? +} + +if [ $# -ne 2 ] ; then + echo $"Usage: `basename $0` username password " + exit 0 +fi + +write_passwd $1 $2 +if [ $? -ne 0 ] +then + echo "Failed to update password" + exit 2 +fi + +config_htaccess +exit $? diff --git a/services/secondary-storage/scripts/config_ssl.sh b/services/secondary-storage/scripts/config_ssl.sh new file mode 100755 index 00000000000..8d80c4731ad --- /dev/null +++ b/services/secondary-storage/scripts/config_ssl.sh @@ -0,0 +1,174 @@ +#!/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. + + + + +help() { + printf " -c use customized key/cert\n" + printf " -k path of private key\n" + printf " -p path of certificate of public key\n" + printf " -t path of certificate chain\n" +} + + +config_httpd_conf() { + local ip=$1 + local srvr=$2 + cp -f /etc/httpd/conf/httpd.conf.orig /etc/httpd/conf/httpd.conf + sed -i -e "s/Listen.*:80$/Listen $ip:80/" /etc/httpd/conf/httpd.conf + echo " " >> /etc/httpd/conf/httpd.conf + echo " DocumentRoot /var/www/html/" >> /etc/httpd/conf/httpd.conf + echo " ServerName $srvr" >> /etc/httpd/conf/httpd.conf + echo " SSLEngine on" >> /etc/httpd/conf/httpd.conf + echo " SSLCertificateFile /etc/httpd/ssl/certs/realhostip.crt" >> /etc/httpd/conf/httpd.conf + echo " SSLCertificateKeyFile /etc/httpd/ssl/keys/realhostip.key" >> /etc/httpd/conf/httpd.conf + echo "" >> /etc/httpd/conf/httpd.conf +} + +config_apache2_conf() { + local ip=$1 + local srvr=$2 + cp -f /etc/apache2/sites-available/default.orig /etc/apache2/sites-available/default + cp -f /etc/apache2/sites-available/default-ssl.orig /etc/apache2/sites-available/default-ssl + sed -i -e "s///" /etc/apache2/sites-available/default + sed -i -e "s///" /etc/apache2/sites-available/default-ssl + sed -i -e "s/Listen .*:80/Listen $ip:80/g" /etc/apache2/ports.conf + sed -i -e "s/Listen .*:443/Listen $ip:443/g" /etc/apache2/ports.conf + sed -i -e "s/NameVirtualHost .*:80/NameVirtualHost $ip:80/g" /etc/apache2/ports.conf + sed -i 's/ssl-cert-snakeoil.key/cert_apache.key/' /etc/apache2/sites-available/default-ssl + sed -i 's/ssl-cert-snakeoil.pem/cert_apache.crt/' /etc/apache2/sites-available/default-ssl +} + +copy_certs() { + local certdir=$(dirname $0)/certs + local mydir=$(dirname $0) + if [ -d $certdir ] && [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then + mkdir -p /etc/httpd/ssl/keys && mkdir -p /etc/httpd/ssl/certs && cp $customprivKey /etc/httpd/ssl/keys && cp $customPrivCert /etc/httpd/ssl/certs + return $? + fi + if [ ! -z customCertChain ] && [ -f $customCertChain ] ; then + cp $customCertChain /etc/httpd/ssl/certs + fi + return 1 +} + +copy_certs_apache2() { + local certdir=$(dirname $0)/certs + local mydir=$(dirname $0) + if [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then + cp $customPrivKey /etc/ssl/private/cert_apache.key && cp $customPrivCert /etc/ssl/certs/cert_apache.crt + fi + if [ ! -z "$customCertChain" ] && [ -f "$customCertChain" ] ; then + cp $customCertChain /etc/ssl/certs/cert_apache_chain.crt + fi + return 0 +} + + +cflag= +cpkflag= +cpcflag= +cccflag= +customPrivKey=$(dirname $0)/certs/realhostip.key +customPrivCert=$(dirname $0)/certs/realhostip.crt +customCertChain= +publicIp= +hostName= +while getopts 'i:h:k:p:t:c' OPTION +do + case $OPTION in + c) cflag=1 + ;; + k) cpkflag=1 + customPrivKey="$OPTARG" + ;; + p) cpcflag=1 + customPrivCert="$OPTARG" + ;; + t) cccflag=1 + customCertChain="$OPTARG" + ;; + i) publicIp="$OPTARG" + ;; + h) hostName="$OPTARG" + ;; + ?) help + ;; + esac +done + + +if [ -z "$publicIp" ] || [ -z "$hostName" ] +then + help + exit 1 +fi + +if [ "$cflag" == "1" ] +then + if [ "$cpkflag$cpcflag" != "11" ] + then + help + exit 1 + fi + if [ ! -f "$customPrivKey" ] + then + printf "priviate key file is not exist\n" + exit 2 + fi + + if [ ! -f "$customPrivCert" ] + then + printf "public certificate is not exist\n" + exit 3 + fi + + if [ "$cccflag" == "1" ] + then + if [ ! -f "$customCertChain" ] + then + printf "certificate chain is not exist\n" + exit 4 + fi + fi +fi + +if [ -d /etc/apache2 ] +then + copy_certs_apache2 +else + copy_certs +fi + +if [ $? -ne 0 ] +then + echo "Failed to copy certificates" + exit 2 +fi + +if [ -d /etc/apache2 ] +then + config_apache2_conf $publicIp $hostName + /etc/init.d/apache2 stop + /etc/init.d/apache2 start +else + config_httpd_conf $publicIp $hostName +fi + + diff --git a/services/secondary-storage/scripts/ipfirewall.sh b/services/secondary-storage/scripts/ipfirewall.sh new file mode 100755 index 00000000000..4711b8ac6db --- /dev/null +++ b/services/secondary-storage/scripts/ipfirewall.sh @@ -0,0 +1,50 @@ +#!/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. + +BASE_DIR="/var/www/html/copy/" +HTACCESS="$BASE_DIR/.htaccess" + +config_htaccess() { + mkdir -p $BASE_DIR + result=$? + echo "Options -Indexes" > $HTACCESS + let "result=$result+$?" + echo "order deny,allow" >> $HTACCESS + let "result=$result+$?" + echo "deny from all" >> $HTACCESS + let "result=$result+$?" + return $result +} + +ips(){ + echo "allow from $1" >> $HTACCESS + result=$? + return $result +} + +is_append="$1" +shift +if [ $is_append != "true" ]; then + config_htaccess +fi +for i in $@ +do + ips "$i" +done +exit $? + diff --git a/services/secondary-storage/scripts/run-proxy.sh b/services/secondary-storage/scripts/run-proxy.sh new file mode 100644 index 00000000000..d6ccf7c0091 --- /dev/null +++ b/services/secondary-storage/scripts/run-proxy.sh @@ -0,0 +1,48 @@ +#!/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. + + + + + +#run.sh runs the console proxy. + +# make sure we delete the old files from the original template +rm console-proxy.jar +rm console-common.jar +rm conf/cloud.properties + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done + +#CMDLINE=$(cat /proc/cmdline) +#for i in $CMDLINE +# do +# KEY=$(echo $i | cut -d= -f1) +# VALUE=$(echo $i | cut -d= -f2) +# case $KEY in +# mgmt_host) +# MGMT_HOST=$VALUE +# ;; +# esac +# done + +java -mx700m -cp $CP:./conf com.cloud.consoleproxy.ConsoleProxy $@ diff --git a/services/secondary-storage/scripts/run.bat b/services/secondary-storage/scripts/run.bat new file mode 100644 index 00000000000..ce6dc404574 --- /dev/null +++ b/services/secondary-storage/scripts/run.bat @@ -0,0 +1,18 @@ +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +java -mx700m -cp cloud-console-proxy.jar;;cloud-console-common.jar;log4j-1.2.15.jar;apache-log4j-extras-1.0.jar;gson-1.3.jar;commons-logging-1.1.1.jar;.;.\conf; com.cloud.consoleproxy.ConsoleProxy %* diff --git a/services/secondary-storage/scripts/run.sh b/services/secondary-storage/scripts/run.sh new file mode 100755 index 00000000000..146d96f0287 --- /dev/null +++ b/services/secondary-storage/scripts/run.sh @@ -0,0 +1,45 @@ +#!/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. + + + + + +#_run.sh runs the agent client. + +# set -x + +while true +do + ./_run.sh "$@" & + wait + ex=$? + if [ $ex -eq 0 ] || [ $ex -eq 1 ] || [ $ex -eq 66 ] || [ $ex -gt 128 ]; then + # permanent errors + sleep 5 + fi + + # user stop agent by service cloud stop + grep 'stop' /usr/local/cloud/systemvm/user_request &>/dev/null + if [ $? -eq 0 ]; then + timestamp=$(date) + echo "$timestamp User stops cloud.com service" >> /var/log/cloud.log + exit 0 + fi + sleep 5 +done diff --git a/services/secondary-storage/scripts/ssvm-check.sh b/services/secondary-storage/scripts/ssvm-check.sh new file mode 100644 index 00000000000..a4011647f07 --- /dev/null +++ b/services/secondary-storage/scripts/ssvm-check.sh @@ -0,0 +1,136 @@ +#!/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. + + +# Health check script for the Secondary Storage VM + +# DNS server is specified. + + +CMDLINE=/var/cache/cloud/cmdline +for i in `cat $CMDLINE` +do + key=`echo $i | cut -d= -f1` + value=`echo $i | cut -d= -f2` + case $key in + host) + MGMTSERVER=$value + ;; + esac +done + + +# ping dns server +echo ================================================ +DNSSERVER=`egrep '^nameserver' /etc/resolv.conf | awk '{print $2}'| head -1` +echo "First DNS server is " $DNSSERVER +ping -c 2 $DNSSERVER +if [ $? -eq 0 ] +then + echo "Good: Can ping DNS server" +else + echo "WARNING: cannot ping DNS server" + echo "route follows" + route -n +fi + + +# check dns resolve +echo ================================================ +nslookup download.cloud.com 1> /tmp/dns 2>&1 +grep 'no servers could' /tmp/dns 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "ERROR: DNS not resolving download.cloud.com" + echo resolv.conf follows + cat /etc/resolv.conf + exit 2 +else + echo "Good: DNS resolves download.cloud.com" +fi + + +# check to see if we have the NFS volume mounted +echo ================================================ +mount|grep -v sunrpc|grep nfs 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "NFS is currently mounted" + # check for write access + for MOUNTPT in `mount|grep -v sunrpc|grep nfs| awk '{print $3}'` + do + if [ $MOUNTPT != "/proc/xen" ] # mounted by xen + then + echo Mount point is $MOUNTPT + touch $MOUNTPT/foo + if [ $? -eq 0 ] + then + echo "Good: Can write to mount point" + rm $MOUNTPT/foo + else + echo "ERROR: Cannot write to mount point" + echo "You need to export with norootsquash" + fi + fi + done +else + echo "ERROR: NFS is not currently mounted" + echo "Try manually mounting from inside the VM" + NFSSERVER=`awk '{print $17}' $CMDLINE|awk -F= '{print $2}'|awk -F: '{print $1}'` + echo "NFS server is " $NFSSERVER + ping -c 2 $NFSSERVER + if [ $? -eq 0 ] + then + echo "Good: Can ping NFS server" + else + echo "WARNING: cannot ping NFS server" + echo routing table follows + route -n + fi +fi + + +# check for connectivity to the management server +echo ================================================ +echo Management server is $MGMTSERVER. Checking connectivity. +socatout=$(echo | socat - TCP:$MGMTSERVER:8250,connect-timeout=3 2>&1) +if [ $? -eq 0 ] +then + echo "Good: Can connect to management server port 8250" +else + echo "ERROR: Cannot connect to $MGMTSERVER port 8250" + echo $socatout + exit 4 +fi + + +# check for the java process running +echo ================================================ +ps -eaf|grep -v grep|grep java 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "Good: Java process is running" +else + echo "ERROR: Java process not running. Try restarting the SSVM." + exit 3 +fi + +echo ================================================ +echo Tests Complete. Look for ERROR or WARNING above. + +exit 0 diff --git a/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/CifsSecondaryStorageResource.java similarity index 98% rename from core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/CifsSecondaryStorageResource.java index 285005a1c3a..de4cfe0c97f 100755 --- a/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/CifsSecondaryStorageResource.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.resource; +package org.apache.cloudstack.storage.resource; import java.io.File; import java.net.InetAddress; @@ -27,6 +27,10 @@ import java.util.Random; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.template.DownloadManager; +import org.apache.cloudstack.storage.template.DownloadManagerImpl; +import org.apache.cloudstack.storage.template.UploadManager; +import org.apache.cloudstack.storage.template.UploadManagerImpl; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -58,11 +62,7 @@ import com.cloud.resource.ServerResourceBase; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; -import com.cloud.storage.template.DownloadManager; -import com.cloud.storage.template.DownloadManagerImpl; import com.cloud.storage.template.TemplateInfo; -import com.cloud.storage.template.UploadManager; -import com.cloud.storage.template.UploadManagerImpl; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; diff --git a/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/LocalSecondaryStorageResource.java similarity index 97% rename from core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/LocalSecondaryStorageResource.java index c638c5d874e..b904254b944 100644 --- a/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/LocalSecondaryStorageResource.java @@ -14,13 +14,15 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.resource; +package org.apache.cloudstack.storage.resource; import java.util.HashMap; import java.util.Map; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.template.DownloadManager; +import org.apache.cloudstack.storage.template.DownloadManagerImpl; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -46,8 +48,6 @@ import com.cloud.resource.ServerResourceBase; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; -import com.cloud.storage.template.DownloadManager; -import com.cloud.storage.template.DownloadManagerImpl; import com.cloud.storage.template.TemplateInfo; import com.cloud.utils.component.ComponentContext; diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java similarity index 96% rename from core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index e65cbe1312e..6bcf98e1a60 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.resource; +package org.apache.cloudstack.storage.resource; import static com.cloud.utils.S3Utils.deleteDirectory; import static com.cloud.utils.S3Utils.getDirectory; @@ -46,6 +46,11 @@ import java.util.concurrent.Callable; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.template.DownloadManager; +import org.apache.cloudstack.storage.template.DownloadManagerImpl; +import org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser; +import org.apache.cloudstack.storage.template.UploadManager; +import org.apache.cloudstack.storage.template.UploadManagerImpl; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -97,18 +102,12 @@ import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.StorageLayer; -import com.cloud.storage.template.DownloadManager; -import com.cloud.storage.template.DownloadManagerImpl; -import com.cloud.storage.template.DownloadManagerImpl.ZfsPathParser; import com.cloud.storage.template.TemplateInfo; import com.cloud.storage.template.TemplateLocation; -import com.cloud.storage.template.UploadManager; -import com.cloud.storage.template.UploadManagerImpl; import com.cloud.utils.NumbersUtil; import com.cloud.utils.S3Utils; import com.cloud.utils.S3Utils.FileNamingStrategy; import com.cloud.utils.S3Utils.ObjectNamingStrategy; -import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter; @@ -133,7 +132,7 @@ SecondaryStorageResource { String _role; Map _params; StorageLayer _storage; - boolean _inSystemVM = false; + protected boolean _inSystemVM = false; boolean _sslCopy = false; DownloadManager _dlMgr; @@ -150,7 +149,7 @@ SecondaryStorageResource { private String _storageNetmask; private String _storageGateway; private final List nfsIps = new ArrayList(); - final private String _parent = "/mnt/SecStorage"; + private String _parent = "/mnt/SecStorage"; final private String _tmpltDir = "/var/cloudstack/template"; final private String _tmpltpp = "template.properties"; @Override @@ -397,8 +396,8 @@ SecondaryStorageResource { @Override public boolean accept(final File directory, final String fileName) { - File fileToUpload = new File(directory.getAbsolutePath() + "/" + fileName); - return !fileName.startsWith(".") && !fileToUpload.isDirectory(); + File fileToUpload = new File(directory.getAbsolutePath() + "/" + fileName); + return !fileName.startsWith(".") && !fileToUpload.isDirectory(); } }, new ObjectNamingStrategy() { @Override @@ -1107,9 +1106,7 @@ SecondaryStorageResource { } private Answer execute(ListTemplateCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } + if (cmd.getSwift() != null) { Map templateInfos = swiftListTemplate(cmd.getSwift()); return new ListTemplateAnswer(cmd.getSwift().toString(), templateInfos); @@ -1121,9 +1118,6 @@ SecondaryStorageResource { } private Answer execute(ListVolumeCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } String root = getRootDir(cmd.getSecUrl()); Map templateInfos = _dlMgr.gatherVolumeInfo(root); @@ -1217,7 +1211,9 @@ SecondaryStorageResource { } public String allowOutgoingOnPrivate(String destCidr) { - + if (!_inSystemVM) { + return null; + } Script command = new Script("/bin/bash", s_logger); String intf = "eth1"; command.add("-c"); @@ -1392,6 +1388,9 @@ SecondaryStorageResource { synchronized public String getRootDir(String secUrl) { + if (!_inSystemVM) { + return _parent; + } try { URI uri = new URI(secUrl); String nfsHost = uri.getHost(); @@ -1474,6 +1473,11 @@ SecondaryStorageResource { _publicIp = (String) params.get("eth2ip"); _hostname = (String) params.get("name"); + String inSystemVM = (String) params.get("secondary.storage.vm"); + if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) { + _inSystemVM = true; + } + _storageIp = (String) params.get("storageip"); if (_storageIp == null) { s_logger.warn("Wait, there is no storageip in /proc/cmdline, something wrong!"); @@ -1505,7 +1509,10 @@ SecondaryStorageResource { throw new ConfigurationException("Unable to find class " + value); } } - _storage.mkdirs(_parent); + + if (_inSystemVM) + _storage.mkdirs(_parent); + _configSslScr = Script.findScript(getDefaultScriptsDir(), "config_ssl.sh"); if (_configSslScr != null) { s_logger.info("config_ssl.sh found in " + _configSslScr); @@ -1539,10 +1546,12 @@ SecondaryStorageResource { _instance = (String)params.get("instance"); + if (!_inSystemVM) { + _parent = (String) params.get("mount.path"); + } - String inSystemVM = (String)params.get("secondary.storage.vm"); - if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) { - _inSystemVM = true; + + if (_inSystemVM) { _localgw = (String)params.get("localgw"); if (_localgw != null) { // can only happen inside service vm String mgmtHost = (String) params.get("host"); @@ -1581,6 +1590,9 @@ SecondaryStorageResource { } private void startAdditionalServices() { + if (!_inSystemVM) { + return; + } Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); @@ -1598,6 +1610,9 @@ SecondaryStorageResource { } private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { + if (!_inSystemVM) { + return; + } s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); if (destIpOrCidr == null) { s_logger.debug("addRouteToInternalIp: destIp is null"); @@ -1637,6 +1652,9 @@ SecondaryStorageResource { } private void configureSSL() { + if (!_inSystemVM) { + return; + } Script command = new Script(_configSslScr); command.add("-i", _publicIp); command.add("-h", _hostname); @@ -1647,6 +1665,9 @@ SecondaryStorageResource { } private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) { + if (!_inSystemVM) { + return; + } Script command = new Script(_configSslScr); command.add("-i", _publicIp); command.add("-h", _hostname); @@ -1758,13 +1779,15 @@ SecondaryStorageResource { if(_publicIp != null) cmd.setPublicIpAddress(_publicIp); - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ln -sf " + _parent + " /var/www/html/copy"); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in linking err=" + result); - return null; + if (_inSystemVM) { + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ln -sf " + _parent + " /var/www/html/copy"); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in linking err=" + result); + return null; + } } return new StartupCommand[] {cmd}; } @@ -1810,33 +1833,50 @@ SecondaryStorageResource { return "./scripts/storage/secondary"; } - @Override - public void setName(String name) { - // TODO Auto-generated method stub - - } + @Override + public void setName(String name) { + // TODO Auto-generated method stub - @Override - public void setConfigParams(Map params) { - // TODO Auto-generated method stub - - } + } - @Override - public Map getConfigParams() { - // TODO Auto-generated method stub - return null; - } + @Override + public void setConfigParams(Map params) { + // TODO Auto-generated method stub - @Override - public int getRunLevel() { - // TODO Auto-generated method stub - return 0; - } + } - @Override - public void setRunLevel(int level) { - // TODO Auto-generated method stub - - } + @Override + public Map getConfigParams() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getRunLevel() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setRunLevel(int level) { + // TODO Auto-generated method stub + + } + + @Override + public void fillNetworkInformation(final StartupCommand cmd) { + final String dummyMac = "00:06:0A:0B:0C:0D"; + final String dummyNetmask = "255.255.255.0"; + if (!_inSystemVM) { + cmd.setPrivateIpAddress(_eth1ip); + cmd.setPrivateMacAddress(dummyMac); + cmd.setPrivateNetmask(dummyNetmask); + cmd.setPublicIpAddress(_publicIp); + cmd.setPublicMacAddress(dummyMac); + cmd.setPublicNetmask(dummyNetmask); + cmd.setName(_hostname); + } else { + super.fillNetworkInformation(cmd); + } + } } diff --git a/core/src/com/cloud/storage/resource/SecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java similarity index 95% rename from core/src/com/cloud/storage/resource/SecondaryStorageResource.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java index 37c6686afd8..5c87b0dcc92 100755 --- a/core/src/com/cloud/storage/resource/SecondaryStorageResource.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.resource; +package org.apache.cloudstack.storage.resource; import com.cloud.agent.api.storage.ssCommand; import com.cloud.resource.ServerResource; /** diff --git a/core/src/com/cloud/storage/resource/SecondaryStorageResourceHandler.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java similarity index 95% rename from core/src/com/cloud/storage/resource/SecondaryStorageResourceHandler.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java index 28b96b621ed..d03d983dcd8 100644 --- a/core/src/com/cloud/storage/resource/SecondaryStorageResourceHandler.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.resource; +package org.apache.cloudstack.storage.resource; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; diff --git a/core/src/com/cloud/storage/template/DownloadManager.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java similarity index 94% rename from core/src/com/cloud/storage/template/DownloadManager.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java index f4f8a0f17fa..3e5072abfa3 100644 --- a/core/src/com/cloud/storage/template/DownloadManager.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java @@ -14,18 +14,20 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.template; +package org.apache.cloudstack.storage.template; -import java.util.List; import java.util.Map; +import org.apache.cloudstack.storage.resource.SecondaryStorageResource; + import com.cloud.agent.api.storage.DownloadAnswer; import com.cloud.agent.api.storage.DownloadCommand; import com.cloud.agent.api.storage.DownloadCommand.Proxy; import com.cloud.agent.api.storage.DownloadCommand.ResourceType; -import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.resource.SecondaryStorageResource; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.template.TemplateDownloader; +import com.cloud.storage.template.TemplateInfo; import com.cloud.utils.component.Manager; public interface DownloadManager extends Manager { diff --git a/core/src/com/cloud/storage/template/DownloadManagerImpl.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java similarity index 95% rename from core/src/com/cloud/storage/template/DownloadManagerImpl.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java index 22e78a081c1..a9d23cb7779 100755 --- a/core/src/com/cloud/storage/template/DownloadManagerImpl.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.template; +package org.apache.cloudstack.storage.template; import java.io.BufferedReader; import java.io.File; @@ -41,6 +41,7 @@ import java.util.concurrent.Executors; import javax.ejb.Local; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.SecondaryStorageResource; import org.apache.log4j.Logger; import com.cloud.agent.api.storage.DownloadAnswer; @@ -54,10 +55,22 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; -import com.cloud.storage.resource.SecondaryStorageResource; +import com.cloud.storage.template.HttpTemplateDownloader; +import com.cloud.storage.template.IsoProcessor; +import com.cloud.storage.template.LocalTemplateDownloader; +import com.cloud.storage.template.Processor; import com.cloud.storage.template.Processor.FormatInfo; +import com.cloud.storage.template.QCOW2Processor; +import com.cloud.storage.template.RawImageProcessor; +import com.cloud.storage.template.ScpTemplateDownloader; +import com.cloud.storage.template.TemplateConstants; +import com.cloud.storage.template.TemplateDownloader; import com.cloud.storage.template.TemplateDownloader.DownloadCompleteCallback; import com.cloud.storage.template.TemplateDownloader.Status; +import com.cloud.storage.template.TemplateInfo; +import com.cloud.storage.template.TemplateLocation; +import com.cloud.storage.template.VhdProcessor; +import com.cloud.storage.template.VmdkProcessor; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.exception.CloudRuntimeException; @@ -743,21 +756,27 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager TemplateInfo tInfo = loc.getTemplateInfo(); - if ((tInfo.size == tInfo.physicalSize) && (tInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) { + if ((tInfo.getSize() == tInfo.getPhysicalSize()) + && (tInfo.getInstallPath().endsWith(ImageFormat.OVA.getFileExtension()))) { try { Processor processor = _processors.get("VMDK Processor"); VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; - long vSize = vmdkProcessor.getTemplateVirtualSize(path, tInfo.installPath.substring(tInfo.installPath.lastIndexOf(File.separator) + 1)); - tInfo.size = vSize; + long vSize = + vmdkProcessor.getTemplateVirtualSize( + path, + tInfo.getInstallPath().substring( + tInfo.getInstallPath().lastIndexOf(File.separator) + 1)); + tInfo.setSize(vSize); loc.updateVirtualSize(vSize); loc.save(); } catch (Exception e) { - s_logger.error("Unable to get the virtual size of the template: " + tInfo.installPath + " due to " + e.getMessage()); + s_logger.error("Unable to get the virtual size of the template: " + tInfo.getInstallPath() + + " due to " + e.getMessage()); } } - result.put(tInfo.templateName, tInfo); - s_logger.debug("Added template name: " + tInfo.templateName + ", path: " + tmplt); + result.put(tInfo.getTemplateName(), tInfo); + s_logger.debug("Added template name: " + tInfo.getTemplateName() + ", path: " + tmplt); } /* for (String tmplt : isoTmplts) { @@ -800,21 +819,27 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager TemplateInfo vInfo = loc.getTemplateInfo(); - if ((vInfo.size == vInfo.physicalSize) && (vInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) { + if ((vInfo.getSize() == vInfo.getPhysicalSize()) + && (vInfo.getInstallPath().endsWith(ImageFormat.OVA.getFileExtension()))) { try { Processor processor = _processors.get("VMDK Processor"); VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; - long vSize = vmdkProcessor.getTemplateVirtualSize(path, vInfo.installPath.substring(vInfo.installPath.lastIndexOf(File.separator) + 1)); - vInfo.size = vSize; + long vSize = + vmdkProcessor.getTemplateVirtualSize( + path, + vInfo.getInstallPath().substring( + vInfo.getInstallPath().lastIndexOf(File.separator) + 1)); + vInfo.setSize(vSize); loc.updateVirtualSize(vSize); loc.save(); } catch (Exception e) { - s_logger.error("Unable to get the virtual size of the volume: " + vInfo.installPath + " due to " + e.getMessage()); + s_logger.error("Unable to get the virtual size of the volume: " + vInfo.getInstallPath() + + " due to " + e.getMessage()); } } result.put(vInfo.getId(), vInfo); - s_logger.debug("Added volume name: " + vInfo.templateName + ", path: " + vol); + s_logger.debug("Added volume name: " + vInfo.getTemplateName() + ", path: " + vol); } return result; } diff --git a/core/src/com/cloud/storage/template/UploadManager.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java similarity index 94% rename from core/src/com/cloud/storage/template/UploadManager.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java index fa40b8c9ac6..14de1500104 100755 --- a/core/src/com/cloud/storage/template/UploadManager.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java @@ -14,7 +14,9 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.template; +package org.apache.cloudstack.storage.template; + +import org.apache.cloudstack.storage.resource.SecondaryStorageResource; import com.cloud.agent.api.storage.CreateEntityDownloadURLAnswer; import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand; @@ -24,7 +26,7 @@ import com.cloud.agent.api.storage.UploadAnswer; import com.cloud.agent.api.storage.UploadCommand; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Upload.Status; -import com.cloud.storage.resource.SecondaryStorageResource; +import com.cloud.storage.template.TemplateUploader; import com.cloud.utils.component.Manager; public interface UploadManager extends Manager { diff --git a/core/src/com/cloud/storage/template/UploadManagerImpl.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java similarity index 98% rename from core/src/com/cloud/storage/template/UploadManagerImpl.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java index 2492a1be2b2..88623a9e0fb 100755 --- a/core/src/com/cloud/storage/template/UploadManagerImpl.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.template; +package org.apache.cloudstack.storage.template; import java.io.File; import java.net.URI; @@ -30,6 +30,7 @@ import java.util.concurrent.Executors; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.SecondaryStorageResource; import org.apache.log4j.Logger; import com.cloud.agent.api.storage.CreateEntityDownloadURLAnswer; @@ -43,7 +44,9 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.storage.Upload; import com.cloud.storage.UploadVO; -import com.cloud.storage.resource.SecondaryStorageResource; +import com.cloud.storage.template.FtpTemplateUploader; +import com.cloud.storage.template.Processor; +import com.cloud.storage.template.TemplateUploader; import com.cloud.storage.template.TemplateUploader.Status; import com.cloud.storage.template.TemplateUploader.UploadCompleteCallback; import com.cloud.utils.NumbersUtil; diff --git a/tools/devcloud/pom.xml b/tools/devcloud/pom.xml index d7b82c9d7fe..d32d84bd691 100644 --- a/tools/devcloud/pom.xml +++ b/tools/devcloud/pom.xml @@ -142,5 +142,38 @@ + + quicksvr + + + quicksvr + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + package + + exec + + + + + python + + ../marvin/marvin/deployDataCenter.py + -i + quickcloud.cfg + + + + + + diff --git a/tools/devcloud/quickcloud.cfg b/tools/devcloud/quickcloud.cfg new file mode 100644 index 00000000000..0e1fb4fb208 --- /dev/null +++ b/tools/devcloud/quickcloud.cfg @@ -0,0 +1,120 @@ +# 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. +# + +{ + "zones": [ + { + "name": "QuickCloud00", + "enabled" : "True", + "details" : [ + {"key" : "enable.secstorage.vm", "value": "False"}, + {"key" : "enable.consoleproxy.vm", "value": "False"} + ], + "physical_networks": [ + { + "broadcastdomainrange": "Zone", + "name": "test-network", + "traffictypes": [ + { + "typ": "Guest" + }, + { + "typ": "Management" + } + ], + "providers": [ + { + "broadcastdomainrange": "ZONE", + "name": "VirtualRouter" + }, + { + "broadcastdomainrange": "Pod", + "name": "SecurityGroupProvider" + } + ] + } + ], + "dns2": "4.4.4.4", + "dns1": "8.8.8.8", + "securitygroupenabled": "true", + "localstorageenabled": "true", + "networktype": "Basic", + "pods": [ + { + "endip": "192.168.56.220", + "name": "test00", + "startip": "192.168.56.200", + "guestIpRanges": [ + { + "startip": "192.168.56.100", + "endip": "192.168.56.199", + "netmask": "255.255.255.0", + "gateway": "192.168.56.1" + } + ], + "netmask": "255.255.255.0", + "clusters": [ + { + "clustername": "test000", + "hypervisor": "XenServer", + "hosts": [ + { + "username": "root", + "url": "http://192.168.56.10/", + "password": "password" + } + ], + "clustertype": "CloudManaged" + } + ], + "gateway": "192.168.56.1" + } + ], + "internaldns1": "192.168.56.1", + "secondaryStorages": [ + { + "url": "nfs://192.168.56.10:/opt/storage/secondary" + } + ] + } + ], + "logger": [ + { + "name": "TestClient", + "file": "testclient.log" + }, + { + "name": "TestCase", + "file": "testcase.log" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "127.0.0.1", + "port": 8096 + } + ], + "dbSvr": + { + "dbSvr": "127.0.0.1", + "port": 3306, + "user": "cloud", + "passwd": "cloud", + "db": "cloud" + } +} diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py index e8b861eedb2..1caeef35ff2 100644 --- a/tools/marvin/marvin/cloudstackConnection.py +++ b/tools/marvin/marvin/cloudstackConnection.py @@ -162,9 +162,9 @@ class cloudConnection(object): else: requests.pop(param) i = 0 - for v in value: - for key, val in v.iteritems(): - requests["%s[%d].%s"%(param,i,key)] = val + for val in value: + for k,v in val.iteritems(): + requests["%s[%d].%s"%(param,i,k)] = v i = i + 1 if self.logging is not None: diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index cec920c5ff0..42bc5f9ebef 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -270,6 +270,12 @@ class deployDataCenters(): zoneCmd.allocationstate = allocation_state return self.apiClient.updateZone(zoneCmd) + def updateZoneDetails(self, zoneid, details): + zoneCmd = updateZone.updateZoneCmd() + zoneCmd.id = zoneid + zoneCmd.details = details + return self.apiClient.updateZone(zoneCmd) + def createZones(self, zones): for zone in zones: createzone = createZone.createZoneCmd() @@ -320,7 +326,15 @@ class deployDataCenters(): zoneId) self.createSecondaryStorages(zone.secondaryStorages, zoneId) - self.enableZone(zoneId, "Enabled") + + enabled = getattr(zone, 'enabled', 'True') + if enabled == 'True' or enabled == 'None': + self.enableZone(zoneId, "Enabled") + details = getattr(zone, 'details') + if details is not None: + det = [d.__dict__ for d in details] + self.updateZoneDetails(zoneId, det) + return def isEipElbZone(self, zone): From 16790446e51645dc3e2623ebf57f88e0cfe2c89c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 25 Mar 2013 12:15:14 -0700 Subject: [PATCH 36/81] QuickCloud: start console proxy service from mvn exec:java --- .../console-proxy/server/conf/log4j-cloud.xml | 2 +- services/console-proxy/server/pom.xml | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/services/console-proxy/server/conf/log4j-cloud.xml b/services/console-proxy/server/conf/log4j-cloud.xml index 5b31c9db967..2d1d361c939 100644 --- a/services/console-proxy/server/conf/log4j-cloud.xml +++ b/services/console-proxy/server/conf/log4j-cloud.xml @@ -27,7 +27,7 @@ under the License. - + diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index f57b4caddd6..fd7b964bcab 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -254,6 +254,44 @@ + + quickcloud + + + quickcloud + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + + java + + + + + com.cloud.agent.AgentShell + + zone=1 + pod=1 + host=192.168.56.1 + + + + javax.net.ssl.trustStore + certs/realhostip.keystore + + + + + + + From 790d2ce82ef6b1ac910124c8c9ab519e2431e622 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 25 Mar 2013 12:13:51 -0700 Subject: [PATCH 37/81] QuickCloud: Remove reference to unused code QuickCloud: remove some dead code in ConsoleProxyManager --- .../api/commands/DestroyConsoleProxyCmd.java | 12 ++-------- .../consoleproxy/ConsoleProxyService.java | 23 ------------------- .../org/apache/cloudstack/api/BaseCmd.java | 2 -- .../consoleproxy/ConsoleProxyManager.java | 17 +------------- .../consoleproxy/ConsoleProxyManagerImpl.java | 20 +++------------- .../consoleproxy/ConsoleProxyService.java | 9 ++++++++ 6 files changed, 15 insertions(+), 68 deletions(-) delete mode 100644 api/src/com/cloud/consoleproxy/ConsoleProxyService.java create mode 100644 server/src/com/cloud/consoleproxy/ConsoleProxyService.java diff --git a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java b/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java index 829283e8b9f..f3210ce06f4 100644 --- a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java +++ b/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java @@ -17,14 +17,12 @@ package com.cloud.api.commands; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.log4j.Logger; import com.cloud.event.EventTypes; +import com.cloud.exception.UnsupportedServiceException; import com.cloud.user.Account; import com.cloud.user.UserContext; @@ -82,12 +80,6 @@ public class DestroyConsoleProxyCmd extends BaseAsyncCmd { @Override public void execute(){ - boolean result = _consoleProxyService.destroyConsoleProxy(this); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy console proxy"); - } + throw new UnsupportedServiceException("Use destroySystemVm API instead"); } } diff --git a/api/src/com/cloud/consoleproxy/ConsoleProxyService.java b/api/src/com/cloud/consoleproxy/ConsoleProxyService.java deleted file mode 100644 index c347e0bf9fc..00000000000 --- a/api/src/com/cloud/consoleproxy/ConsoleProxyService.java +++ /dev/null @@ -1,23 +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.consoleproxy; - -import com.cloud.api.commands.DestroyConsoleProxyCmd; - -public interface ConsoleProxyService { - boolean destroyConsoleProxy(DestroyConsoleProxyCmd cmd); -} diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index 78a2af36aa2..8fef422b915 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -32,7 +32,6 @@ import org.apache.cloudstack.usage.UsageService; import org.apache.log4j.Logger; import com.cloud.configuration.ConfigurationService; -import com.cloud.consoleproxy.ConsoleProxyService; import com.cloud.dao.EntityManager; import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; @@ -109,7 +108,6 @@ public abstract class BaseCmd { @Inject public TemplateService _templateService; @Inject public SecurityGroupService _securityGroupService; @Inject public SnapshotService _snapshotService; - @Inject public ConsoleProxyService _consoleProxyService; @Inject public VpcVirtualNetworkApplianceService _routerService; @Inject public ResponseGenerator _responseGenerator; @Inject public EntityManager _entityMgr; diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java index 6ebf3bc61f4..faec51307b5 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManager.java @@ -16,17 +16,9 @@ // under the License. package com.cloud.consoleproxy; -import com.cloud.agent.api.AgentControlAnswer; -import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; -import com.cloud.agent.api.ConsoleProxyLoadReportCommand; -import com.cloud.agent.api.StartupCommand; -import com.cloud.host.HostVO; -import com.cloud.host.Status; -import com.cloud.host.Host.Type; -import com.cloud.info.ConsoleProxyInfo; import com.cloud.utils.component.Manager; import com.cloud.vm.ConsoleProxyVO; -public interface ConsoleProxyManager extends Manager { +public interface ConsoleProxyManager extends Manager, ConsoleProxyService { public static final int DEFAULT_PROXY_CAPACITY = 50; public static final int DEFAULT_STANDBY_CAPACITY = 10; @@ -45,16 +37,9 @@ public interface ConsoleProxyManager extends Manager { public ConsoleProxyManagementState getManagementState(); public void resumeLastManagementState(); - public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId); - public ConsoleProxyVO startProxy(long proxyVmId); public boolean stopProxy(long proxyVmId); public boolean rebootProxy(long proxyVmId); public boolean destroyProxy(long proxyVmId); - - public void onLoadReport(ConsoleProxyLoadReportCommand cmd); - public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd); - public void onAgentConnect(HostVO host, StartupCommand cmd); - public void onAgentDisconnect(long agentId, Status state); } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 1edd8692ec7..fc4fc6eda77 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -30,7 +30,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; @@ -54,7 +53,6 @@ import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; -import com.cloud.api.commands.DestroyConsoleProxyCmd; import com.cloud.certificate.dao.CertificateDao; import com.cloud.cluster.ClusterManager; import com.cloud.configuration.Config; @@ -71,7 +69,6 @@ import com.cloud.deploy.DeployDestination; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; @@ -113,8 +110,8 @@ import com.cloud.servlet.ConsoleProxyServlet; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.VMTemplateHostVO; -import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; @@ -127,7 +124,6 @@ import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; @@ -170,7 +166,8 @@ import com.google.gson.GsonBuilder; // because sooner or later, it will be driven into Running state // @Local(value = { ConsoleProxyManager.class, ConsoleProxyService.class }) -public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxyManager, ConsoleProxyService, AgentHook, VirtualMachineGuru, SystemVmLoadScanHandler, ResourceStateAdapter { +public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxyManager, + AgentHook, VirtualMachineGuru, SystemVmLoadScanHandler, ResourceStateAdapter { private static final Logger s_logger = Logger.getLogger(ConsoleProxyManagerImpl.class); private static final int DEFAULT_CAPACITY_SCAN_INTERVAL = 30000; // 30 seconds @@ -1549,18 +1546,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy return true; } - @Override - public boolean destroyConsoleProxy(DestroyConsoleProxyCmd cmd) throws ServerApiException { - Long proxyId = cmd.getId(); - // verify parameters - ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyId); - if (proxy == null) { - throw new InvalidParameterValueException("unable to find a console proxy with id " + proxyId); - } - - return destroyProxy(proxyId); - } protected ConsoleProxyManagerImpl() { } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyService.java b/server/src/com/cloud/consoleproxy/ConsoleProxyService.java new file mode 100644 index 00000000000..e43e5c315e2 --- /dev/null +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyService.java @@ -0,0 +1,9 @@ +package com.cloud.consoleproxy; + +import com.cloud.info.ConsoleProxyInfo; + +public interface ConsoleProxyService { + + public abstract ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId); + +} \ No newline at end of file From 3d78019e571677f1b2ac87acebe6192f2a4fa96c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 26 Mar 2013 10:05:06 -0700 Subject: [PATCH 38/81] QuickCloud: copy authorization code from ConsoleProxyManagerImpl QuickCloud: refactor to avoid copy paste of authentication and startup code --- .../AgentBasedConsoleProxyManager.java | 184 +-------- .../com/cloud/consoleproxy/AgentHookBase.java | 266 +++++++++++++ .../consoleproxy/ConsoleProxyManagerImpl.java | 362 +++++++----------- .../StaticConsoleProxyManager.java | 84 +++- 4 files changed, 489 insertions(+), 407 deletions(-) create mode 100644 server/src/com/cloud/consoleproxy/AgentHookBase.java diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java index 6f8575d751c..ff6e64eca94 100755 --- a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java @@ -23,48 +23,28 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; -import com.cloud.agent.api.AgentControlAnswer; -import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer; -import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; -import com.cloud.agent.api.ConsoleProxyLoadReportCommand; import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; -import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupProxyCommand; -import com.cloud.agent.api.StopAnswer; -import com.cloud.agent.api.to.NicTO; -import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.agent.manager.Commands; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.deploy.DeployDestination; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.HostVO; -import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyInfo; -import com.cloud.network.Network; +import com.cloud.keystore.KeystoreManager; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.ConsoleProxyVO; -import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineGuru; import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.VirtualMachineName; -import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Local(value = { ConsoleProxyManager.class }) -public class AgentBasedConsoleProxyManager extends ManagerBase implements ConsoleProxyManager, VirtualMachineGuru, AgentHook { +public class AgentBasedConsoleProxyManager extends ManagerBase implements ConsoleProxyManager { private static final Logger s_logger = Logger.getLogger(AgentBasedConsoleProxyManager.class); @Inject @@ -85,9 +65,25 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol VirtualMachineManager _itMgr; @Inject protected ConsoleProxyDao _cpDao; + @Inject + protected KeystoreManager _ksMgr; @Inject ConfigurationDao _configDao; + public class AgentBasedAgentHook extends AgentHookBase { + + public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, + KeystoreManager ksMgr, AgentManager agentMgr) { + super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr); + } + + @Override + protected HostVO findConsoleProxyHost(StartupProxyCommand cmd) { + return _hostDao.findByGuid(cmd.getGuid()); + } + + } + public int getVncPort(VMInstanceVO vm) { if (vm.getHostId() == null) { return -1; @@ -123,11 +119,10 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol _consoleProxyUrlDomain = configs.get("consoleproxy.url.domain"); - _listener = new ConsoleProxyListener(this); + _listener = + new ConsoleProxyListener(new AgentBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, _agentMgr)); _agentMgr.registerForHostEvents(_listener, true, true, false); - _itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this); - if (s_logger.isInfoEnabled()) { s_logger.info("AgentBasedConsoleProxyManager has been configured. SSL enabled: " + _sslEnabled); } @@ -177,64 +172,8 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol return null; } - @Override - public void onLoadReport(ConsoleProxyLoadReportCommand cmd) { - } - @Override - public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) { - long vmId = 0; - if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("Invalid vm id sent from proxy(happens when proxy session has terminated)"); - } - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - try { - vmId = Long.parseLong(cmd.getVmId()); - } catch (NumberFormatException e) { - s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication", e); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - // TODO authentication channel between console proxy VM and management - // server needs to be secured, - // the data is now being sent through private network, but this is - // apparently not enough - VMInstanceVO vm = _instanceDao.findById(vmId); - if (vm == null) { - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - if (vm.getHostId() == null) { - s_logger.warn("VM " + vmId + " lost host info, failed authentication request"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - HostVO host = _hostDao.findById(vm.getHostId()); - if (host == null) { - s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - String sid = cmd.getSid(); - if (sid == null || !sid.equals(vm.getVncPassword())) { - s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - return new ConsoleAccessAuthenticationAnswer(cmd, true); - } - - @Override - public void onAgentConnect(HostVO host, StartupCommand cmd) { - } - - @Override - public void onAgentDisconnect(long agentId, Status state) { - } @Override public ConsoleProxyVO startProxy(long proxyVmId) { @@ -269,91 +208,8 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol public void resumeLastManagementState() { } - @Override - public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) { - } - @Override public String getName() { return _name; } - - @Override - public Long convertToId(String vmName) { - if (!VirtualMachineName.isValidConsoleProxyName(vmName, _instance)) { - return null; - } - return VirtualMachineName.getConsoleProxyId(vmName); - } - - @Override - public ConsoleProxyVO findByName(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public ConsoleProxyVO findById(long id) { - // TODO Auto-generated method stub - return null; - } - - @Override - public ConsoleProxyVO persist(ConsoleProxyVO vm) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { - // TODO Auto-generated method stub - } - - @Override - public void finalizeExpunge(ConsoleProxyVO proxy) { - } - - @Override - public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, - ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { - //not supported - throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType()); - } - - - @Override - public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, - ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { - //not supported - throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); - } - - @Override - public void prepareStop(VirtualMachineProfile profile) { - } } diff --git a/server/src/com/cloud/consoleproxy/AgentHookBase.java b/server/src/com/cloud/consoleproxy/AgentHookBase.java new file mode 100644 index 00000000000..b969f6da2ce --- /dev/null +++ b/server/src/com/cloud/consoleproxy/AgentHookBase.java @@ -0,0 +1,266 @@ +// 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.consoleproxy; + +import java.util.Date; +import java.util.Random; +import java.util.UUID; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer; +import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; +import com.cloud.agent.api.ConsoleProxyLoadReportCommand; +import com.cloud.agent.api.GetVncPortAnswer; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupProxyCommand; +import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.keystore.KeystoreManager; +import com.cloud.servlet.ConsoleProxyServlet; +import com.cloud.utils.Ternary; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; + +/** + * Utility class to manage interactions with agent-based console access + * Extracted from ConsoleProxyManagerImpl so that other console proxy managers + * can reuse + */ +public abstract class AgentHookBase implements AgentHook { + private static final Logger s_logger = Logger.getLogger(AgentHookBase.class); + + VMInstanceDao _instanceDao; + HostDao _hostDao; + ConfigurationDao _configDao; + AgentManager _agentMgr; + KeystoreManager _ksMgr; + final Random _random = new Random(System.currentTimeMillis()); + private String _hashKey; + + + public AgentHookBase(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, KeystoreManager ksMgr, + AgentManager agentMgr) { + this._instanceDao = instanceDao; + this._hostDao = hostDao; + this._agentMgr = agentMgr; + this._configDao = cfgDao; + this._ksMgr = ksMgr; + } + + public String getHashKey() { + // although we may have race condition here, database transaction + // serialization should give us the same key + if (_hashKey == null) { + _hashKey = + _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), UUID + .randomUUID().toString()); + } + return _hashKey; + } + + public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) { + Long vmId = null; + + String ticketInUrl = cmd.getTicket(); + if (ticketInUrl == null) { + s_logger.error("Access ticket could not be found, you could be running an old version of console proxy. vmId: " + + cmd.getVmId()); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in url for " + cmd.getHost() + ":" + cmd.getPort() + "-" + + cmd.getVmId() + " is " + ticketInUrl); + } + + if (!cmd.isReauthenticating()) { + String ticket = + ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); + } + + if (!ticket.equals(ticketInUrl)) { + Date now = new Date(); + // considering of minute round-up + String minuteEarlyTicket = + ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), + new Date(now.getTime() - 60 * 1000)); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); + } + + if (!minuteEarlyTicket.equals(ticketInUrl)) { + s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," + + minuteEarlyTicket); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + } + } + + if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Invalid vm id sent from proxy(happens when proxy session has terminated)"); + } + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + VirtualMachine vm = _instanceDao.findByUuid(cmd.getVmId()); + if (vm == null) { + vm = _instanceDao.findById(Long.parseLong(cmd.getVmId())); + } + if (vm == null) { + s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication"); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + if (vm.getHostId() == null) { + s_logger.warn("VM " + vmId + " lost host info, failed authentication request"); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + HostVO host = _hostDao.findById(vm.getHostId()); + if (host == null) { + s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request"); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + String sid = cmd.getSid(); + if (sid == null || !sid.equals(vm.getVncPassword())) { + s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + + if (cmd.isReauthenticating()) { + ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true); + authenticationAnswer.setReauthenticating(true); + + s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info"); + GetVncPortAnswer answer = + (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), + new GetVncPortCommand(vm.getId(), vm.getInstanceName())); + + if (answer != null && answer.getResult()) { + Ternary parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress()); + + if (parsedHostInfo.second() != null && parsedHostInfo.third() != null) { + + s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: " + + parsedHostInfo.second() + ", tunnel session: " + parsedHostInfo.third()); + + authenticationAnswer.setTunnelUrl(parsedHostInfo.second()); + authenticationAnswer.setTunnelSession(parsedHostInfo.third()); + } else { + s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: " + + parsedHostInfo.first() + ", port: " + answer.getPort()); + + authenticationAnswer.setHost(parsedHostInfo.first()); + authenticationAnswer.setPort(answer.getPort()); + } + } else { + s_logger.warn("Re-authentication request failed"); + + authenticationAnswer.setSuccess(false); + } + + return authenticationAnswer; + } + + return new ConsoleAccessAuthenticationAnswer(cmd, true); + } + + public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) { + StartConsoleProxyAgentHttpHandlerCommand cmd = null; + if (_configDao.isPremium()) { + String storePassword = String.valueOf(_random.nextLong()); + byte[] ksBits = + _ksMgr.getKeystoreBits(ConsoleProxyManager.CERTIFICATE_NAME, ConsoleProxyManager.CERTIFICATE_NAME, + storePassword); + + assert (ksBits != null); + if (ksBits == null) { + s_logger.error("Could not find and construct a valid SSL certificate"); + } + cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword); + cmd.setEncryptorPassword(getHashKey()); + } else { + cmd = new StartConsoleProxyAgentHttpHandlerCommand(); + cmd.setEncryptorPassword(getHashKey()); + } + + try { + + HostVO consoleProxyHost = findConsoleProxyHost(startupCmd); + + assert (consoleProxyHost != null); + + Answer answer = _agentMgr.send(consoleProxyHost.getId(), cmd); + if (answer == null || !answer.getResult()) { + s_logger.error("Console proxy agent reported that it failed to execute http handling startup command"); + } else { + s_logger.info("Successfully sent out command to start HTTP handling in console proxy agent"); + } + } catch (AgentUnavailableException e) { + s_logger.error("Unable to send http handling startup command to the console proxy resource for proxy:" + + startupCmd.getProxyVmId(), e); + } catch (OperationTimedoutException e) { + s_logger.error( + "Unable to send http handling startup command(time out) to the console proxy resource for proxy:" + + startupCmd.getProxyVmId(), e); + } catch (OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch (Exception e) { + s_logger.error( + "Unexpected exception when sending http handling startup command(time out) to the console proxy resource for proxy:" + + startupCmd.getProxyVmId(), e); + } + } + + protected abstract HostVO findConsoleProxyHost(StartupProxyCommand cmd); + + @Override + public void onLoadReport(ConsoleProxyLoadReportCommand cmd) { + // no-op since we do not auto-scale + } + + @Override + public void onAgentConnect(HostVO host, StartupCommand cmd) { + // no-op + } + + @Override + public void onAgentDisconnect(long agentId, Status state) { + // no-op since we do not autoscale + } +} diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index fc4fc6eda77..9740d28a94f 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -23,7 +23,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.UUID; import javax.ejb.Local; @@ -35,13 +34,8 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; -import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.Answer; -import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer; -import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; import com.cloud.agent.api.ConsoleProxyLoadReportCommand; -import com.cloud.agent.api.GetVncPortAnswer; -import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupProxyCommand; @@ -49,7 +43,6 @@ import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; -import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; @@ -66,10 +59,8 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; -import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.Host; @@ -106,7 +97,6 @@ import com.cloud.resource.ServerResource; import com.cloud.resource.UnableDeleteHostException; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; -import com.cloud.servlet.ConsoleProxyServlet; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.VMTemplateHostVO; @@ -123,7 +113,6 @@ import com.cloud.user.UserContext; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; @@ -167,7 +156,7 @@ import com.google.gson.GsonBuilder; // @Local(value = { ConsoleProxyManager.class, ConsoleProxyService.class }) public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxyManager, - AgentHook, VirtualMachineGuru, SystemVmLoadScanHandler, ResourceStateAdapter { + VirtualMachineGuru, SystemVmLoadScanHandler, ResourceStateAdapter { private static final Logger s_logger = Logger.getLogger(ConsoleProxyManagerImpl.class); private static final int DEFAULT_CAPACITY_SCAN_INTERVAL = 30000; // 30 seconds @@ -455,7 +444,131 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy private KeystoreDao _ksDao; @Inject private KeystoreManager _ksMgr; - private final Random _random = new Random(System.currentTimeMillis()); + + public class VmBasedAgentHook extends AgentHookBase { + + public VmBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, + KeystoreManager ksMgr, AgentManager agentMgr) { + super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr); + } + + @Override + public void onLoadReport(ConsoleProxyLoadReportCommand cmd) { + if (cmd.getLoadInfo() == null) { + return; + } + + ConsoleProxyStatus status = null; + try { + GsonBuilder gb = new GsonBuilder(); + gb.setVersion(1.3); + Gson gson = gb.create(); + status = gson.fromJson(cmd.getLoadInfo(), ConsoleProxyStatus.class); + } catch (Throwable e) { + s_logger.warn("Unable to parse load info from proxy, proxy vm id : " + cmd.getProxyVmId() + ", info : " + cmd.getLoadInfo()); + } + + if (status != null) { + int count = 0; + if (status.getConnections() != null) { + count = status.getConnections().length; + } + + byte[] details = null; + if (cmd.getLoadInfo() != null) { + details = cmd.getLoadInfo().getBytes(Charset.forName("US-ASCII")); + } + _consoleProxyDao.update(cmd.getProxyVmId(), count, DateUtil.currentGMTTime(), details); + } else { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Unable to get console proxy load info, id : " + cmd.getProxyVmId()); + } + + _consoleProxyDao.update(cmd.getProxyVmId(), 0, DateUtil.currentGMTTime(), null); + } + } + + @Override + public void onAgentConnect(HostVO host, StartupCommand cmd) { + // no-op + } + + @Override + public void onAgentDisconnect(long agentId, com.cloud.host.Status state) { + + if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) { + // be it either in alert or in disconnected state, the agent + // process + // may be gone in the VM, + // we will be reacting to stop the corresponding VM and let the + // scan + // process to + HostVO host = _hostDao.findById(agentId); + if (host.getType() == Type.ConsoleProxy) { + String name = host.getName(); + if (s_logger.isInfoEnabled()) { + s_logger.info("Console proxy agent disconnected, proxy: " + name); + } + if (name != null && name.startsWith("v-")) { + String[] tokens = name.split("-"); + long proxyVmId = 0; + try { + proxyVmId = Long.parseLong(tokens[1]); + } catch (NumberFormatException e) { + s_logger.error("Unexpected exception " + e.getMessage(), e); + return; + } + + final ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyVmId); + if (proxy != null) { + + // Disable this feature for now, as it conflicts + // with + // the case of allowing user to reboot console proxy + // when rebooting happens, we will receive + // disconnect + // here and we can't enter into stopping process, + // as when the rebooted one comes up, it will kick + // off a + // newly started one and trigger the process + // continue on forever + + /* + * _capacityScanScheduler.execute(new Runnable() { + * public void run() { if(s_logger.isInfoEnabled()) + * s_logger.info("Stop console proxy " + + * proxy.getName() + + * " VM because of that the agent running inside it has disconnected" + * ); stopProxy(proxy.getId()); } }); + */ + } else { + if (s_logger.isInfoEnabled()) { + s_logger.info("Console proxy agent disconnected but corresponding console proxy VM no longer exists in DB, proxy: " + + name); + } + } + } else { + assert (false) : "Invalid console proxy name: " + name; + } + } + } + + } + + @Override + protected HostVO findConsoleProxyHost(StartupProxyCommand startupCmd) { + long proxyVmId = startupCmd.getProxyVmId(); + ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId); + if (consoleProxy == null) { + s_logger.info("Proxy " + proxyVmId + " is no longer in DB, skip sending startup command"); + return null; + } + + assert (consoleProxy != null); + return findConsoleProxyHostByName(consoleProxy.getHostName()); + } + + } @Override public ConsoleProxyInfo assignProxy(final long dataCenterId, final long vmId) { @@ -847,181 +960,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy } } - @Override - public void onLoadReport(ConsoleProxyLoadReportCommand cmd) { - if (cmd.getLoadInfo() == null) { - return; - } - ConsoleProxyStatus status = null; - try { - GsonBuilder gb = new GsonBuilder(); - gb.setVersion(1.3); - Gson gson = gb.create(); - status = gson.fromJson(cmd.getLoadInfo(), ConsoleProxyStatus.class); - } catch (Throwable e) { - s_logger.warn("Unable to parse load info from proxy, proxy vm id : " + cmd.getProxyVmId() + ", info : " + cmd.getLoadInfo()); - } - if (status != null) { - int count = 0; - if (status.getConnections() != null) { - count = status.getConnections().length; - } - - byte[] details = null; - if (cmd.getLoadInfo() != null) { - details = cmd.getLoadInfo().getBytes(Charset.forName("US-ASCII")); - } - _consoleProxyDao.update(cmd.getProxyVmId(), count, DateUtil.currentGMTTime(), details); - } else { - if (s_logger.isTraceEnabled()) { - s_logger.trace("Unable to get console proxy load info, id : " + cmd.getProxyVmId()); - } - - _consoleProxyDao.update(cmd.getProxyVmId(), 0, DateUtil.currentGMTTime(), null); - } - } - - @Override - public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) { - Long vmId = null; - - String ticketInUrl = cmd.getTicket(); - if (ticketInUrl == null) { - s_logger.error("Access ticket could not be found, you could be running an old version of console proxy. vmId: " + cmd.getVmId()); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in url for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticketInUrl); - } - - if(!cmd.isReauthenticating()) { - String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); - } - - if (!ticket.equals(ticketInUrl)) { - Date now = new Date(); - // considering of minute round-up - String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000)); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); - } - - if (!minuteEarlyTicket.equals(ticketInUrl)) { - s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," - + minuteEarlyTicket); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - } - } - - if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Invalid vm id sent from proxy(happens when proxy session has terminated)"); - } - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - VirtualMachine vm = _instanceDao.findByUuid(cmd.getVmId()); - if (vm == null) { - vm = _instanceDao.findById(Long.parseLong(cmd.getVmId())); - } - if (vm == null) { - s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - if (vm.getHostId() == null) { - s_logger.warn("VM " + vmId + " lost host info, failed authentication request"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - HostVO host = _hostDao.findById(vm.getHostId()); - if (host == null) { - s_logger.warn("VM " + vmId + "'s host does not exist, fail authentication request"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - String sid = cmd.getSid(); - if (sid == null || !sid.equals(vm.getVncPassword())) { - s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - if(cmd.isReauthenticating()) { - ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true); - authenticationAnswer.setReauthenticating(true); - - s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info"); - GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new - GetVncPortCommand(vm.getId(), vm.getInstanceName())); - - if (answer != null && answer.getResult()) { - Ternary parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress()); - - if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) { - - s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: " + parsedHostInfo.second() - + ", tunnel session: " + parsedHostInfo.third()); - - authenticationAnswer.setTunnelUrl(parsedHostInfo.second()); - authenticationAnswer.setTunnelSession(parsedHostInfo.third()); - } else { - s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: " + parsedHostInfo.first() - + ", port: " + answer.getPort()); - - authenticationAnswer.setHost(parsedHostInfo.first()); - authenticationAnswer.setPort(answer.getPort()); - } - } else { - s_logger.warn("Re-authentication request failed"); - - authenticationAnswer.setSuccess(false); - } - - return authenticationAnswer; - } - - return new ConsoleAccessAuthenticationAnswer(cmd, true); - } - - @Override - public void onAgentConnect(HostVO host, StartupCommand cmd) { - // if (host.getType() == Type.ConsoleProxy) { - // // TODO we can use this event to mark the proxy is up and - // // functioning instead of - // // pinging the console proxy VM command port - // // - // // for now, just log a message - // if (s_logger.isInfoEnabled()) { - // s_logger.info("Console proxy agent is connected. proxy: " + host.getName()); - // } - // - // /* update public/private ip address */ - // if (_IpAllocator != null && _IpAllocator.exteralIpAddressAllocatorEnabled()) { - // try { - // ConsoleProxyVO console = findConsoleProxyByHost(host); - // if (console == null) { - // s_logger.debug("Can't find console proxy "); - // return; - // } - // console.setPrivateIpAddress(cmd.getPrivateIpAddress()); - // console.setPublicIpAddress(cmd.getPublicIpAddress()); - // console.setPublicNetmask(cmd.getPublicNetmask()); - // _consoleProxyDao.persist(console); - // } catch (NumberFormatException e) { - // } - // } - // } - } - - @Override - public void onAgentDisconnect(long agentId, com.cloud.host.Status state) { + public void handleAgentDisconnect(long agentId, com.cloud.host.Status state) { if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) { // be it either in alert or in disconnected state, the agent process // may be gone in the VM, @@ -1496,7 +1437,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy value = agentMgrConfigs.get("port"); _mgmt_port = NumbersUtil.parseInt(value, 8250); - _listener = new ConsoleProxyListener(this); + _listener = + new ConsoleProxyListener(new VmBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, + _agentMgr)); _agentMgr.registerForHostEvents(_listener, true, true, false); _itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this); @@ -1719,52 +1662,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy _consoleProxyDao.update(proxy.getId(), proxy); } - @Override - public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) { - StartConsoleProxyAgentHttpHandlerCommand cmd = null; - if (_configDao.isPremium()) { - String storePassword = String.valueOf(_random.nextLong()); - byte[] ksBits = _ksMgr.getKeystoreBits(ConsoleProxyManager.CERTIFICATE_NAME, ConsoleProxyManager.CERTIFICATE_NAME, storePassword); - assert (ksBits != null); - if (ksBits == null) { - s_logger.error("Could not find and construct a valid SSL certificate"); - } - cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword); - cmd.setEncryptorPassword(getHashKey()); - } else { - cmd = new StartConsoleProxyAgentHttpHandlerCommand(); - cmd.setEncryptorPassword(getHashKey()); - } - - try { - long proxyVmId = startupCmd.getProxyVmId(); - ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(proxyVmId); - if (consoleProxy == null) { - s_logger.info("Proxy " + proxyVmId + " is no longer in DB, skip sending startup command"); - return; - } - - assert (consoleProxy != null); - HostVO consoleProxyHost = findConsoleProxyHostByName(consoleProxy.getHostName()); - - Answer answer = _agentMgr.send(consoleProxyHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("Console proxy agent reported that it failed to execute http handling startup command"); - } else { - s_logger.info("Successfully sent out command to start HTTP handling in console proxy agent"); - } - } catch (AgentUnavailableException e) { - s_logger.error("Unable to send http handling startup command to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e); - } catch (OperationTimedoutException e) { - s_logger.error("Unable to send http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e); - } catch (OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch (Exception e) { - s_logger.error("Unexpected exception when sending http handling startup command(time out) to the console proxy resource for proxy:" + startupCmd.getProxyVmId(), e); - } - } @Override public ConsoleProxyVO persist(ConsoleProxyVO proxy) { diff --git a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java index 3ba98a94362..7b59a6bf45e 100755 --- a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java @@ -16,29 +16,59 @@ // under the License. package com.cloud.consoleproxy; + import java.util.List; import java.util.Map; +import java.util.Random; import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.springframework.stereotype.Component; +import org.apache.log4j.Logger; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupProxyCommand; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyInfo; +import com.cloud.keystore.KeystoreDao; +import com.cloud.keystore.KeystoreManager; import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.NumbersUtil; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.ConsoleProxyDao; +import com.cloud.vm.dao.VMInstanceDao; @Local(value={ConsoleProxyManager.class}) -public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager implements ConsoleProxyManager { - String _ip = null; - @Inject ConsoleProxyDao _proxyDao; - @Inject ResourceManager _resourceMgr; - @Inject ConfigurationDao _configDao; +public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager implements ConsoleProxyManager, + ResourceStateAdapter { + private static final Logger s_logger = Logger.getLogger(StaticConsoleProxyManager.class); + + @Inject + ConsoleProxyDao _proxyDao; + @Inject + ResourceManager _resourceMgr; + @Inject + ConfigurationDao _configDao; + @Inject + private VMInstanceDao _instanceDao; + @Inject + KeystoreDao _ksDao; + @Inject + private KeystoreManager _ksMgr; + @Inject + private HostDao _hostDao; + private final Random _random = new Random(System.currentTimeMillis()); + private String _hashKey; + private String _ip = null; + + @Override protected HostVO findHost(VMInstanceVO vm) { @@ -50,20 +80,52 @@ public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager imp @Override public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId) { - return new ConsoleProxyInfo(false, _ip, _consoleProxyPort, _consoleProxyUrlPort, _consoleProxyUrlDomain); + return new ConsoleProxyInfo(_sslEnabled, _ip, _consoleProxyPort, _consoleProxyUrlPort, _consoleProxyUrlDomain); } @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - - Map dbParams = _configDao.getConfiguration("ManagementServer", params); - - _ip = dbParams.get("public.ip"); + _ip = _configDao.getValue("consoleproxy.static.publicIp"); if (_ip == null) { _ip = "127.0.0.1"; } + + String value = (String) params.get("consoleproxy.sslEnabled"); + if (value != null && value.equalsIgnoreCase("true")) { + _sslEnabled = true; + } + int defaultPort = 8088; + if (_sslEnabled) + defaultPort = 8443; + _consoleProxyUrlPort = NumbersUtil.parseInt(_configDao.getValue("consoleproxy.static.port"), defaultPort); + + _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + if (!(cmd[0] instanceof StartupProxyCommand)) { + return null; + } + + host.setType(com.cloud.host.Host.Type.ConsoleProxy); + return host; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, + Map details, List hostTags) { + return null; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) + throws UnableDeleteHostException { + return null; + } + } From 21b4635948152710935ba420cee50b823fd7a2b4 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 26 Mar 2013 10:06:46 -0700 Subject: [PATCH 39/81] QuickCloud: also look for consoleproxy.properties using PropertiesUtil. When executed with mvn exec:java, the path 'conf' is not on the classpath --- .../src/com/cloud/consoleproxy/ConsoleProxy.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java index b5c29892a7b..2abce565856 100644 --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java @@ -17,6 +17,8 @@ package com.cloud.consoleproxy; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -34,6 +36,7 @@ import org.apache.commons.codec.binary.Base64; import org.apache.log4j.xml.DOMConfigurator; import com.cloud.consoleproxy.util.Logger; +import com.cloud.utils.PropertiesUtil; import com.google.gson.Gson; import com.sun.net.httpserver.HttpServer; @@ -282,8 +285,17 @@ public class ConsoleProxy { InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); Properties props = new Properties(); if (confs == null) { - s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); - } else { + final File file = PropertiesUtil.findConfigFile("consoleproxy.properties"); + if (file == null) + s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); + else + try { + confs = new FileInputStream(file); + } catch (FileNotFoundException e) { + s_logger.info("Ignoring file not found exception and using defaults"); + } + } + if (confs != null) { try { props.load(confs); From 1d70b9ea77fd1e9fce98f7d9cd5fd92cfe444c39 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 26 Mar 2013 13:36:04 -0700 Subject: [PATCH 40/81] QuickCloud: add a network offering without any services This can be used to avoid starting up a virtual router simply for the purposes of offering dhcp and dns services With the QuickCloudNoServices offering, no virtual router will be started up and the vm instance will not get a CloudStack-assigned IP address. Instead, the VM will simply get whatever IP address is offered by an DHCP service that happens to be running in the same network --- api/src/com/cloud/offering/NetworkOffering.java | 1 + .../com/cloud/network/NetworkManagerImpl.java | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/api/src/com/cloud/offering/NetworkOffering.java b/api/src/com/cloud/offering/NetworkOffering.java index 8cb82996036..bd14acd4718 100644 --- a/api/src/com/cloud/offering/NetworkOffering.java +++ b/api/src/com/cloud/offering/NetworkOffering.java @@ -46,6 +46,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, public final static String SystemPrivateGatewayNetworkOffering = "System-Private-Gateway-Network-Offering"; public final static String DefaultSharedNetworkOfferingWithSGService = "DefaultSharedNetworkOfferingWithSGService"; + public final static String QuickCloudNoServices = "QuickCloudNoServices"; public final static String DefaultIsolatedNetworkOfferingWithSourceNatService = "DefaultIsolatedNetworkOfferingWithSourceNatService"; public final static String OvsIsolatedNetworkOfferingWithSourceNatService = "OvsIsolatedNetworkOfferingWithSourceNatService"; public final static String DefaultSharedNetworkOffering = "DefaultSharedNetworkOffering"; diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 62960116dce..a97f2ce13e4 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -968,9 +968,21 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L // diff between offering #1 and #2 - securityGroup is enabled for the first, and disabled for the third NetworkOfferingVO offering = null; + if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) { + offering = + _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, + "Offering for QuickCloud with no services", TrafficType.Guest, null, true, + Availability.Optional, null, new HashMap>(), true, + Network.GuestType.Shared, false, null, true, null, true, false); + offering.setState(NetworkOffering.State.Enabled); + _networkOfferingDao.update(offering.getId(), offering); + } if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) { - offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", TrafficType.Guest, null, - true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false); + offering = + _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, + "Offering for Shared Security group enabled networks", TrafficType.Guest, null, true, + Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, + Network.GuestType.Shared, false, null, true, null, true, false); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } From a806ce43d32e8d5ac064b79dd623c01be4489126 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 26 Mar 2013 14:11:36 -0700 Subject: [PATCH 41/81] QuickCloud: allow specification of network offering name in datacenter config. The deployDatacenter python script will use this to pick the network offering during creation of the default network in a basic zone --- tools/devcloud/quickcloud.cfg | 1 + tools/marvin/marvin/deployDataCenter.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/devcloud/quickcloud.cfg b/tools/devcloud/quickcloud.cfg index 0e1fb4fb208..a2613d22bdb 100644 --- a/tools/devcloud/quickcloud.cfg +++ b/tools/devcloud/quickcloud.cfg @@ -54,6 +54,7 @@ "securitygroupenabled": "true", "localstorageenabled": "true", "networktype": "Basic", + "networkofferingname": "QuickCloudNoServices", "pods": [ { "endip": "192.168.56.220", diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index 42bc5f9ebef..d3653958fdd 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -299,10 +299,11 @@ class deployDataCenters(): if zone.networktype == "Basic": listnetworkoffering = listNetworkOfferings.listNetworkOfferingsCmd() - listnetworkoffering.name = "DefaultSharedNetscalerEIPandELBNetworkOffering" \ if len(filter(lambda x : x.typ == 'Public', zone.physical_networks[0].traffictypes)) > 0 \ else "DefaultSharedNetworkOfferingWithSGService" + if zone.networkofferingname is not None: + listnetworkoffering.name = zone.networkofferingname listnetworkofferingresponse = \ self.apiClient.listNetworkOfferings(listnetworkoffering) From c5b11df6b78dd755acc4141dc2063608e581996d Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 26 Mar 2013 16:40:19 -0700 Subject: [PATCH 42/81] QuickCloud: option to build with componentContext tailored for quickcloud For now it replaces ConsoleProxyManagerImpl with StaticConsoleProxyManager Usage: mvn install -Dquickcloud QuickCloud: rename deploy profile QuickCloud: remove cyclic dependency introduced in nonoss build by moving SecondaryStorageDiscoverer into services However with this fix, developers will be unable to run 'PremiumSecondaryStorageResource' (for VMWare installations) using mvn exec:java. Instead they will have to use the exploded archive from systemvm.zip --- client/pom.xml | 16 + .../quickCloudComponentContext.xml.in | 336 ++++++++++++++++++ plugins/hypervisors/simulator/pom.xml | 5 + .../SimulatorSecondaryDiscoverer.java | 3 +- plugins/hypervisors/vmware/pom.xml | 5 + server/pom.xml | 5 - services/console-proxy/server/pom.xml | 10 +- services/secondary-storage/pom.xml | 28 +- .../resource}/SecondaryStorageDiscoverer.java | 4 +- tools/devcloud/pom.xml | 4 +- 10 files changed, 376 insertions(+), 40 deletions(-) create mode 100644 client/tomcatconf/quickCloudComponentContext.xml.in rename {server/src/com/cloud/storage/secondary => services/secondary-storage/src/org/apache/cloudstack/storage/resource}/SecondaryStorageDiscoverer.java (98%) diff --git a/client/pom.xml b/client/pom.xml index 9323d0fb20f..4bd1a6fedaa 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -453,6 +453,22 @@ + + process-quickcloud-spring-context + process-resources + + run + + + + quickcloud + + + + diff --git a/client/tomcatconf/quickCloudComponentContext.xml.in b/client/tomcatconf/quickCloudComponentContext.xml.in new file mode 100644 index 00000000000..dbd31173644 --- /dev/null +++ b/client/tomcatconf/quickCloudComponentContext.xml.in @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/hypervisors/simulator/pom.xml b/plugins/hypervisors/simulator/pom.xml index ff1664ad85f..e4ca9272853 100644 --- a/plugins/hypervisors/simulator/pom.xml +++ b/plugins/hypervisors/simulator/pom.xml @@ -40,5 +40,10 @@ cloud-utils ${project.version} + + org.apache.cloudstack + cloud-secondary-storage + ${project.version} + diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java index 3a8cf17e24b..c121fbac5d9 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java @@ -24,6 +24,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.resource.SecondaryStorageDiscoverer; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -40,9 +41,7 @@ import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.secondary.SecondaryStorageDiscoverer; import com.cloud.utils.exception.CloudRuntimeException; -import org.springframework.stereotype.Component; @Local(value=Discoverer.class) diff --git a/plugins/hypervisors/vmware/pom.xml b/plugins/hypervisors/vmware/pom.xml index 468e0a50599..d65ef640655 100644 --- a/plugins/hypervisors/vmware/pom.xml +++ b/plugins/hypervisors/vmware/pom.xml @@ -32,6 +32,11 @@ cloud-vmware-base ${project.version} + + org.apache.cloudstack + cloud-secondary-storage + ${project.version} + com.cloud.com.vmware vmware-vim25 diff --git a/server/pom.xml b/server/pom.xml index 8a6a10c591e..a3971954475 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -31,11 +31,6 @@ cloud-core ${project.version} - - org.apache.cloudstack - cloud-secondary-storage - ${project.version} - javax.servlet servlet-api diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index fd7b964bcab..3d149a5c724 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -57,6 +57,11 @@ ${project.version} pom + + org.apache.cloudstack + cloud-secondary-storage + ${project.version} + install @@ -256,11 +261,6 @@ quickcloud - - - quickcloud - - diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml index d8dbf1dd250..05770848987 100644 --- a/services/secondary-storage/pom.xml +++ b/services/secondary-storage/pom.xml @@ -54,6 +54,11 @@ ${project.version} pom + + org.apache.cloudstack + cloud-server + ${project.version} + install @@ -87,27 +92,4 @@ - - - vmware - - - nonoss - - - - - org.apache.cloudstack - cloud-plugin-hypervisor-vmware - ${project.version} - - - org.apache.cloudstack - cloud-vmware-base - ${project.version} - - - - - diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageDiscoverer.java similarity index 98% rename from server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java rename to services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageDiscoverer.java index 6e66e0de8f1..d3af792faa5 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageDiscoverer.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageDiscoverer.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.storage.secondary; +package org.apache.cloudstack.storage.resource; import java.io.File; import java.lang.reflect.Constructor; @@ -30,8 +30,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.storage.resource.LocalSecondaryStorageResource; -import org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; diff --git a/tools/devcloud/pom.xml b/tools/devcloud/pom.xml index d32d84bd691..93029e1332a 100644 --- a/tools/devcloud/pom.xml +++ b/tools/devcloud/pom.xml @@ -143,10 +143,10 @@ - quicksvr + quickcloud - quicksvr + quickcloud From 936973aff7ba372abe442a7a40e112219fba7570 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Wed, 27 Mar 2013 16:49:56 -0700 Subject: [PATCH 43/81] QuickCloud: launch scripts for secondary storage and console proxy outside system vm QuickCloud: remove stuff added by the daemon --- services/console-proxy/server/scripts/_run.sh | 3 +- .../server/scripts/consoleproxy.sh | 33 +++++++++++++++++++ .../secondary-storage/conf/agent.properties | 4 +-- services/secondary-storage/scripts/_run.sh | 3 +- .../secondary-storage/scripts/secstorage.sh | 33 +++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100755 services/console-proxy/server/scripts/consoleproxy.sh create mode 100755 services/secondary-storage/scripts/secstorage.sh diff --git a/services/console-proxy/server/scripts/_run.sh b/services/console-proxy/server/scripts/_run.sh index e408378afbc..bd063468cfb 100755 --- a/services/console-proxy/server/scripts/_run.sh +++ b/services/console-proxy/server/scripts/_run.sh @@ -35,6 +35,7 @@ do CP=${CP}:$file done keyvalues= +LOGHOME=/var/log/cloud/ CMDLINE=$(cat /var/cache/cloud/cmdline) @@ -60,4 +61,4 @@ then maxmem=$eightypcnt fi -java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/console-proxy/server/scripts/consoleproxy.sh b/services/console-proxy/server/scripts/consoleproxy.sh new file mode 100755 index 00000000000..4a6acdee299 --- /dev/null +++ b/services/console-proxy/server/scripts/consoleproxy.sh @@ -0,0 +1,33 @@ +#!/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. + + + +#runs the console proxy as a standalone server +#i.e., not in the system vm + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done +keyvalues= +#LOGHOME=/var/log/cloud/ +LOGHOME=$PWD + +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/secondary-storage/conf/agent.properties b/services/secondary-storage/conf/agent.properties index 4a25cbd8611..554007f90af 100644 --- a/services/secondary-storage/conf/agent.properties +++ b/services/secondary-storage/conf/agent.properties @@ -1,6 +1,4 @@ -#Storage -#Sun Mar 24 22:52:35 PDT 2013 -mount.path=/Users/chiradeep/secondary-storage +#mount.path=~/secondary-storage eth1ip=192.168.56.1 name=192.168.56.10 eth2ip=192.168.56.10 diff --git a/services/secondary-storage/scripts/_run.sh b/services/secondary-storage/scripts/_run.sh index e408378afbc..cb9624c58e6 100755 --- a/services/secondary-storage/scripts/_run.sh +++ b/services/secondary-storage/scripts/_run.sh @@ -36,6 +36,7 @@ do done keyvalues= +LOGHOME=/var/log/cloud/ CMDLINE=$(cat /var/cache/cloud/cmdline) #CMDLINE="graphical utf8 eth0ip=0.0.0.0 eth0mask=255.255.255.0 eth1ip=192.168.140.40 eth1mask=255.255.255.0 eth2ip=172.24.0.50 eth2mask=255.255.0.0 gateway=172.24.0.1 dns1=72.52.126.11 template=domP dns2=72.52.126.12 host=192.168.1.142 port=8250 mgmtcidr=192.168.1.0/24 localgw=192.168.140.1 zone=5 pod=5" @@ -60,4 +61,4 @@ then maxmem=$eightypcnt fi -java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/secondary-storage/scripts/secstorage.sh b/services/secondary-storage/scripts/secstorage.sh new file mode 100755 index 00000000000..9afc5215dee --- /dev/null +++ b/services/secondary-storage/scripts/secstorage.sh @@ -0,0 +1,33 @@ +#!/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. + + + +#runs the secondary storage service as a standalone server +#i.e., not in the system vm + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done +keyvalues= +#LOGHOME=/var/log/cloud/ +LOGHOME=$PWD + +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -cp $CP com.cloud.agent.AgentShell $keyvalues $@ From 5ff8bcaa2e16035197c6d58bb212ba1696411dce Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Thu, 4 Apr 2013 17:23:10 -0700 Subject: [PATCH 44/81] QuickCloud: when using SSVM, pass in the new name of the SS class (com.cloud -> org.apache.cloudstack) QuickCloud : configuration moved to applicationContext.xml from componentContext.xml QuickCloud: default to enabled state for devcloud zone QuickCloud: environment.properties helps customize location of pid file --- client/pom.xml | 6 +- .../quickCloudComponentContext.xml.in | 336 ------------------ .../SecondaryStorageManagerImpl.java | 8 +- .../server/conf/environment.properties | 2 + tools/devcloud/devcloud.cfg | 1 + 5 files changed, 9 insertions(+), 344 deletions(-) delete mode 100644 client/tomcatconf/quickCloudComponentContext.xml.in create mode 100644 services/console-proxy/server/conf/environment.properties diff --git a/client/pom.xml b/client/pom.xml index 4bd1a6fedaa..b3d7acb08a8 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -463,9 +463,9 @@ quickcloud + file="${basedir}/target/generated-webapp/WEB-INF/classes/applicationContext.xml" + match="com.cloud.consoleproxy.ConsoleProxyManagerImpl" + replace="com.cloud.consoleproxy.StaticConsoleProxyManager" byline="true" /> diff --git a/client/tomcatconf/quickCloudComponentContext.xml.in b/client/tomcatconf/quickCloudComponentContext.xml.in deleted file mode 100644 index dbd31173644..00000000000 --- a/client/tomcatconf/quickCloudComponentContext.xml.in +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index c94224b264c..d6d6fc0eb69 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -30,8 +30,6 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -98,8 +96,8 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateHostVO; -import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; @@ -1066,10 +1064,10 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar buf.append(" resource=com.cloud.storage.resource.PremiumSecondaryStorageResource"); } else { s_logger.debug("Telling the ssvm to load the NfsSecondaryStorageResource"); - buf.append(" resource=com.cloud.storage.resource.NfsSecondaryStorageResource"); + buf.append(" resource=org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource"); } } else { - buf.append(" resource=com.cloud.storage.resource.NfsSecondaryStorageResource"); + buf.append(" resource=org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource"); } buf.append(" instance=SecStorage"); buf.append(" sslcopy=").append(Boolean.toString(_useSSlCopy)); diff --git a/services/console-proxy/server/conf/environment.properties b/services/console-proxy/server/conf/environment.properties new file mode 100644 index 00000000000..269acad9152 --- /dev/null +++ b/services/console-proxy/server/conf/environment.properties @@ -0,0 +1,2 @@ +paths.script=../../scripts/storage/secondary/ +paths.pid=. diff --git a/tools/devcloud/devcloud.cfg b/tools/devcloud/devcloud.cfg index c41f8bcef58..e6ab71b5ebf 100644 --- a/tools/devcloud/devcloud.cfg +++ b/tools/devcloud/devcloud.cfg @@ -20,6 +20,7 @@ "zones": [ { "name": "DevCloud0", + "enabled" : "True", "physical_networks": [ { "broadcastdomainrange": "Zone", From 778a59fbf6bc8957771718123079bd8a2707affa Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 5 Apr 2013 14:25:44 -0700 Subject: [PATCH 45/81] QuickCloud: move devcloud configuration into pom profile for exec:java Also ignore agent.properties in RAT since a. they are trivial b. they are modified by the java code (stripping the license) QuickCloud: proper path for log.home QuickCloud: proper path for secstorage.sh --- pom.xml | 4 ++++ .../console-proxy/server/conf/agent.properties | 17 ----------------- services/console-proxy/server/pom.xml | 3 +++ .../server/scripts/consoleproxy.sh | 2 +- .../server}/scripts/secstorage.sh | 2 +- .../secondary-storage/conf/agent.properties | 9 +-------- services/secondary-storage/pom.xml | 8 ++++++++ 7 files changed, 18 insertions(+), 27 deletions(-) rename services/{secondary-storage => console-proxy/server}/scripts/secstorage.sh (98%) diff --git a/pom.xml b/pom.xml index 62081241445..dbc39076f2d 100644 --- a/pom.xml +++ b/pom.xml @@ -317,6 +317,10 @@ deps/XenServerJava/Makefile dist/console-proxy/js/jquery.js scripts/vm/systemvm/id_rsa.cloud + services/console-proxy/server/conf/agent.properties + services/console-proxy/server/conf/environment.properties + services/secondary-storage/conf/agent.properties + services/secondary-storage/conf/environment.properties tools/devcloud/basebuild/puppet-devcloudinitial/files/network.conf tools/appliance/definitions/devcloud/* tools/appliance/definitions/systemvmtemplate/* diff --git a/services/console-proxy/server/conf/agent.properties b/services/console-proxy/server/conf/agent.properties index 4e217f21100..246cb1c3d08 100644 --- a/services/console-proxy/server/conf/agent.properties +++ b/services/console-proxy/server/conf/agent.properties @@ -1,19 +1,2 @@ -# 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. - instance=ConsoleProxy resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index 3d149a5c724..3ac5d5957f7 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -280,11 +280,14 @@ zone=1 pod=1 host=192.168.56.1 + guid=ConsoleProxy.1 javax.net.ssl.trustStore certs/realhostip.keystore + log.home + ${PWD}/ diff --git a/services/console-proxy/server/scripts/consoleproxy.sh b/services/console-proxy/server/scripts/consoleproxy.sh index 4a6acdee299..294d5974bb5 100755 --- a/services/console-proxy/server/scripts/consoleproxy.sh +++ b/services/console-proxy/server/scripts/consoleproxy.sh @@ -28,6 +28,6 @@ do done keyvalues= #LOGHOME=/var/log/cloud/ -LOGHOME=$PWD +LOGHOME=$PWD/ java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/secondary-storage/scripts/secstorage.sh b/services/console-proxy/server/scripts/secstorage.sh similarity index 98% rename from services/secondary-storage/scripts/secstorage.sh rename to services/console-proxy/server/scripts/secstorage.sh index 9afc5215dee..b45afc2e8ca 100755 --- a/services/secondary-storage/scripts/secstorage.sh +++ b/services/console-proxy/server/scripts/secstorage.sh @@ -28,6 +28,6 @@ do done keyvalues= #LOGHOME=/var/log/cloud/ -LOGHOME=$PWD +LOGHOME=$PWD/ java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Dlog.home=$LOGHOME -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/secondary-storage/conf/agent.properties b/services/secondary-storage/conf/agent.properties index 554007f90af..aab82b63374 100644 --- a/services/secondary-storage/conf/agent.properties +++ b/services/secondary-storage/conf/agent.properties @@ -1,9 +1,2 @@ -#mount.path=~/secondary-storage -eth1ip=192.168.56.1 -name=192.168.56.10 -eth2ip=192.168.56.10 +#mount.path=~/secondary-storage/ resource=org.apache.cloudstack.storage.resource.NfsSecondaryStorageResource -piddir=. -instance=SecondaryStorage -developer=true -secondary.storage.vm=false diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml index 05770848987..2c8a1d0b9b8 100644 --- a/services/secondary-storage/pom.xml +++ b/services/secondary-storage/pom.xml @@ -81,11 +81,19 @@ zone=1 pod=1 host=192.168.56.1 + name=192.168.56.10 + eth1ip=192.168.56.10 + eth2ip=192.168.56.10 + guid=SecondaryStorage.1 + secondary.storage.vm=false + instance=Secondary javax.net.ssl.trustStore certs/realhostip.keystore + log.home + ${PWD}/ From 271d232d620f27c6b8b10bc85f849563c528e3ae Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 5 Apr 2013 11:52:52 -0700 Subject: [PATCH 46/81] QuickCloud: we moved the SecondaryStorageDiscoverer from the com.cloud package to the org.apache.cloudstack package QuickCloud: fix license issue QuickCloud: use a different activation for deploying quickcloud Otherwise marvin gets invoked during mvn install -Dquickcloud --- client/tomcatconf/applicationContext.xml.in | 2 +- .../cloud/consoleproxy/ConsoleProxyService.java | 16 ++++++++++++++++ tools/devcloud/pom.xml | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 3b51a83e6ad..67b1286eaed 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -503,7 +503,7 @@ - + diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyService.java b/server/src/com/cloud/consoleproxy/ConsoleProxyService.java index e43e5c315e2..fd00f56333a 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyService.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyService.java @@ -1,3 +1,19 @@ +// 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.consoleproxy; import com.cloud.info.ConsoleProxyInfo; diff --git a/tools/devcloud/pom.xml b/tools/devcloud/pom.xml index 93029e1332a..ba4cc464ccc 100644 --- a/tools/devcloud/pom.xml +++ b/tools/devcloud/pom.xml @@ -146,7 +146,7 @@ quickcloud - quickcloud + deployquick @@ -157,7 +157,7 @@ 1.2.1 - package + integration-test exec From 2e6c65fd34dc5f4f885c12a4e5469b505975685d Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 5 Apr 2013 14:11:05 -0700 Subject: [PATCH 47/81] QuickCloud: sanitize logs for normal running of agents outside systemvm --- .../consoleproxy/ConsoleProxyResource.java | 4 +- .../cloud/resource/ServerResourceBase.java | 2 +- .../AgentBasedConsoleProxyManager.java.orig | 298 ++++++++++++++++++ .../PremiumSecondaryStorageManagerImpl.java | 6 +- .../storage/download/DownloadMonitorImpl.java | 11 +- .../SecondaryStorageManagerImpl.java | 2 +- .../resource/NfsSecondaryStorageResource.java | 6 +- utils/src/com/cloud/utils/nio/NioClient.java | 3 +- .../com/cloud/utils/nio/NioConnection.java | 2 +- 9 files changed, 320 insertions(+), 14 deletions(-) create mode 100755 server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java.orig diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index 516430b2fed..991764c53f8 100644 --- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java +++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java @@ -235,14 +235,14 @@ public class ConsoleProxyResource extends ServerResourceBase implements if (_eth1ip != null) { params.put("private.network.device", "eth1"); } else { - s_logger.warn("WARNING: eth1ip parameter is not found!"); + s_logger.info("eth1ip parameter has not been configured, assuming that we are not inside a system vm"); } String eth2ip = (String) params.get("eth2ip"); if (eth2ip != null) { params.put("public.network.device", "eth2"); } else { - s_logger.warn("WARNING: eth2ip parameter is not found!"); + s_logger.info("eth2ip parameter is not found, assuming that we are not inside a system vm"); } super.configure(name, params); diff --git a/core/src/com/cloud/resource/ServerResourceBase.java b/core/src/com/cloud/resource/ServerResourceBase.java index 9449b058556..e381fcbec86 100755 --- a/core/src/com/cloud/resource/ServerResourceBase.java +++ b/core/src/com/cloud/resource/ServerResourceBase.java @@ -80,7 +80,7 @@ public abstract class ServerResourceBase implements ServerResource { _storageNic2 = getNetworkInterface(storageNic2); if (_privateNic == null) { - s_logger.error("Nics are not configured!"); + s_logger.warn("Nics are not specified in properties file/db, will try to autodiscover"); Enumeration nics = null; try { diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java.orig b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java.orig new file mode 100755 index 00000000000..134d59d0065 --- /dev/null +++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java.orig @@ -0,0 +1,298 @@ +// 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.consoleproxy; + +import java.util.Map; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.GetVncPortAnswer; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.StartupProxyCommand; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.info.ConsoleProxyInfo; +import com.cloud.keystore.KeystoreManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.component.ManagerBase; +import com.cloud.vm.ConsoleProxyVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.ConsoleProxyDao; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = { ConsoleProxyManager.class }) +public class AgentBasedConsoleProxyManager extends ManagerBase implements ConsoleProxyManager { + private static final Logger s_logger = Logger.getLogger(AgentBasedConsoleProxyManager.class); + + @Inject + protected HostDao _hostDao; + @Inject + protected UserVmDao _userVmDao; + private String _instance; + protected String _consoleProxyUrlDomain; + @Inject + private VMInstanceDao _instanceDao; + private ConsoleProxyListener _listener; + protected int _consoleProxyUrlPort = ConsoleProxyManager.DEFAULT_PROXY_URL_PORT; + protected int _consoleProxyPort = ConsoleProxyManager.DEFAULT_PROXY_VNC_PORT; + protected boolean _sslEnabled = false; + @Inject + AgentManager _agentMgr; + @Inject + VirtualMachineManager _itMgr; + @Inject + protected ConsoleProxyDao _cpDao; + @Inject + protected KeystoreManager _ksMgr; + + @Inject ConfigurationDao _configDao; + + public class AgentBasedAgentHook extends AgentHookBase { + + public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, + KeystoreManager ksMgr, AgentManager agentMgr) { + super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr); + } + + @Override + protected HostVO findConsoleProxyHost(StartupProxyCommand cmd) { + return _hostDao.findByGuid(cmd.getGuid()); + } + + } + + public int getVncPort(VMInstanceVO vm) { + if (vm.getHostId() == null) { + return -1; + } + GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new GetVncPortCommand(vm.getId(), vm.getHostName())); + return (answer == null || !answer.getResult()) ? -1 : answer.getPort(); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + + if (s_logger.isInfoEnabled()) { + s_logger.info("Start configuring AgentBasedConsoleProxyManager"); + } + + Map configs = _configDao.getConfiguration("management-server", params); + String value = configs.get("consoleproxy.url.port"); + if (value != null) { + _consoleProxyUrlPort = NumbersUtil.parseInt(value, ConsoleProxyManager.DEFAULT_PROXY_URL_PORT); + } + + value = configs.get("consoleproxy.port"); + if (value != null) { + _consoleProxyPort = NumbersUtil.parseInt(value, ConsoleProxyManager.DEFAULT_PROXY_VNC_PORT); + } + + value = configs.get("consoleproxy.sslEnabled"); + if (value != null && value.equalsIgnoreCase("true")) { + _sslEnabled = true; + } + + _instance = configs.get("instance.name"); + + _consoleProxyUrlDomain = configs.get("consoleproxy.url.domain"); + + _listener = + new ConsoleProxyListener(new AgentBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, _agentMgr)); + _agentMgr.registerForHostEvents(_listener, true, true, false); + + if (s_logger.isInfoEnabled()) { + s_logger.info("AgentBasedConsoleProxyManager has been configured. SSL enabled: " + _sslEnabled); + } + return true; + } + + HostVO findHost(VMInstanceVO vm) { + return _hostDao.findById(vm.getHostId()); + } + + @Override + public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId) { + UserVmVO userVm = _userVmDao.findById(userVmId); + if (userVm == null) { + s_logger.warn("User VM " + userVmId + " no longer exists, return a null proxy for user vm:" + userVmId); + return null; + } + + HostVO host = findHost(userVm); + if (host != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Assign embedded console proxy running at " + host.getName() + " to user vm " + userVmId + " with public IP " + + host.getPublicIpAddress()); + } + + // only private IP, public IP, host id have meaningful values, rest + // of all are place-holder values + String publicIp = host.getPublicIpAddress(); + if (publicIp == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host " + host.getName() + "/" + host.getPrivateIpAddress() + + " does not have public interface, we will return its private IP for cosole proxy."); + } + publicIp = host.getPrivateIpAddress(); + } + + int urlPort = _consoleProxyUrlPort; + + if (host.getProxyPort() != null && host.getProxyPort().intValue() > 0) { + urlPort = host.getProxyPort().intValue(); + } + + return new ConsoleProxyInfo(_sslEnabled, publicIp, _consoleProxyPort, urlPort, _consoleProxyUrlDomain); + } else { + s_logger.warn("Host that VM is running is no longer available, console access to VM " + userVmId + " will be temporarily unavailable."); + } + return null; + } + + + + + @Override + public ConsoleProxyVO startProxy(long proxyVmId) { + return null; + } + + @Override + public boolean destroyProxy(long proxyVmId) { + return false; + } + + @Override + public boolean rebootProxy(long proxyVmId) { + return false; + } + + @Override + public boolean stopProxy(long proxyVmId) { + return false; + } + + @Override + public void setManagementState(ConsoleProxyManagementState state) { + } + + @Override + public ConsoleProxyManagementState getManagementState() { + return null; + } + + @Override + public void resumeLastManagementState() { + } + + @Override + public String getName() { + return _name; + } +<<<<<<< HEAD + + @Override + public Long convertToId(String vmName) { + if (!VirtualMachineName.isValidConsoleProxyName(vmName, _instance)) { + return null; + } + return VirtualMachineName.getConsoleProxyId(vmName); + } + + @Override + public ConsoleProxyVO findByName(String name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ConsoleProxyVO findById(long id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ConsoleProxyVO persist(ConsoleProxyVO vm) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { + // TODO Auto-generated method stub + } + + @Override + public void finalizeExpunge(ConsoleProxyVO proxy) { + } + + @Override + public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, + ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + //not supported + throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType()); + } + + + @Override + public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, + ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { + //not supported + throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); + } + + @Override + public void prepareStop(VirtualMachineProfile profile) { + } +} +======= +} +>>>>>>> QuickCloud: refactor to avoid copy paste of authentication and startup code diff --git a/server/src/com/cloud/secstorage/PremiumSecondaryStorageManagerImpl.java b/server/src/com/cloud/secstorage/PremiumSecondaryStorageManagerImpl.java index 73015c11464..8658113d0f6 100755 --- a/server/src/com/cloud/secstorage/PremiumSecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/secstorage/PremiumSecondaryStorageManagerImpl.java @@ -25,8 +25,6 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; import com.cloud.agent.api.Command; import com.cloud.configuration.Config; @@ -90,6 +88,10 @@ public class PremiumSecondaryStorageManagerImpl extends SecondaryStorageManagerI @Override public Pair scanPool(Long pool) { long dataCenterId = pool.longValue(); + if (!isSecondaryStorageVmRequired(dataCenterId)) { + return new Pair(AfterScanAction.nop, null); + } + Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - _maxExecutionTimeMs); _cmdExecLogDao.expungeExpiredRecords(cutTime); diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index cfb92d5ccde..5d7a2106e6a 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -851,7 +851,7 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor TemplateInfo tmpltInfo = templateInfos.remove(uniqueName); toBeDownloaded.remove(tmplt); if (tmpltHost != null) { - s_logger.info("Template Sync found " + uniqueName + " already in the template host table"); + s_logger.info("Template Sync found " + tmplt.getName() + " already in the template host table"); if (tmpltHost.getDownloadState() != Status.DOWNLOADED) { tmpltHost.setErrorString(""); } @@ -911,10 +911,12 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor continue; } if (tmpltHost != null && tmpltHost.getDownloadState() != Status.DOWNLOADED) { - s_logger.info("Template Sync did not find " + uniqueName + " ready on server " + sserverId + ", will request download to start/resume shortly"); + s_logger.info("Template Sync did not find " + tmplt.getName() + " ready on server " + sserverId + + ", will request download to start/resume shortly"); } else if (tmpltHost == null) { - s_logger.info("Template Sync did not find " + uniqueName + " on the server " + sserverId + ", will request download shortly"); + s_logger.info("Template Sync did not find " + tmplt.getName() + " on the server " + sserverId + + ", will request download shortly"); VMTemplateHostVO templtHost = new VMTemplateHostVO(sserverId, tmplt.getId(), new Date(), 0, Status.NOT_DOWNLOADED, null, null, null, null, tmplt.getUrl()); _vmTemplateHostDao.persist(templtHost); VMTemplateZoneVO tmpltZoneVO = _vmTemplateZoneDao.findByZoneTemplate(zoneId, tmplt.getId()); @@ -964,6 +966,9 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor } s_logger.debug("Template " + tmplt.getName() + " needs to be downloaded to " + ssHost.getName()); downloadTemplateToStorage(tmplt, ssHost); + } else { + s_logger.info("Skipping download of template " + tmplt.getName() + " since we don't have any " + + tmplt.getHypervisorType() + " hypervisors"); } } } diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index d6d6fc0eb69..3cf9a7ef5a3 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -472,7 +472,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar } - private boolean isSecondaryStorageVmRequired(long dcId) { + protected boolean isSecondaryStorageVmRequired(long dcId) { DataCenterVO dc = _dcDao.findById(dcId); _dcDao.loadDetails(dc); String ssvmReq = dc.getDetail(ZoneConfig.EnableSecStorageVm.key()); diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index 6bcf98e1a60..1176d762f95 100755 --- a/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java @@ -1464,7 +1464,7 @@ SecondaryStorageResource { if (_eth1ip != null) { //can only happen inside service vm params.put("private.network.device", "eth1"); } else { - s_logger.warn("Wait, what's going on? eth1ip is null!!"); + s_logger.warn("eth1ip parameter has not been configured, assuming that we are not inside a system vm"); } String eth2ip = (String) params.get("eth2ip"); if (eth2ip != null) { @@ -1479,8 +1479,8 @@ SecondaryStorageResource { } _storageIp = (String) params.get("storageip"); - if (_storageIp == null) { - s_logger.warn("Wait, there is no storageip in /proc/cmdline, something wrong!"); + if (_storageIp == null && _inSystemVM) { + s_logger.warn("There is no storageip in /proc/cmdline, something wrong!"); } _storageNetmask = (String) params.get("storagenetmask"); _storageGateway = (String) params.get("storagegateway"); diff --git a/utils/src/com/cloud/utils/nio/NioClient.java b/utils/src/com/cloud/utils/nio/NioClient.java index 1e2aa52fc77..8d12f931dc6 100755 --- a/utils/src/com/cloud/utils/nio/NioClient.java +++ b/utils/src/com/cloud/utils/nio/NioClient.java @@ -22,8 +22,8 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; -import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; import org.apache.log4j.Logger; @@ -78,6 +78,7 @@ public class NioClient extends NioConnection { Link.doHandshake(sch, sslEngine, true); s_logger.info("SSL: Handshake done"); + s_logger.info("Connected to " + _host + ":" + _port); } catch (Exception e) { _selector.close(); throw new IOException("SSL: Fail to init SSL! " + e); diff --git a/utils/src/com/cloud/utils/nio/NioConnection.java b/utils/src/com/cloud/utils/nio/NioConnection.java index 50e6a88e3e4..07c2beaf517 100755 --- a/utils/src/com/cloud/utils/nio/NioConnection.java +++ b/utils/src/com/cloud/utils/nio/NioConnection.java @@ -107,7 +107,7 @@ public abstract class NioConnection implements Runnable { try { init(); } catch (ConnectException e) { - s_logger.error("Unable to connect to remote"); + s_logger.warn("Unable to connect to remote: is there a server running on port " + _port); return; } catch (IOException e) { s_logger.error("Unable to initialize the threads.", e); From bf56403d828aa50c0650cc41cdc51aae79b1c19e Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 9 Apr 2013 14:45:12 -0700 Subject: [PATCH 48/81] QuickCloud: default to enabled if not specified in datacenter config --- tools/marvin/marvin/deployDataCenter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index d3653958fdd..2e270a7cd58 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -329,7 +329,7 @@ class deployDataCenters(): self.createSecondaryStorages(zone.secondaryStorages, zoneId) enabled = getattr(zone, 'enabled', 'True') - if enabled == 'True' or enabled == 'None': + if enabled == 'True' or enabled is None: self.enableZone(zoneId, "Enabled") details = getattr(zone, 'details') if details is not None: From be55c5b3a58376eb2048a8add155ff09f14e65eb Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Tue, 9 Apr 2013 16:26:08 -0600 Subject: [PATCH 49/81] VPC - new system vm doesn't bring up eth0 reliably, and we don't set eth0 to auto start like we should. cloud-early-config sets 'auto lo $1', but we don't pass $1 in vpc router scenario like we do in others for some reason. eth0 is always link local in vpc router, so setting it to that. Signed-off-by: Marcus Sorensen 1365546368 -0600 --- patches/systemvm/debian/config/etc/init.d/cloud-early-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config index 2b99c5b6cf3..514c0b012cf 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -704,7 +704,7 @@ setup_vpcrouter() { fi cat > /etc/network/interfaces << EOF -auto lo $1 +auto lo eth0 iface lo inet loopback EOF setup_interface "0" $ETH0_IP $ETH0_MASK $GW From 6f3069f0da803363e6c5df50e67d9489a566e79d Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 9 Apr 2013 17:15:55 -0700 Subject: [PATCH 50/81] CLOUDSTACK-1974: cloudstack UI - Infrastructure menu - zone detail - public traffic type - IP Ranges tab - add action filter. --- ui/scripts/system.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index d89f6b69dcd..f74711d273d 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -565,6 +565,14 @@ }); } }, + actionPreFilter: function(args) { + var actionsToShow = ['destroy']; + if(args.context.multiRule[0].domain == 'ROOT' && args.context.multiRule[0].account.account == 'system') + actionsToShow.push('addAccount'); + else + actionsToShow.push('releaseFromAccount'); + return actionsToShow; + }, actions: { destroy: { label: 'label.remove.ip.range', @@ -588,7 +596,7 @@ } }); } - }, + }, /* releaseFromAccount: { label: 'Release from Account', @@ -643,7 +651,10 @@ }, action: function(args) { var data = { - id: args.context.multiRule[0].id + id: args.context.multiRule[0].id, + zoneid: args.context.multiRule[0].zoneid, + domainid: args.data.domainid, + account: args.data.account }; $.ajax({ url: createURL('dedicatePublicIpRange'), @@ -663,8 +674,8 @@ } }); } - } - */ + } + */ }, dataProvider: function(args) { $.ajax({ From 913f73ab6b9df57c12601eff0ccbc71bf65fa1fa Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 9 Apr 2013 17:16:14 -0700 Subject: [PATCH 51/81] Apply patch https://reviews.apache.org/r/10137/ --- .../com/cloud/hypervisor/guru/VMwareGuru.java | 5 ++--- .../src/com/cloud/vm/UserVmManagerImpl.java | 21 +++++++++++++------ setup/db/db/schema-410to420.sql | 9 ++++++++ .../vmware/util/VmwareGuestOsMapper.java | 5 +++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java index bb7c29745d9..20146970daa 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -135,10 +135,9 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { if (!(vm.getVirtualMachine() instanceof DomainRouterVO || vm.getVirtualMachine() instanceof ConsoleProxyVO || vm.getVirtualMachine() instanceof SecondaryStorageVmVO)){ // user vm - if (diskDeviceType != null){ - details.remove(VmDetailConstants.ROOK_DISK_CONTROLLER); + if (diskDeviceType == null){ + details.put(VmDetailConstants.ROOK_DISK_CONTROLLER, _vmwareMgr.getRootDiskController()); } - details.put(VmDetailConstants.ROOK_DISK_CONTROLLER, _vmwareMgr.getRootDiskController()); } to.setDetails(details); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 646cf9285e3..1c3764a8391 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2485,6 +2485,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use _vmCloneSettingDao.persist(vmCloneSettingVO); } + long guestOSId = template.getGuestOSId(); + GuestOSVO guestOS = _guestOSDao.findById(guestOSId); + long guestOSCategoryId = guestOS.getCategoryId(); + GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); + + + // If hypervisor is vSphere and OS is OS X, set special settings. + if (hypervisorType.equals(HypervisorType.VMware)) { + if (guestOS.getDisplayName().toLowerCase().contains("apple mac os")){ + vm.setDetail("smc.present", "TRUE"); + vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, "scsi"); + vm.setDetail("firmware", "efi"); + s_logger.info("guestOS is OSX : overwrite root disk controller to scsi, use smc and efi"); + } + } _vmDao.persist(vm); _vmDao.saveDetails(vm); @@ -2492,12 +2507,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use s_logger.debug("Allocating in the DB for vm"); DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); - - long guestOSId = template.getGuestOSId(); - GuestOSVO guestOS = _guestOSDao.findById(guestOSId); - long guestOSCategoryId = guestOS.getCategoryId(); - GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); - List computeTags = new ArrayList(); computeTags.add(offering.getHostTag()); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index ab9df05103b..97d0da3c9a8 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -124,6 +124,15 @@ INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Windows 8 (64 bit)', 209); INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Windows 8 Server (64 bit)', 210); +INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (211, UUID(), 7, 'Apple Mac OS X 10.6 (32 bits)'); +INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (212, UUID(), 7, 'Apple Mac OS X 10.6 (64 bits)'); +INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (213, UUID(), 7, 'Apple Mac OS X 10.7 (32 bits)'); +INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (214, UUID(), 7, 'Apple Mac OS X 10.7 (64 bits)'); +INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Apple Mac OS X 10.6 (32 bits)', 211); +INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Apple Mac OS X 10.6 (64 bits)', 212); +INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Apple Mac OS X 10.7 (32 bits)', 213); +INSERT INTO `cloud`.`guest_os_hypervisor` (hypervisor_type, guest_os_name, guest_os_id) VALUES ("VmWare", 'Apple Mac OS X 10.7 (64 bits)', 214); + CREATE TABLE `cloud`.`user_vm_clone_setting` ( `vm_id` bigint unsigned NOT NULL COMMENT 'guest VM id', `clone_type` varchar(10) NOT NULL COMMENT 'Full or Linked Clone (applicable to VMs on ESX)', diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java index 04b4d8edcce..c76ce84bc3d 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java @@ -62,6 +62,11 @@ public class VmwareGuestOsMapper { s_mapper.put("Windows 8 (64 bit)", VirtualMachineGuestOsIdentifier.WINDOWS_8_64_GUEST); s_mapper.put("Windows 8 Server (64 bit)", VirtualMachineGuestOsIdentifier.WINDOWS_8_SERVER_64_GUEST); + s_mapper.put("Apple Mac OS X 10.6 (32 bits)", VirtualMachineGuestOsIdentifier.DARWIN_10_GUEST); + s_mapper.put("Apple Mac OS X 10.6 (64 bits)", VirtualMachineGuestOsIdentifier.DARWIN_10_64_GUEST); + s_mapper.put("Apple Mac OS X 10.7 (32 bits)", VirtualMachineGuestOsIdentifier.DARWIN_11_GUEST); + s_mapper.put("Apple Mac OS X 10.7 (64 bits)", VirtualMachineGuestOsIdentifier.DARWIN_11_64_GUEST); + s_mapper.put("Open Enterprise Server", VirtualMachineGuestOsIdentifier.OES_GUEST); s_mapper.put("Asianux 3(32-bit)", VirtualMachineGuestOsIdentifier.ASIANUX_3_GUEST); From 6175f6e4ebd13463424ead338e9c989a1d9a7d65 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Tue, 9 Apr 2013 19:36:43 -0500 Subject: [PATCH 52/81] More work on Release Notes --- docs/en-US/Release_Notes.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 00cbc49c881..4535fd8c7e3 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -48,15 +48,15 @@ under the License. Stop your management server or servers. Run this on all management server hosts: - # service cloud-management stop + # service cloud-management stop Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. - # mysqldump -u root -p cloud > cloudstack-backup.sql + # mysqldump -u root -p cloud > cloudstack-backup.sql Whether you're upgrading a Red Hat/CentOS based system or Ubuntu based system, you're going to need to stop the CloudStack management server before proceeding. - # service cloud-management stop + # service cloud-management stop If you have made changes to /etc/cloud/management/components.xml, you'll need to carry these over manually to the new file, /etc/cloudstack/management/componentContext.xml. This is not done automatically. (If you're unsure, we recommend making a backup of the original components.xml to be on the safe side. @@ -114,7 +114,7 @@ db.awsapi.port= - ### + The package names have changed between 4.0 and 4.1, so upgrading the packages won't happen automatically with a yum update
@@ -220,11 +220,11 @@ db.awsapi.port= Stop all Usage Servers if running. Run this on all Usage Server hosts. - # service cloud-usage stop + # service cloud-usage stop Stop the Management Servers. Run this on all Management Server hosts. - # service cloud-management stop + # service cloud-management stop On the MySQL master, take a backup of the MySQL databases. We recommend performing @@ -288,7 +288,7 @@ db.awsapi.port= Start the first Management Server. Do not start any other Management Server nodes yet. - # service cloud-management start + # service cloud-management start Wait until the databases are upgraded. Ensure that the database upgrade is complete. After confirmation, start the other Management Servers one at a time by running the same command on each node. @@ -669,7 +669,7 @@ db.awsapi.port= Stop the Management Servers. Run this on all Management Server hosts. - # service cloud-management stop + # service cloud-management stop On the MySQL master, take a backup of the MySQL databases. We recommend performing @@ -780,7 +780,7 @@ db.awsapi.port= Start the first Management Server. Do not start any other Management Server nodes yet. - # service cloud-management start + # service cloud-management start Wait until the databases are upgraded. Ensure that the database upgrade is complete. You should see a message like "Complete! Done." After confirmation, start the other Management Servers one at a time by running the same command on each node. @@ -788,7 +788,7 @@ db.awsapi.port= Start all Usage Servers (if they were running on your previous version). Perform this on each Usage Server host. - # service cloud-usage start + # service cloud-usage start (KVM only) Additional steps are required for each KVM host. These steps will not From d8103afdf6d501478b406b24e55a21017b36ab63 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Tue, 9 Apr 2013 22:01:26 +0000 Subject: [PATCH 53/81] Fixed typos Signed-off-by: Joe Brockmeier --- docs/en-US/added-API-commands.xml | 2 +- docs/en-US/aws-ec2-introduction.xml | 2 +- docs/en-US/building-documentation.xml | 2 +- docs/en-US/building-marvin.xml | 2 +- docs/en-US/building-translation.xml | 2 +- docs/en-US/change-console-proxy-ssl-certificate-domain.xml | 2 +- docs/en-US/citrix-xenserver-installation.xml | 2 +- docs/en-US/configure-package-repository.xml | 2 +- docs/en-US/configure-vpn.xml | 2 +- docs/en-US/console-proxy.xml | 2 +- docs/en-US/hypervisor-host-install-libvirt.xml | 2 +- docs/en-US/hypervisor-host-install-network-openvswitch.xml | 2 +- docs/en-US/hypervisor-host-install-network.xml | 4 ++-- docs/en-US/management-server-install-prepare-os.xml | 2 +- docs/en-US/management-server-lb.xml | 2 +- docs/en-US/minimum-system-requirements.xml | 2 +- docs/en-US/translating-documentation.xml | 2 +- docs/en-US/using-sshkeys.xml | 2 +- docs/en-US/vmware-requirements.xml | 2 +- docs/en-US/writing-new-documentation.xml | 4 ++-- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/en-US/added-API-commands.xml b/docs/en-US/added-API-commands.xml index 208aac29dc2..99635de4697 100644 --- a/docs/en-US/added-API-commands.xml +++ b/docs/en-US/added-API-commands.xml @@ -87,7 +87,7 @@ suspendProject (Suspends a project) listProjects (Lists projects and provides detailed information for listed projects) - addAccountToProject (Adds acoount to a project) + addAccountToProject (Adds account to a project) deleteAccountFromProject (Deletes account from the project) diff --git a/docs/en-US/aws-ec2-introduction.xml b/docs/en-US/aws-ec2-introduction.xml index 538c09d5ad1..4cf071bcbb2 100644 --- a/docs/en-US/aws-ec2-introduction.xml +++ b/docs/en-US/aws-ec2-introduction.xml @@ -45,7 +45,7 @@ Available in fresh installations of &PRODUCT;. Not available through upgrade of previous versions. - Features such as Elastic IP (EIP) and Elastic Load Balacing (ELB) are only available in an infrastructure + Features such as Elastic IP (EIP) and Elastic Load Balancing (ELB) are only available in an infrastructure with a Citrix NetScaler device. Users accessing a Zone with a NetScaler device will need to use a NetScaler-enabled network offering (DefaultSharedNetscalerEIP and ELBNetworkOffering). diff --git a/docs/en-US/building-documentation.xml b/docs/en-US/building-documentation.xml index 484826604fa..8ee63b06ec0 100644 --- a/docs/en-US/building-documentation.xml +++ b/docs/en-US/building-documentation.xml @@ -25,7 +25,7 @@
Building &PRODUCT; Documentation To build a specific guide, go to the source tree of the documentation in /docs and identify the guide you want to build. - Currenlty there are four guides plus the release notes, all defined in publican configuration files: + Currently there are four guides plus the release notes, all defined in publican configuration files: publican-adminguide.cfg publican-devguide.cfg diff --git a/docs/en-US/building-marvin.xml b/docs/en-US/building-marvin.xml index 3332b16d9b1..e33c4cb2248 100644 --- a/docs/en-US/building-marvin.xml +++ b/docs/en-US/building-marvin.xml @@ -27,7 +27,7 @@ Marvin is built with Maven and is dependent on APIdoc. To build it do the following in the root tree of &PRODUCT;: mvn -P developer -pl :cloud-apidoc mvn -P developer -pl :cloud-marvin - If successfull the build will have created the cloudstackAPI Python package under tools/marvin/marvin/cloudstackAPI as well as a gziped Marvin package under tools/marvin dist. To install the Python Marvin module do the following in tools/marvin: + If successful the build will have created the cloudstackAPI Python package under tools/marvin/marvin/cloudstackAPI as well as a gziped Marvin package under tools/marvin dist. To install the Python Marvin module do the following in tools/marvin: sudo python ./setup.py install The dependencies will be downloaded the Python module installed and you should be able to use Marvin in Python. Check that you can import the module before starting to use it. $ python diff --git a/docs/en-US/building-translation.xml b/docs/en-US/building-translation.xml index 659c55ffc5e..dd66365cd9d 100644 --- a/docs/en-US/building-translation.xml +++ b/docs/en-US/building-translation.xml @@ -52,7 +52,7 @@ how to prepare a document for translation. The basic command to execute to build the pot files for the developer guide is: publican update_pot --config=publican-devguide.cfg - This will create a pot directory with pot files in it, one for each corresponding xml files needed to build the guide. Once genereated, all pots files need to be configured for translation using transifex this is best done by using the transifex client that you can install with the following command (For RHEL and its derivatives): + This will create a pot directory with pot files in it, one for each corresponding xml files needed to build the guide. Once generated, all pots files need to be configured for translation using transifex this is best done by using the transifex client that you can install with the following command (For RHEL and its derivatives): yum install transifex-client The transifex client is also available via PyPi and you can install it like this: easy_install transifex-client diff --git a/docs/en-US/change-console-proxy-ssl-certificate-domain.xml b/docs/en-US/change-console-proxy-ssl-certificate-domain.xml index 89796a22c23..3fd05018e99 100644 --- a/docs/en-US/change-console-proxy-ssl-certificate-domain.xml +++ b/docs/en-US/change-console-proxy-ssl-certificate-domain.xml @@ -32,7 +32,7 @@ Generate a new 2048-bit private keyopenssl genrsa -des3 -out yourprivate.key 2048 Generate a new certificate CSRopenssl req -new -key yourprivate.key -out yourcertificate.csr Head to the website of your favorite trusted Certificate Authority, purchase an SSL certificate, and submit the CSR. You should receive a valid certificate in return - Convert your private key format into PKCS#8 encrypted format.openssl pkcs8 -topk8 -in yourprivate.key -out yourprivate.pkcs8.encryped.key + Convert your private key format into PKCS#8 encrypted format.openssl pkcs8 -topk8 -in yourprivate.key -out yourprivate.pkcs8.encrypted.key Convert your PKCS#8 encrypted private key into the PKCS#8 format that is compliant with &PRODUCT;openssl pkcs8 -in yourprivate.pkcs8.encrypted.key -out yourprivate.pkcs8.key diff --git a/docs/en-US/citrix-xenserver-installation.xml b/docs/en-US/citrix-xenserver-installation.xml index 40538658078..2cd39a41368 100644 --- a/docs/en-US/citrix-xenserver-installation.xml +++ b/docs/en-US/citrix-xenserver-installation.xml @@ -62,7 +62,7 @@ support any system that is not up to date with patches. - All hosts within a cluster must be homogenous. The CPUs must be of the same type, + All hosts within a cluster must be homogeneous. The CPUs must be of the same type, count, and feature flags. diff --git a/docs/en-US/configure-package-repository.xml b/docs/en-US/configure-package-repository.xml index 3d102c697ad..c8ba48f2717 100644 --- a/docs/en-US/configure-package-repository.xml +++ b/docs/en-US/configure-package-repository.xml @@ -33,7 +33,7 @@ If you didn't follow the steps to build your own packages from source in the sections for or you may find pre-built - DEB and RPM packages for your convience linked from the + DEB and RPM packages for your convenience linked from the downloads page. diff --git a/docs/en-US/configure-vpn.xml b/docs/en-US/configure-vpn.xml index 87b4e65b56f..5d25620b3eb 100644 --- a/docs/en-US/configure-vpn.xml +++ b/docs/en-US/configure-vpn.xml @@ -29,7 +29,7 @@ In the left navigation, click Global Settings. Set the following global configuration parameters. - remote.access.vpn.client.ip.range – The range of IP addressess to be allocated to remote access VPN clients. The first IP in the range is used by the VPN server. + remote.access.vpn.client.ip.range – The range of IP addresses to be allocated to remote access VPN clients. The first IP in the range is used by the VPN server. remote.access.vpn.psk.length – Length of the IPSec key. remote.access.vpn.user.limit – Maximum number of VPN users per account. diff --git a/docs/en-US/console-proxy.xml b/docs/en-US/console-proxy.xml index 64183b4bfc0..5f9a82027d2 100644 --- a/docs/en-US/console-proxy.xml +++ b/docs/en-US/console-proxy.xml @@ -95,7 +95,7 @@ Convert your private key format into PKCS#8 encrypted format. - openssl pkcs8 -topk8 -in yourprivate.key -out yourprivate.pkcs8.encryped.key + openssl pkcs8 -topk8 -in yourprivate.key -out yourprivate.pkcs8.encrypted.key Convert your PKCS#8 encrypted private key into the PKCS#8 format that is compliant diff --git a/docs/en-US/hypervisor-host-install-libvirt.xml b/docs/en-US/hypervisor-host-install-libvirt.xml index d7dc47f8dbd..f3ff090463c 100644 --- a/docs/en-US/hypervisor-host-install-libvirt.xml +++ b/docs/en-US/hypervisor-host-install-libvirt.xml @@ -28,7 +28,7 @@ In order to have live migration working libvirt has to listen for unsecured TCP connections. We also need to turn off libvirts attempt to use Multicast DNS advertising. Both of these settings are in /etc/libvirt/libvirtd.conf - Set the following paramaters: + Set the following parameters: listen_tls = 0 listen_tcp = 1 tcp_port = "16509" diff --git a/docs/en-US/hypervisor-host-install-network-openvswitch.xml b/docs/en-US/hypervisor-host-install-network-openvswitch.xml index e9bf47a0d20..a16dc8e0e8d 100644 --- a/docs/en-US/hypervisor-host-install-network-openvswitch.xml +++ b/docs/en-US/hypervisor-host-install-network-openvswitch.xml @@ -69,7 +69,7 @@ we can proceed to configuring the network. First we configure eth0 vi /etc/sysconfig/network-scripts/ifcfg-eth0 - Make sure it looks similair to: + Make sure it looks similar to: The required packages were installed when libvirt was installed, we can proceed to configuring the network. First we configure eth0 vi /etc/sysconfig/network-scripts/ifcfg-eth0 - Make sure it looks similair to: + Make sure it looks similar to: Now we have the VLAN interfaces configured we can add the bridges on top of them. vi /etc/sysconfig/network-scripts/ifcfg-cloudbr0 - Now we just configure it is a plain bridge without an IP-Adress + Now we just configure it is a plain bridge without an IP-Address Check for a fully qualified hostname. hostname --fqdn - This should return a fully qualified hostname such as "managament1.lab.example.org". If it does not, edit /etc/hosts so that it does. + This should return a fully qualified hostname such as "management1.lab.example.org". If it does not, edit /etc/hosts so that it does. Make sure that the machine can reach the Internet. diff --git a/docs/en-US/management-server-lb.xml b/docs/en-US/management-server-lb.xml index 9aee1548026..13f87560e10 100644 --- a/docs/en-US/management-server-lb.xml +++ b/docs/en-US/management-server-lb.xml @@ -58,7 +58,7 @@ - In addition to above settings, the adminstrator is responsible for setting the 'host' global + In addition to above settings, the administrator is responsible for setting the 'host' global config value from the management server IP to load balancer virtual IP address. If the 'host' value is not set to the VIP for Port 8250 and one of your management servers crashes, the UI is still available but the system VMs will not be able to contact the management server. diff --git a/docs/en-US/minimum-system-requirements.xml b/docs/en-US/minimum-system-requirements.xml index de1bc222023..870ef68eae4 100644 --- a/docs/en-US/minimum-system-requirements.xml +++ b/docs/en-US/minimum-system-requirements.xml @@ -57,7 +57,7 @@ If DHCP is used for hosts, ensure that no conflict occurs between DHCP server used for these hosts and the DHCP router created by &PRODUCT;. Latest hotfixes applied to hypervisor software When you deploy &PRODUCT;, the hypervisor host must not have any VMs already running - All hosts within a cluster must be homogenous. The CPUs must be of the same type, count, and feature flags. + All hosts within a cluster must be homogeneous. The CPUs must be of the same type, count, and feature flags. Hosts have additional requirements depending on the hypervisor. See the requirements listed at the top of the Installation section for your chosen hypervisor: diff --git a/docs/en-US/translating-documentation.xml b/docs/en-US/translating-documentation.xml index afe27658f1a..4d5e3d21b43 100644 --- a/docs/en-US/translating-documentation.xml +++ b/docs/en-US/translating-documentation.xml @@ -32,7 +32,7 @@ Using the Transifex client and pushing your translated strings to the website. - Once a translation is complete, a site admin will pull the translated strings within the &PRODUCT; repository, build the documenation and publish it. + Once a translation is complete, a site admin will pull the translated strings within the &PRODUCT; repository, build the documentation and publish it. For instructions on how to use the Transifex website see http://sebgoa.blogspot.ch/2012/11/translating-apache-cloudstack-docs-with.html For instructions on how to use the Transifex client to translate from the command line see http://sebgoa.blogspot.ch/2012/12/using-transifex-client-to-translate.html
diff --git a/docs/en-US/using-sshkeys.xml b/docs/en-US/using-sshkeys.xml index cd10d68470a..f34dfa0c15b 100644 --- a/docs/en-US/using-sshkeys.xml +++ b/docs/en-US/using-sshkeys.xml @@ -92,7 +92,7 @@ KfEEuzcCUIxtJYTahJ1pvlFkQ8anpuxjSEDp8x/18bq3 After you save the SSH keypair file, you must create an instance by using the template that you created at . Ensure that you use the same SSH key name that you created at . You cannot create the instance by using the GUI at this time and associate the instance with the newly created SSH keypair. A sample curl command to create a new instance is: - curl --globoff http://localhost:<port numbet>/?command=deployVirtualMachine\&zoneId=1\&serviceOfferingId=18727021-7556-4110-9322-d625b52e0813\&templateId=e899c18a-ce13-4bbf-98a9-625c5026e0b5\&securitygroupids=ff03f02f-9e3b-48f8-834d-91b822da40c5\&account=admin\&domainid=1\&keypair=keypair-doc + curl --globoff http://localhost:<port number>/?command=deployVirtualMachine\&zoneId=1\&serviceOfferingId=18727021-7556-4110-9322-d625b52e0813\&templateId=e899c18a-ce13-4bbf-98a9-625c5026e0b5\&securitygroupids=ff03f02f-9e3b-48f8-834d-91b822da40c5\&account=admin\&domainid=1\&keypair=keypair-doc Substitute the template, service offering and security group IDs (if you are using the security group feature) that are in your cloud environment.
diff --git a/docs/en-US/vmware-requirements.xml b/docs/en-US/vmware-requirements.xml index 207a4566de8..d7a6d70e6a4 100644 --- a/docs/en-US/vmware-requirements.xml +++ b/docs/en-US/vmware-requirements.xml @@ -68,7 +68,7 @@ vCenter must be configured to use the standard port 443 so that it can communicate with the &PRODUCT; Management Server. You must re-install VMware ESXi if you are going to re-use a host from a previous install. &PRODUCT; requires VMware vSphere 4.1 or 5.0. VMware vSphere 4.0 is not supported. - All hosts must be 64-bit and must support HVM (Intel-VT or AMD-V enabled). All hosts within a cluster must be homogenous. That means the CPUs must be of the same type, count, and feature flags. + All hosts must be 64-bit and must support HVM (Intel-VT or AMD-V enabled). All hosts within a cluster must be homogeneous. That means the CPUs must be of the same type, count, and feature flags. The &PRODUCT; management network must not be configured as a separate virtual network. The &PRODUCT; management network is the same as the vCenter management network, and will inherit its configuration. See . &PRODUCT; requires ESXi. ESX is not supported. All resources used for &PRODUCT; must be used for &PRODUCT; only. &PRODUCT; cannot share instance of ESXi or storage with other management consoles. Do not share the same storage volumes that will be used by &PRODUCT; with a different set of ESXi servers that are not managed by &PRODUCT;. diff --git a/docs/en-US/writing-new-documentation.xml b/docs/en-US/writing-new-documentation.xml index 340900e3c60..7557359fd09 100644 --- a/docs/en-US/writing-new-documentation.xml +++ b/docs/en-US/writing-new-documentation.xml @@ -82,7 +82,7 @@
Building &PRODUCT; Documentation To build a specific guide, go to the source tree of the documentation in /docs and identify the guide you want to build. - Currenlty there are four guides plus the release notes, all defined in publican configuration files: + Currently there are four guides plus the release notes, all defined in publican configuration files: publican-adminguide.cfg publican-devguide.cfg @@ -96,5 +96,5 @@
]]> - Happy Publicaning and DocBooking. + Happy Publicating and DocBooking.
From d4e2aa32c6555971312fab47c45eef23b57579d1 Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Wed, 27 Mar 2013 09:33:00 -0700 Subject: [PATCH 54/81] CLOUDSTACK-1675: VMSnapshot: ListVMSnapshotsCmd should not display Domain ID in the Error Message Description: Replace domain db id by uuid in exception message. Signed-off-by: Mice Xia --- server/src/com/cloud/user/AccountManagerImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 9736aa122e6..bc93df8e756 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -2138,7 +2138,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M permittedAccounts, Ternary domainIdRecursiveListProject, boolean listAll, boolean forProjectInvitation) { Long domainId = domainIdRecursiveListProject.first(); - if (domainId != null) { Domain domain = _domainDao.findById(domainId); if (domain == null) { @@ -2154,10 +2153,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } Account userAccount = null; + Domain domain = null; if (domainId != null) { userAccount = _accountDao.findActiveAccount(accountName, domainId); + domain = _domainDao.findById(domainId); } else { userAccount = _accountDao.findActiveAccount(accountName, caller.getDomainId()); + domain = _domainDao.findById(caller.getDomainId()); } if (userAccount != null) { @@ -2165,7 +2167,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M //check permissions permittedAccounts.add(userAccount.getId()); } else { - throw new InvalidParameterValueException("could not find account " + accountName + " in domain " + domainId); + throw new InvalidParameterValueException("could not find account " + accountName + " in domain " + domain.getUuid()); } } From bd7a38957a33bedb6f135cd506600f4ced6be896 Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Wed, 10 Apr 2013 11:30:16 +0800 Subject: [PATCH 55/81] 1)use vmsnapshot.create.wait to control vmsnapshot timeout 2)remove global configuration vmsnapshot.expunge.interval and vmsnapshot.expunge.workers since vmsnapshot expunge is synchronous --- .../xen/resource/CitrixResourceBase.java | 2 +- .../src/com/cloud/configuration/Config.java | 4 +-- .../vm/snapshot/VMSnapshotManagerImpl.java | 5 ++++ setup/db/db/schema-40to410.sql | 29 ------------------ setup/db/db/schema-410to420.sql | 30 +++++++++++++++++++ 5 files changed, 37 insertions(+), 33 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 c50f13ce7e5..4ef583a5463 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 @@ -6380,7 +6380,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe String guestOSType = cmd.getGuestOSType(); boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; - long timeout = 600; + long timeout = cmd.getWait(); Connection conn = getConnection(); VM vm = null; diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 200943b35e6..2137c003912 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -397,9 +397,7 @@ public enum Config { // VMSnapshots VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null), - VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "600", "In second, timeout for create vm snapshot", null), - VMSnapshotExpungeInterval("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.interval", "60", "The interval (in seconds) to wait before running the expunge thread.", null), - VMSnapshotExpungeWorkers("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.workers", "1", "Number of workers performing expunge ", null), + VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null), CloudDnsName("Advanced", ManagementServer.class, String.class, "cloud.dns.name", "default", " DNS name of the cloud", null); diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index 638be6c0c9b..ffbbb820ecb 100644 --- a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -120,6 +120,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana @Inject DataStoreManager dataStoreMgr; @Inject ConfigurationDao _configDao; int _vmSnapshotMax; + int _wait; StateMachine2 _vmSnapshottateMachine ; @Override @@ -131,6 +132,9 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana } _vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX); + + String value = _configDao.getValue("vmsnapshot.create.wait"); + _wait = NumbersUtil.parseInt(value, 1800); _vmSnapshottateMachine = VMSnapshot.State.getStateMachine(); return true; @@ -361,6 +365,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana vmSnapshot.setParent(current.getId()); CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(userVm.getInstanceName(),target ,volumeTOs, guestOS.getDisplayName(),userVm.getState()); + ccmd.setWait(_wait); answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd); if (answer != null && answer.getResult()) { diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index fc15b94493e..e2949d93e61 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -404,35 +404,6 @@ INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (1, INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (2, UUID(), 'snmp','Linux System CPU - percentage', '1.3.6.1.4.1.2021.11.10.0', now()); INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (3, UUID(), 'snmp','Linux CPU Idle - percentage', '1.3.6.1.4.1.2021.11.11.0', now()); INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (100, UUID(), 'netscaler','Response Time - microseconds', 'RESPTIME', now()); -CREATE TABLE `cloud`.`vm_snapshots` ( - `id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'Primary Key', - `uuid` varchar(40) NOT NULL, - `name` varchar(255) NOT NULL, - `display_name` varchar(255) default NULL, - `description` varchar(255) default NULL, - `vm_id` bigint(20) unsigned NOT NULL, - `account_id` bigint(20) unsigned NOT NULL, - `domain_id` bigint(20) unsigned NOT NULL, - `vm_snapshot_type` varchar(32) default NULL, - `state` varchar(32) NOT NULL, - `parent` bigint unsigned default NULL, - `current` int(1) unsigned default NULL, - `update_count` bigint unsigned NOT NULL DEFAULT 0, - `updated` datetime default NULL, - `created` datetime default NULL, - `removed` datetime default NULL, - PRIMARY KEY (`id`), - CONSTRAINT UNIQUE KEY `uc_vm_snapshots_uuid` (`uuid`), - INDEX `vm_snapshots_name` (`name`), - INDEX `vm_snapshots_vm_id` (`vm_id`), - INDEX `vm_snapshots_account_id` (`account_id`), - INDEX `vm_snapshots_display_name` (`display_name`), - INDEX `vm_snapshots_removed` (`removed`), - INDEX `vm_snapshots_parent` (`parent`), - CONSTRAINT `fk_vm_snapshots_vm_id__vm_instance_id` FOREIGN KEY `fk_vm_snapshots_vm_id__vm_instance_id` (`vm_id`) REFERENCES `vm_instance` (`id`), - CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`), - CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`user_ipv6_address` ( `id` bigint unsigned NOT NULL UNIQUE auto_increment, diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 97d0da3c9a8..ff9ca1de42a 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -414,3 +414,33 @@ INSERT INTO `cloud`.`vm_template` (id, unique_name, name, public, created, type, VALUES (10, 'routing-10', 'SystemVM Template (LXC)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2', '2755de1f9ef2ce4d6f2bee2efbb4da92', 0, 'SystemVM Template (LXC)', 'QCOW2', 15, 0, 1, 'LXC'); -- END: support for LXC + +CREATE TABLE `cloud`.`vm_snapshots` ( + `id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'Primary Key', + `uuid` varchar(40) NOT NULL, + `name` varchar(255) NOT NULL, + `display_name` varchar(255) default NULL, + `description` varchar(255) default NULL, + `vm_id` bigint(20) unsigned NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `vm_snapshot_type` varchar(32) default NULL, + `state` varchar(32) NOT NULL, + `parent` bigint unsigned default NULL, + `current` int(1) unsigned default NULL, + `update_count` bigint unsigned NOT NULL DEFAULT 0, + `updated` datetime default NULL, + `created` datetime default NULL, + `removed` datetime default NULL, + PRIMARY KEY (`id`), + CONSTRAINT UNIQUE KEY `uc_vm_snapshots_uuid` (`uuid`), + INDEX `vm_snapshots_name` (`name`), + INDEX `vm_snapshots_vm_id` (`vm_id`), + INDEX `vm_snapshots_account_id` (`account_id`), + INDEX `vm_snapshots_display_name` (`display_name`), + INDEX `vm_snapshots_removed` (`removed`), + INDEX `vm_snapshots_parent` (`parent`), + CONSTRAINT `fk_vm_snapshots_vm_id__vm_instance_id` FOREIGN KEY `fk_vm_snapshots_vm_id__vm_instance_id` (`vm_id`) REFERENCES `vm_instance` (`id`), + CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`), + CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; From b646e43a1a7fb3c668a7160823c3740aeaf3ac8c Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Wed, 10 Apr 2013 11:34:39 +0800 Subject: [PATCH 56/81] use hypervisor capabilities to control if vm snapshot is enabled for hypervisors --- .../hypervisor/HypervisorCapabilitiesVO.java | 13 +++++++++++- .../dao/HypervisorCapabilitiesDao.java | 2 ++ .../dao/HypervisorCapabilitiesDaoImpl.java | 7 +++++++ .../vm/snapshot/VMSnapshotManagerImpl.java | 8 +++++++- .../vm/snapshot/VMSnapshotManagerTest.java | 20 +++++++++++++++++-- setup/db/db/schema-410to420.sql | 4 ++++ 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/core/src/com/cloud/hypervisor/HypervisorCapabilitiesVO.java b/core/src/com/cloud/hypervisor/HypervisorCapabilitiesVO.java index b525a2d05d5..fafc0a33af0 100644 --- a/core/src/com/cloud/hypervisor/HypervisorCapabilitiesVO.java +++ b/core/src/com/cloud/hypervisor/HypervisorCapabilitiesVO.java @@ -62,6 +62,9 @@ public class HypervisorCapabilitiesVO implements HypervisorCapabilities { @Column(name="max_hosts_per_cluster") private Integer maxHostsPerCluster; + @Column(name="vm_snapshot_enabled") + private Boolean vmSnapshotEnabled; + protected HypervisorCapabilitiesVO() { this.uuid = UUID.randomUUID().toString(); } @@ -169,7 +172,15 @@ public class HypervisorCapabilitiesVO implements HypervisorCapabilities { this.maxHostsPerCluster = maxHostsPerCluster; } - @Override + public Boolean getVmSnapshotEnabled() { + return vmSnapshotEnabled; + } + + public void setVmSnapshotEnabled(Boolean vmSnapshotEnabled) { + this.vmSnapshotEnabled = vmSnapshotEnabled; + } + + @Override public boolean equals(Object obj) { if (obj instanceof HypervisorCapabilitiesVO) { return ((HypervisorCapabilitiesVO)obj).getId() == this.getId(); diff --git a/server/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java b/server/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java index 0fe0b535f78..1fdc03a74fe 100644 --- a/server/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java +++ b/server/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java @@ -33,4 +33,6 @@ public interface HypervisorCapabilitiesDao extends GenericDao _vmSnapshottateMachine ; @@ -244,7 +246,11 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana if (userVmVo == null) { throw new InvalidParameterValueException("Creating VM snapshot failed due to VM:" + vmId + " is a system VM or does not exist"); } - + + // check hypervisor capabilities + if(!_hypervisorCapabilitiesDao.isVmSnapshotEnabled(userVmVo.getHypervisorType(), "default")) + throw new InvalidParameterValueException("VM snapshot is not enabled for hypervisor type: " + userVmVo.getHypervisorType()); + // parameter length check if(vsDisplayName != null && vsDisplayName.length()>255) throw new InvalidParameterValueException("Creating VM snapshot failed due to length of VM snapshot vsDisplayName should not exceed 255"); diff --git a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 576c95b3788..41c9d120539 100644 --- a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -27,6 +27,8 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -36,6 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import com.amazonaws.services.ec2.model.HypervisorType; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CreateVMSnapshotAnswer; @@ -47,7 +50,9 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceAllocationException; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.storage.GuestOSVO; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; @@ -88,6 +93,7 @@ public class VMSnapshotManagerTest { @Mock SnapshotDao _snapshotDao; @Mock VirtualMachineManager _itMgr; @Mock ConfigurationDao _configDao; + @Mock HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; int _vmSnapshotMax = 10; private static long TEST_VM_ID = 3L; @@ -105,6 +111,7 @@ public class VMSnapshotManagerTest { _vmSnapshotMgr._accountMgr = _accountMgr; _vmSnapshotMgr._snapshotDao = _snapshotDao; _vmSnapshotMgr._guestOSDao = _guestOSDao; + _vmSnapshotMgr._hypervisorCapabilitiesDao = _hypervisorCapabilitiesDao; doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class)); @@ -114,7 +121,8 @@ public class VMSnapshotManagerTest { when(_userVMDao.findById(anyLong())).thenReturn(vmMock); when(_vmSnapshotDao.findByName(anyLong(), anyString())).thenReturn(null); when(_vmSnapshotDao.findByVm(anyLong())).thenReturn(new ArrayList()); - + when(_hypervisorCapabilitiesDao.isVmSnapshotEnabled(Hypervisor.HypervisorType.XenServer, "default")).thenReturn(true); + List mockVolumeList = new ArrayList(); mockVolumeList.add(volumeMock); when(volumeMock.getInstanceId()).thenReturn(TEST_VM_ID); @@ -122,7 +130,7 @@ public class VMSnapshotManagerTest { when(vmMock.getInstanceName()).thenReturn("i-3-VM-TEST"); when(vmMock.getState()).thenReturn(State.Running); - + when(vmMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); when(_guestOSDao.findById(anyLong())).thenReturn(mock(GuestOSVO.class)); } @@ -133,6 +141,14 @@ public class VMSnapshotManagerTest { _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); } + // hypervisorCapabilities not expected case + @Test(expected=InvalidParameterValueException.class) + public void testAllocVMSnapshotF6() throws ResourceAllocationException{ + when(vmMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.Ovm); + when(_hypervisorCapabilitiesDao.isVmSnapshotEnabled(Hypervisor.HypervisorType.Ovm, "default")).thenReturn(false); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + // vm state not in [running, stopped] case @Test(expected=InvalidParameterValueException.class) public void testAllocVMSnapshotF2() throws ResourceAllocationException{ diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index ff9ca1de42a..c7c8b5b49ca 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -444,3 +444,7 @@ CREATE TABLE `cloud`.`vm_snapshots` ( CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`), CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud`.`hypervisor_capabilities` ADD COLUMN `vm_snapshot_enabled` tinyint(1) DEFAULT 0 NOT NULL COMMENT 'Whether VM snapshot is supported by hypervisor'; +UPDATE `cloud`.`hypervisor_capabilities` SET `vm_snapshot_enabled`=1 WHERE `hypervisor_type` in ('VMware', 'XenServer'); + From 97c2f7d0c4b330f3456e8c1b5236e280b9931197 Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Wed, 10 Apr 2013 12:53:25 +0800 Subject: [PATCH 57/81] add API version for vm snapshot related API commands --- .../api/command/user/vmsnapshot/CreateVMSnapshotCmd.java | 2 +- .../api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java | 2 +- .../api/command/user/vmsnapshot/ListVMSnapshotCmd.java | 2 +- .../api/command/user/vmsnapshot/RevertToSnapshotCmd.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java index f0dbf16b250..aa9feb8e2a3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java @@ -34,7 +34,7 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "createVMSnapshot", description = "Creates snapshot for a vm.", responseObject = VMSnapshotResponse.class) +@APICommand(name = "createVMSnapshot", description = "Creates snapshot for a vm.", responseObject = VMSnapshotResponse.class, since="4.2.0") public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd { public static final Logger s_logger = Logger diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java index a2b2c08b381..bda84c893a5 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java @@ -32,7 +32,7 @@ import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name="deleteVMSnapshot", description = "Deletes a vmsnapshot.", responseObject = SuccessResponse.class) +@APICommand(name="deleteVMSnapshot", description = "Deletes a vmsnapshot.", responseObject = SuccessResponse.class, since="4.2.0") public class DeleteVMSnapshotCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger .getLogger(DeleteVMSnapshotCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java index 936d348950d..82fb9e57877 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java @@ -30,7 +30,7 @@ import org.apache.cloudstack.api.response.VMSnapshotResponse; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name="listVMSnapshot", description = "List virtual machine snapshot by conditions", responseObject = VMSnapshotResponse.class) +@APICommand(name="listVMSnapshot", description = "List virtual machine snapshot by conditions", responseObject = VMSnapshotResponse.class, since="4.2.0") public class ListVMSnapshotCmd extends BaseListTaggedResourcesCmd { private static final String s_name = "listvmsnapshotresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java index d7b4599d6c4..ea7bf60d6a3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java @@ -37,7 +37,7 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "revertToSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class) +@APICommand(name = "revertToSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class, since="4.2.0") public class RevertToSnapshotCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger .getLogger(RevertToSnapshotCmd.class.getName()); From 499bc6306e11e8ca74931b733d7717ac09fff5b7 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 10 Apr 2013 11:43:59 +0530 Subject: [PATCH 58/81] adding error handling mechanism to scale up vm functionality --- ui/scripts/instances.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 80c1634199a..1c4c38c75fd 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -1084,7 +1084,7 @@ label:'scaleUp VM', action: function(args) { $.ajax({ - url: createURL("scaleVirtualMachine&id=" + args.context.instances[0].id), + url: createURL("scaleVirtualMachine&id=" + args.context.instances[0].id + "&serviceofferingid=" + args.context.instances[0].serviceofferingid), dataType: "json", async: true, success: function(json) { @@ -1101,7 +1101,11 @@ } } ); + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); } + }); }, messages: { From 643ee1d15e27e9974e3cb40fac160805e46cbd55 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 13:05:54 +0530 Subject: [PATCH 59/81] CLOUDSTACK-1867: Export both ova and vmdk systemvm appliances for VMWare Signed-off-by: Rohit Yadav --- tools/appliance/build.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/appliance/build.sh b/tools/appliance/build.sh index f1ee4a64b40..c39d38a4e76 100644 --- a/tools/appliance/build.sh +++ b/tools/appliance/build.sh @@ -81,7 +81,10 @@ rm raw.img bzip2 $appliance-$build_date-$branch-kvm.qcow2 echo "$appliance exported for KVM: dist/$appliance-$build_date-$branch-kvm.qcow2.bz2" -# Export for VMWare vSphere +# Export both ova and vmdk for VMWare +vboxmanage clonehd $hdd_uuid $appliance-$build_date-$branch-vmware.vmdk --format VMDK +bzip2 $appliance-$build_date-$branch-vmware.vmdk +echo "$appliance exported for VMWare: dist/$appliance-$build_date-$branch-vmware.vmdk.bz2" vboxmanage export $machine_uuid --output $appliance-$build_date-$branch-vmware.ova echo "$appliance exported for VMWare: dist/$appliance-$build_date-$branch-vmware.ova" From 9213fa6f3a863480fd0faeacb4271f46f9ea559a Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 13:21:24 +0530 Subject: [PATCH 60/81] CLOUDSTACK-797: Removing cmds CreatePrivateNetworkCmd and DestroyConsoleProxyCmd Removed as per Kishan. We still don't know if we want to keep: ListRecurringSnapshotScheduleCmd.java Signed-off-by: Rohit Yadav --- .../api/commands/CreatePrivateNetworkCmd.java | 197 ------------------ .../api/commands/DestroyConsoleProxyCmd.java | 85 -------- 2 files changed, 282 deletions(-) delete mode 100644 api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java delete mode 100644 api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java diff --git a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java b/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java deleted file mode 100644 index 2b63c64425d..00000000000 --- a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java +++ /dev/null @@ -1,197 +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.api.commands; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCreateCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.api.response.PhysicalNetworkResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.log4j.Logger; - -import com.cloud.event.EventTypes; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.network.Network; -import com.cloud.user.UserContext; - -//@APICommand(description="Creates a private network", responseObject=NetworkResponse.class) -public class CreatePrivateNetworkCmd extends BaseAsyncCreateCmd { - public static final Logger s_logger = Logger.getLogger(CreatePrivateNetworkCmd.class.getName()); - - private static final String s_name = "createnetworkresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="the name of the network") - private String name; - - @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, required=true, description="the display text of the network") - private String displayText; - - @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.UUID, entityType = PhysicalNetworkResponse.class, - required=true, description="the Physical Network ID the network belongs to") - private Long physicalNetworkId; - - @Parameter(name=ApiConstants.GATEWAY, type=CommandType.STRING, required=true, description="the gateway of the network") - private String gateway; - - @Parameter(name=ApiConstants.NETMASK, type=CommandType.STRING, required=true, description="the netmask of the network") - private String netmask; - - @Parameter(name=ApiConstants.START_IP, type=CommandType.STRING, required=true, description="the beginning IP address in the network IP range") - private String startIp; - - @Parameter(name=ApiConstants.END_IP, type=CommandType.STRING, description="the ending IP address in the network IP" + - " range. If not specified, will be defaulted to startIP") - private String endIp; - - @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, required=true, description="the ID or VID of the network") - private String vlan; - - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="account who will own the network") - private String accountName; - - @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, - description="an optional project for the ssh key") - private Long projectId; - - @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType = DomainResponse.class, - description="domain ID of the account owning a network") - private Long domainId; - - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public String getGateway() { - return gateway; - } - - public String getVlan() { - return vlan; - } - - public String getAccountName() { - return accountName; - } - - public Long getDomainId() { - return domainId; - } - - public String getNetmask() { - return netmask; - } - - public String getStartIp() { - return startIp; - } - - public String getNetworkName() { - return name; - } - - public String getDisplayText() { - return displayText; - } - - public Long getProjectId() { - return projectId; - } - - public long getPhysicalNetworkId() { - return physicalNetworkId; - } - - public String getEndIp() { - return endIp; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } - - - @Override - public void create() throws ResourceAllocationException { - Network result = null; - try { - result = _networkService.createPrivateNetwork(getNetworkName(), getDisplayText(), getPhysicalNetworkId(), getVlan(), - getStartIp(), getEndIp(), getGateway(), getNetmask(), getEntityOwnerId(), null); - } catch (InsufficientCapacityException ex){ - s_logger.info(ex); - s_logger.trace(ex); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); - } catch (ConcurrentOperationException ex) { - s_logger.warn("Exception: ", ex); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); - } - - if (result != null) { - this.setEntityId(result.getId()); - this.setEntityUuid(result.getUuid()); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a Private network"); - } - } - - @Override - public void execute() throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException{ - Network result = _networkService.getNetwork(getEntityId()); - if (result != null) { - NetworkResponse response = _responseGenerator.createNetworkResponse(result); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create private network"); - } - } - - @Override - public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); - if (accountId == null) { - return UserContext.current().getCaller().getId(); - } - return accountId; - } - - @Override - public String getEventType() { - return EventTypes.EVENT_NETWORK_CREATE; - } - - @Override - public String getEventDescription() { - return "creating private network"; - - } - -} diff --git a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java b/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java deleted file mode 100644 index f3210ce06f4..00000000000 --- a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java +++ /dev/null @@ -1,85 +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.api.commands; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseAsyncCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.log4j.Logger; - -import com.cloud.event.EventTypes; -import com.cloud.exception.UnsupportedServiceException; -import com.cloud.user.Account; -import com.cloud.user.UserContext; - -//@APICommand(description="Destroys console proxy", responseObject=SuccessResponse.class) -public class DestroyConsoleProxyCmd extends BaseAsyncCmd { - public static final Logger s_logger = Logger.getLogger(DestroyConsoleProxyCmd.class.getName()); - - private static final String s_name = "destroyconsoleproxyresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="console proxy ID") - private Long id; - - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public long getEntityOwnerId() { - Account account = (Account)UserContext.current().getCaller(); - if (account != null) { - return account.getId(); - } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked - } - - @Override - public String getEventType() { - return EventTypes.EVENT_PROXY_DESTROY; - } - - @Override - public String getEventDescription() { - return "destroying console proxy: " + getId(); - } - - @Override - public void execute(){ - throw new UnsupportedServiceException("Use destroySystemVm API instead"); - } -} From a194720417c63a128446c65e48c945fa973f49a9 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 10 Apr 2013 13:59:55 +0530 Subject: [PATCH 61/81] Refactoring the code in network.js to remove the disabling of egress and load balancers tab --- ui/scripts/network.js | 398 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 1 deletion(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 6f6a073812f..6c311923faf 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -924,7 +924,7 @@ } if (isVPC || isAdvancedSGZone || hasSRXFirewall) { - hiddenTabs.push('egressRules'); + hiddenTabs.push('egressRules'); } return hiddenTabs; @@ -1108,6 +1108,401 @@ } }); } + }, + + egressRules: { + title: 'label.egress.rules', + custom: function(args) { + var context = args.context; + + return $('
').multiEdit({ + context: context, + noSelect: true, + noHeaderActionsColumn: true, + fields: { + 'cidrlist': { edit: true, label: 'label.cidr.list', isOptional: true }, + 'protocol': { + label: 'label.protocol', + select: function(args) { + args.$select.change(function() { + var $inputs = args.$form.find('th, td'); + var $icmpFields = $inputs.filter(function() { + var name = $(this).attr('rel'); + + return $.inArray(name, [ + 'icmptype', + 'icmpcode' + ]) > -1; + }); + var $otherFields = $inputs.filter(function() { + var name = $(this).attr('rel'); + + return name != 'cidrlist' && + name != 'icmptype' && + name != 'icmpcode' && + name != 'protocol' && + name != 'add-rule'; + }); + + if ($(this).val() == 'icmp') { + $icmpFields.show(); + $otherFields.hide(); + } else if ($(this).val() == 'all') { + $icmpFields.hide(); + $otherFields.hide(); + } else { + $icmpFields.hide(); + $otherFields.show(); + } + }); + + args.response.success({ + data: [ + { name: 'tcp', description: 'TCP' }, + { name: 'udp', description: 'UDP' }, + { name: 'icmp', description: 'ICMP' }, + { name: 'all', description: 'All' } + ] + }); + } + }, + 'startport': { edit: true, label: 'label.start.port', isOptional: true }, + 'endport': { edit: true, label: 'label.end.port', isOptional: true }, + 'icmptype': { edit: true, label: 'ICMP.type', isHidden: true, isOptional: true }, + 'icmpcode': { edit: true, label: 'ICMP.code', isHidden: true, isOptional: true }, + 'add-rule': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + var data = { + protocol: args.data.protocol, + cidrlist: args.data.cidrlist, + networkid: args.context.networks[0].id + }; + + if (args.data.icmptype && args.data.icmpcode) { // ICMP + $.extend(data, { + icmptype: args.data.icmptype, + icmpcode: args.data.icmpcode + }); + } else { // TCP/UDP + $.extend(data, { + startport: args.data.startport, + endport: args.data.endport + }); + } + + $.ajax({ + url: createURL('createEgressFirewallRule'), + data: data, + dataType: 'json', + async: true, + success: function(json) { + var jobId = json.createegressfirewallruleresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobId + }, + notification: { + label: 'label.add.egress.rule', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + }, + actions: { + destroy: { + label: 'label.remove.rule', + action: function(args) { + $.ajax({ + url: createURL('deleteEgressFirewallRule'), + data: { + id: args.context.multiRule[0].id + }, + dataType: 'json', + async: true, + success: function(data) { + var jobID = data.deleteegressfirewallruleresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobID + }, + notification: { + label: 'label.remove.egress.rule', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + }, + ignoreEmptyFields: true, + dataProvider: function(args) { + $.ajax({ + url: createURL('listEgressFirewallRules'), + data: { + listAll: true, + networkid: args.context.networks[0].id + }, + dataType: 'json', + async: true, + success: function(json) { + var response = json.listegressfirewallrulesresponse.firewallrule ? + json.listegressfirewallrulesresponse.firewallrule : []; + + args.response.success({ + data: $.map(response, function(rule) { + if (rule.protocol == 'all') { + $.extend(rule, { + startport: 'All', + endport: 'All' + }); + } else if (rule.protocol == 'tcp' || rule.protocol == 'udp') { + if (!rule.startport) { + rule.startport = ' '; + } + + if (!rule.endport) { + rule.endport = ' '; + } + } + + return rule; + }) + }); + } + }); + } + }); + } + }, + + addloadBalancer: { // EIP/ELB Basic zone: Add Load Balancer tab in network detailView + title: 'label.add.load.balancer', + custom: function(args) { + var context = args.context; + + return $('
').addClass('loadBalancer').multiEdit( + { + context: context, + listView: $.extend(true, {}, cloudStack.sections.instances, { + listView: { + filters: false, + + dataProvider: function(args) { + var data = { + page: args.page, + pageSize: pageSize, + domainid: g_domainid, + account: g_account, + networkid: args.context.networks[0].id, + listAll: true + }; + + $.ajax({ + url: createURL('listVirtualMachines'), + data: data, + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + data: $.grep( + data.listvirtualmachinesresponse.virtualmachine ? + data.listvirtualmachinesresponse.virtualmachine : [], + function(instance) { + var nonAutoScale=0; + if(instance.displayname == null) + nonAutoScale = 1; + else{ + if( instance.displayname.match(/AutoScale-LB-/)==null) + nonAutoScale =1; + else { + if(instance.displayname.match(/AutoScale-LB-/).length) + nonAutoScale =0; + } + } + var isActiveState= $.inArray(instance.state, ['Destroyed','Expunging']) == -1; + return nonAutoScale && isActiveState; + } + ) + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } + } + }), + multipleAdd: true, + fields: { + 'name': { edit: true, label: 'label.name' }, + 'publicport': { edit: true, label: 'label.public.port' }, + 'privateport': { edit: true, label: 'label.private.port' }, + 'algorithm': { + label: 'label.algorithm', + select: function(args) { + args.response.success({ + data: [ + { name: 'roundrobin', description: _l('label.round.robin') }, + { name: 'leastconn', description: _l('label.least.connections') }, + { name: 'source', description: _l('label.source') } + ] + }); + } + }, + 'sticky': { + label: 'label.stickiness', + custom: { + buttonLabel: 'label.configure', + action: cloudStack.lbStickyPolicy.dialog() + } + }, + 'autoScale': { + label: 'AutoScale', + custom: { + requireValidation: true, + buttonLabel: 'label.configure', + action: cloudStack.uiCustom.autoscaler(cloudStack.autoscaler) + } + }, + 'add-vm': { + label: 'label.add.vms', + addButton: true + } + }, + + add: { //basic zone - elastic IP - Add Load Balancer tab - Add VMs button + label: 'label.add.vms', + action: function(args) { + var data = { + algorithm: args.data.algorithm, + name: args.data.name, + privateport: args.data.privateport, + publicport: args.data.publicport, + openfirewall: false, + domainid: g_domainid, + account: g_account + }; + + if('vpc' in args.context) { //from VPC section + if(args.data.tier == null) { + args.response.error('Tier is required'); + return; + } + $.extend(data, { + networkid: args.data.tier + }); + } + else { //from Guest Network section + $.extend(data, { + networkid: args.context.networks[0].id + }); + } + + var stickyData = $.extend(true, {}, args.data.sticky); + + $.ajax({ + url: createURL('createLoadBalancerRule'), + data: data, + dataType: 'json', + async: true, + success: function(data) { + var itemData = args.itemData; + //var jobID = data.createloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule + + $.ajax({ + url: createURL('assignToLoadBalancerRule'), + data: { + id: data.createloadbalancerruleresponse.id, + virtualmachineids: $.map(itemData, function(elem) { + return elem.id; + }).join(',') + }, + dataType: 'json', + async: true, + success: function(data) { + var jobID = data.assigntoloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule + var lbCreationComplete = false; + + args.response.success({ + _custom: { + jobId: jobID + }, + notification: { + label: 'label.add.load.balancer', + poll: function(args) { + var complete = args.complete; + var error = args.error; + + pollAsyncJobResult({ + _custom: args._custom, + complete: function(args) { + if (lbCreationComplete) { + return; + } + + lbCreationComplete = true; + cloudStack.dialog.notice({ + message: _l('message.add.load.balancer.under.ip') + + args.data.loadbalancer.publicip + }); + + if (stickyData && + stickyData.methodname && + stickyData.methodname != 'None') { + cloudStack.lbStickyPolicy.actions.add( + args.data.loadbalancer.id, + stickyData, + complete, // Complete + complete // Error + ); + } else { + complete(); + } + }, + error: error + }); + } + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } + }, + + + dataProvider: function(args) { + args.response.success({ //no LB listing in AddLoadBalancer tab + data: [] + }); + } + } + ); + } } } } @@ -2783,6 +3178,7 @@ } } }, + itemActions: { add: { label: 'label.add.vms.to.lb', From c8f20031380cada2782c8849fcb5fcfbb1eb41fe Mon Sep 17 00:00:00 2001 From: Jayapal Date: Wed, 10 Apr 2013 12:19:06 +0530 Subject: [PATCH 62/81] CLOUDSTACK-1862 Added vm ip address info in the list PF rules response Signed-off-by: Abhinandan Prateek --- .../api/response/FirewallRuleResponse.java | 12 ++++++++++++ server/src/com/cloud/api/ApiResponseHelper.java | 1 + 2 files changed, 13 insertions(+) diff --git a/api/src/org/apache/cloudstack/api/response/FirewallRuleResponse.java b/api/src/org/apache/cloudstack/api/response/FirewallRuleResponse.java index 08722ae2c6b..787410a24c5 100644 --- a/api/src/org/apache/cloudstack/api/response/FirewallRuleResponse.java +++ b/api/src/org/apache/cloudstack/api/response/FirewallRuleResponse.java @@ -71,6 +71,18 @@ public class FirewallRuleResponse extends BaseResponse { @SerializedName(ApiConstants.TAGS) @Param(description="the list of resource tags associated with the rule", responseObject = ResourceTagResponse.class) private List tags; + @SerializedName(ApiConstants.VM_GUEST_IP) @Param(description="the vm ip address for the port forwarding rule") + private String destNatVmIp; + + + public String getDestNatVmIp() { + return destNatVmIp; + } + + public void setDestNatVmIp(String destNatVmIp) { + this.destNatVmIp = destNatVmIp; + } + @Override public String getObjectId() { diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 64be7f8758a..93b755a665a 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -965,6 +965,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setPublicIpAddress(ip.getAddress().addr()); if (ip != null && fwRule.getDestinationIpAddress() != null) { + response.setDestNatVmIp(fwRule.getDestinationIpAddress().toString()); UserVm vm = ApiDBUtils.findUserVmById(fwRule.getVirtualMachineId()); if (vm != null) { response.setVirtualMachineId(vm.getUuid()); From 9eaef0e7c717418a8268ea1da09d69904efc1c8b Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 16:03:46 +0530 Subject: [PATCH 63/81] appliance: Fix systemvm's preseed to give more space for /usr Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/preseed.cfg | 6 +++--- tools/appliance/definitions/systemvmtemplate64/preseed.cfg | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index ac9edd31213..aa75e134fa1 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 400 40 500 ext4 \ + 300 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 500 30 1000 ext4 \ + 650 30 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -156,7 +156,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ . \ - 500 60 1000 ext4 \ + 450 60 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index ac9edd31213..aa75e134fa1 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 400 40 500 ext4 \ + 300 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 500 30 1000 ext4 \ + 650 30 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -156,7 +156,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ . \ - 500 60 1000 ext4 \ + 450 60 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ From 26d772f74054962cf847336d0680eff946647ef9 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 16:06:06 +0530 Subject: [PATCH 64/81] CLOUDSTACK-1867: Install vmwaretools while buildling systemvms Signed-off-by: Rohit Yadav --- .../definitions/systemvmtemplate/postinstall.sh | 17 +++++++++++++++-- .../systemvmtemplate64/postinstall.sh | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index 5d529de038a..bf593b65834 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -50,8 +50,6 @@ install_packages() { echo "openswan openswan/install_x509_certificate seen true" | debconf-set-selections apt-get --no-install-recommends -q -y --force-yes install openswan - # vmware tools - apt-get --no-install-recommends -q -y --force-yes install open-vm-tools # xenstore utils apt-get --no-install-recommends -q -y --force-yes install xenstore-utils libxenstore3.0 # keepalived and conntrackd for redundant router @@ -64,6 +62,21 @@ install_packages() { echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections apt-get --no-install-recommends -q -y --force-yes install iptables-persistent + + # vmware tools + # uncomment for opensource vmware tool: + # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools + apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + PREVDIR=$PWD + cd /opt + wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz + tar xzf VMwareTools-9.2.1-818201.tar.gz + rm VMwareTools-*.tar.gz + cd vmware-tools-distrib + ./vmware-install.pl -d + cd $PREV + rm -fr /opt/vmware-tools-distrib + apt-get -q -y --force-yes purge build-essential gcc } setup_accounts() { diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index 5d529de038a..bf593b65834 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -50,8 +50,6 @@ install_packages() { echo "openswan openswan/install_x509_certificate seen true" | debconf-set-selections apt-get --no-install-recommends -q -y --force-yes install openswan - # vmware tools - apt-get --no-install-recommends -q -y --force-yes install open-vm-tools # xenstore utils apt-get --no-install-recommends -q -y --force-yes install xenstore-utils libxenstore3.0 # keepalived and conntrackd for redundant router @@ -64,6 +62,21 @@ install_packages() { echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections apt-get --no-install-recommends -q -y --force-yes install iptables-persistent + + # vmware tools + # uncomment for opensource vmware tool: + # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools + apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + PREVDIR=$PWD + cd /opt + wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz + tar xzf VMwareTools-9.2.1-818201.tar.gz + rm VMwareTools-*.tar.gz + cd vmware-tools-distrib + ./vmware-install.pl -d + cd $PREV + rm -fr /opt/vmware-tools-distrib + apt-get -q -y --force-yes purge build-essential gcc } setup_accounts() { From f8bbd88a42ab64bf10d0ca2681c246810a4d2be0 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Wed, 10 Apr 2013 16:15:50 +0530 Subject: [PATCH 65/81] maven+marvin: Fix up the activation property activation property marvin.config will attempt to deploy a zone again on running the marvin.test profile. This is unnecessary as marvin.setup already finishes deploying a zone. Signed-off-by: Prasanna Santhanam --- setup/dev/basic.cfg | 6 +++--- setup/dev/local.cfg | 44 ++++++++++++++++++++++++++++++++++++++++++++ tools/marvin/pom.xml | 18 ++++-------------- 3 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 setup/dev/local.cfg diff --git a/setup/dev/basic.cfg b/setup/dev/basic.cfg index fb99b8b5498..3f56a3ce980 100644 --- a/setup/dev/basic.cfg +++ b/setup/dev/basic.cfg @@ -57,7 +57,7 @@ "startip": "60.147.41.2", "endip": "60.147.41.254", "netmask": "255.255.255.0", - "gateway": "60.147.40.1" + "gateway": "60.147.41.1" } ], "netmask": "255.255.255.0", @@ -102,11 +102,11 @@ "logger": [ { "name": "TestClient", - "file": "/var/log/testclient.log" + "file": "/tmp/testclient.log" }, { "name": "TestCase", - "file": "/var/log/testcase.log" + "file": "/tmp/testcase.log" } ], "globalConfig": [ diff --git a/setup/dev/local.cfg b/setup/dev/local.cfg new file mode 100644 index 00000000000..ce956a54e63 --- /dev/null +++ b/setup/dev/local.cfg @@ -0,0 +1,44 @@ +# 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. + +{ + "dbSvr": { + "dbSvr": "localhost", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": [ + { + "name": "TestClient", + "file": "/tmp/testclient.log" + }, + { + "name": "TestCase", + "file": "/tmp/testcase.log" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "localhost", + "passwd": "password", + "user": "root", + "port": 8096 + } + ] +} diff --git a/tools/marvin/pom.xml b/tools/marvin/pom.xml index ce6ce388125..c0505664486 100644 --- a/tools/marvin/pom.xml +++ b/tools/marvin/pom.xml @@ -157,12 +157,7 @@ marvin.setup ${user.dir}/setup/dev/advanced.cfg - - - - marvin.config - - + @@ -182,7 +177,7 @@ deployAndRun.py -c - ${marvin.config} + ${user.dir}/${marvin.config} -t /tmp/t.log -r @@ -200,15 +195,10 @@ marvin.test - ${user.dir}/setup/dev/advanced.cfg simulator test/integration/smoke + ${user.dir}/setup/dev/advanced.cfg - - - marvin.config - - @@ -228,7 +218,7 @@ --with-marvin --marvin-config - ${marvin.config} + ${user.dir}/${marvin.config} --load -a tags=${tag} From b50201e69c4aef2d7bf0f82aa90f6674b7a69ab2 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 16:23:21 +0530 Subject: [PATCH 66/81] appliance: Make more room in /usr, installing vmwaretools takes 'em all Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/postinstall.sh | 1 + tools/appliance/definitions/systemvmtemplate/preseed.cfg | 4 ++-- tools/appliance/definitions/systemvmtemplate64/postinstall.sh | 1 + tools/appliance/definitions/systemvmtemplate64/preseed.cfg | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index bf593b65834..c0a7e87da2f 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -67,6 +67,7 @@ install_packages() { # uncomment for opensource vmware tool: # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + df -h PREVDIR=$PWD cd /opt wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index aa75e134fa1..5d699c869e9 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 300 40 500 ext4 \ + 200 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 650 30 1000 ext4 \ + 750 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index bf593b65834..c0a7e87da2f 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -67,6 +67,7 @@ install_packages() { # uncomment for opensource vmware tool: # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + df -h PREVDIR=$PWD cd /opt wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index aa75e134fa1..5d699c869e9 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 300 40 500 ext4 \ + 200 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 650 30 1000 ext4 \ + 750 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ From dc3c834a22b3d33ff3c0ab889feb1e20e234d498 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 16:47:33 +0530 Subject: [PATCH 67/81] appliance: Feed more disk space to /usr Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/preseed.cfg | 6 +++--- tools/appliance/definitions/systemvmtemplate64/preseed.cfg | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index 5d699c869e9..4050498e536 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 200 40 400 ext4 \ + 250 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,12 +146,12 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 750 20 1100 ext4 \ + 950 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ . \ - 400 40 500 ext4 \ + 300 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index 5d699c869e9..4050498e536 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 200 40 400 ext4 \ + 250 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,12 +146,12 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 750 20 1100 ext4 \ + 950 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ . \ - 400 40 500 ext4 \ + 300 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ From bdc82627fef2b9393b46128a48b73bedee835760 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 17:04:51 +0530 Subject: [PATCH 68/81] appliance: Permute partition space such as it succeeds during partitioning Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/preseed.cfg | 8 ++++---- .../appliance/definitions/systemvmtemplate64/preseed.cfg | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index 4050498e536..cafb9c3704a 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -130,7 +130,7 @@ d-i partman-auto/choose_recipe select atomic d-i partman-auto/expert_recipe string \ boot-root :: \ - 40 50 100 ext4 \ + 30 50 100 ext4 \ $primary{ } $bootable{ } \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ @@ -141,12 +141,12 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ . \ - 60 100 200 ext4 \ + 50 100 200 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 950 20 1100 ext4 \ + 850 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -166,7 +166,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /tmp } \ . \ - 64 512 300% linux-swap \ + 50 512 300% linux-swap \ method{ swap } format{ } \ . diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index 4050498e536..cafb9c3704a 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -130,7 +130,7 @@ d-i partman-auto/choose_recipe select atomic d-i partman-auto/expert_recipe string \ boot-root :: \ - 40 50 100 ext4 \ + 30 50 100 ext4 \ $primary{ } $bootable{ } \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ @@ -141,12 +141,12 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ . \ - 60 100 200 ext4 \ + 50 100 200 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 950 20 1100 ext4 \ + 850 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -166,7 +166,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /tmp } \ . \ - 64 512 300% linux-swap \ + 50 512 300% linux-swap \ method{ swap } format{ } \ . From c2fb870f7cab75443b9cd6283acc7b0f2f14067a Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 9 Apr 2013 12:32:19 +0530 Subject: [PATCH 69/81] CLOUDSTACK-772 --- docs/en-US/Book_Info.xml | 2 +- docs/en-US/add-clusters-vsphere.xml | 104 +++- docs/en-US/images/add-cluster.png | Bin 46302 -> 35697 bytes docs/en-US/images/dvswitch-config.png | Bin 0 -> 41955 bytes docs/en-US/images/dvswitchconfig.png | Bin 0 -> 38642 bytes docs/en-US/vmware-cluster-config-dvswitch.xml | 193 +++++++ docs/en-US/vmware-install.xml | 513 ++++++++++++------ 7 files changed, 615 insertions(+), 197 deletions(-) create mode 100644 docs/en-US/images/dvswitch-config.png create mode 100644 docs/en-US/images/dvswitchconfig.png create mode 100644 docs/en-US/vmware-cluster-config-dvswitch.xml diff --git a/docs/en-US/Book_Info.xml b/docs/en-US/Book_Info.xml index c125ab8de2b..0ab84c40d9b 100644 --- a/docs/en-US/Book_Info.xml +++ b/docs/en-US/Book_Info.xml @@ -27,7 +27,7 @@ &PRODUCT; Guide Revised August 9, 2012 10:48 pm Pacific Apache CloudStack - 4.0.0-incubating + 4.0.0 1 diff --git a/docs/en-US/add-clusters-vsphere.xml b/docs/en-US/add-clusters-vsphere.xml index 6b2dff2a595..c3a0902be8f 100644 --- a/docs/en-US/add-clusters-vsphere.xml +++ b/docs/en-US/add-clusters-vsphere.xml @@ -71,38 +71,106 @@ In Hypervisor, choose VMware. - Provide the following information in the dialog. The fields below make reference to + Provide the following information in the dialog. The fields below make reference to the values from vCenter. + + + + + + addcluster.png: add a cluster + + - Cluster Name. Enter the name of the cluster you created in vCenter. For example, - "cloud.cluster.2.2.1" + Cluster Name: Enter the name of the cluster you + created in vCenter. For example, "cloud.cluster.2.2.1" - vCenter Host. Enter the hostname or IP address of the vCenter server. + vCenter Username: Enter the username that &PRODUCT; + should use to connect to vCenter. This user must have all the administrative + privileges. - vCenter Username. Enter the username that &PRODUCT; should use to connect to - vCenter. This user must have all administrative privileges. + CPU overcommit ratio: Enter the CPU overcommit + ratio for the cluster. The value you enter determines the CPU consumption of each VM in + the selected cluster. By increasing the over-provisioning ratio, more resource capacity + will be used. If no value is specified, the value is defaulted to 1, which implies no + over-provisioning is done. - vCenter Password. Enter the password for the user named above + RAM overcommit ratio: Enter the RAM overcommit + ratio for the cluster. The value you enter determines the memory consumption of each VM + in the selected cluster. By increasing the over-provisioning ratio, more resource + capacity will be used. If no value is specified, the value is defaulted to 1, which + implies no over-provisioning is done. - vCenter Datacenter. Enter the vCenter datacenter that the cluster is in. For - example, "cloud.dc.VM". + vCenter Host: Enter the hostname or IP address of + the vCenter server. + + + vCenter Password: Enter the password for the user + named above. + + + vCenter Datacenter: Enter the vCenter datacenter + that the cluster is in. For example, "cloud.dc.VM". + + + Override Public Traffic: Enable this option to + override the zone-wide public traffic for the cluster you are creating. + + + Public Traffic vSwitch Type: This option is + displayed only if you enable the Override Public Traffic option. Select a desirable + switch. If the vmware.use.dvswitch global parameter is true, the default option will be + VMware vNetwork Distributed Virtual Switch. + If you have enabled Nexus dvSwitch in the environment, the following parameters for + dvSwitch configuration are displayed: + + + Nexus dvSwitch IP Address: The IP address of the Nexus VSM appliance. + + + Nexus dvSwitch Username: The username required to access the Nexus VSM + appliance. + + + Nexus dvSwitch Password: The password associated with the username specified + above. + + + + + Override Guest Traffic: Enable this option to + override the zone-wide guest traffic for the cluster you are creating. + + + Guest Traffic vSwitch Type: This option is + displayed only if you enable the Override Guest Traffic option. Select a desirable + switch. + If the vmware.use.dvswitch global parameter is true, the default option will be + VMware vNetwork Distributed Virtual Switch. + If you have enabled Nexus dvSwitch in the environment, the following parameters for + dvSwitch configuration are displayed: + + + Nexus dvSwitch IP Address: The IP address of the Nexus VSM appliance. + + + Nexus dvSwitch Username: The username required to access the Nexus VSM + appliance. + + + Nexus dvSwitch Password: The password associated with the username specified + above. + + - - - - - - addcluster.png: add cluster - - There might be a slight delay while the cluster is provisioned. It will - automatically display in the UI + automatically display in the UI. diff --git a/docs/en-US/images/add-cluster.png b/docs/en-US/images/add-cluster.png index 383f375ebedd62d9b294a56f777ed4b8c0d92e10..4b24ec721d8ef25968245184a30bc5a0eef5d62c 100644 GIT binary patch literal 35697 zcmdSAWmH?;+wO}O0u;9*r3DHEhZZSNDDG}0xKo0=yGsicFYfNaJwTD-grEglC{o;g zhd$5y?sxz98RL9ChcOr=A!}vLwdR`F@4D_gkt#~kxPWH>BqStUSs4j6BqZbnBqWqx zOccZ`-eJ9ih(E~AYSLmzRby1!h$m>4?-k!8A=Q4uzBNWiJjZg7`QVI%^tA8s7kSXW z#0&{38bem%y@rRuUWca()m-Ke?8S%n_S^WBl#>;D#-X(3b2v|8qFI9M*w8O}oI2>h)e)&cP0ul$|^wY zCDv&WHSLB_`$0!j*|nRitGjD{)6PANSv3^oeh2HwzF!7itN^`_hZtFaKpO8qwbEUu zg&e*|(Vu+Qu(NOeSc=@FjSKxbxq?;nv}{gill~URTf03ie%H?vh?0Fd(pB(IgO&kZ z$MTC_6lWA~l#nRCC>j;AoG1|zx&pG6sMdU<;ngtFCz1ON>F0Orbf0`&`=%Ay^sE2a z&ez>Y3zZ^~1a({Y1sObA(ce95} z?e6d4wo`1{GbbJFJ*H4;({*#hv2x$1*zxJswtl2!EB0|bpTqv{Z{gsNqx1bdpZ_(B zzw>U|+<}_`u3HTHQ(Wb}4*shPvVH1iq1kuOte#_6?X}-L`22(o^0~G~#`+-+DF?-x ziMA%RrC8Z_H>u!v_JiVL*DdU_$@}2m!RmN+BKWCj&R{^hdO=m@ybWhpw&dj0Sh0U4M2Zq&$!*U9emP0 zR}ykHxn)`AWAStRDKFA-eoM)x{j0YAB9p^K-*=si4;Z)XbKHD*?%g&pVgpK$y3yGi zlkSALDsIdjaa2*?iRj*~iz{CrRqxpexs&`BH=Ms58Z38M?~M{N_zb<$$n~4L!jp{_ z$3skuU!usUpifus^zZx|H!DSK+s;x%+O8(YZ2a5pe|6m+9{c-kR7$5ykw7}H1}>j> zx@>o~dtG%s{2G=Ptm<9#7xdU|`Z&fAdmcSL*WqPOz@>278DKN=kWWc@_?vFrw!h1e z9J8WgN$_dal{?gV4%KgM{=n~g$l1R9>yW8m0d^ebO=xw8tGBshi7!i4mxksie@)Rl zWnnD6pSMfXBHlK}=6-*0&7@jN+DreG%4pyimaSlrjc{2XZ3hBW0$&s3!+VSHIhhE* z;%Ro>QiALs-v2%mvFf_Msm6U6Do=R;U#vdB9j*I1%cHOt{oe(OJV^gsl{TQhUhaAx z)FiUid|%zkbMK&Ox}GZXFj~0SJ={Ar+&kOlZ&81I+h5i??t3Qaf9EmF*m+BJe|Dyb z1s1Ls$ZE@il4UL5&fQdfudZl!E*gv3V+*Viw0!Ql-^Sh2p9}kbcwWj_c2s?AdS@+< zGJknn?|(BixXAIndK9neCFXs@aZz2k2!UL|jmK=CO;@1*8m=`1JO2UV-un#sdt1N;_7YV|%g+jBE^1iPqj zIXc_(8kFr*A-rDaU^;F(PDeb|(NM!w4@7xc2~9X=N8Fj)EIBo2SC)K?{101Ks1JA2 z_76Y1o?najzrOVMdGI;dYlf#A+;501_|mMbUUl8C?uq!_Gh*f4jfUCZuV*G*P10ri zURxsd+$QlNM$xzH51in+R@d`CPm3l(L33}L^XGZI5PjhaC~uRt*3Jk);rK~V7<2qr z8Py4O`rOM|{j0O^Is1e-b^CWG)WZ5Ft@0i^UE9+6(Ja`eBD^P%I)}T1tiZrz8HJTX z6b+*!067e1*>$~#_WQZ;-05Uj+ll|(^2SAn(~<8F!He6YqZE!M@2jQd^ARifI=`du z^+vt>TJz7P<%_*{*!Br)4LloL$B*< z8eg~X>V{-jAH07hbZ-%`@Q6J0VtC;HCIdNOA8q*&Mh~?iZsrO##LbLXeUpJ^c@?(& z;P)HaCv6}2;b75qvt#k*$BoGkZ^-HW;=9RKWh@{ zH1=+{61mg(&e9hh(8KZxd>PH*cQ*cf`?=wV7GD$8CG=j>0l!PLSisHa9LfmNIm8^w zO-v;8m(p>y`MzLZQCiX8g(vJu3V*g_NXMb|N-14dLX5ihZ2%FY;pGU&yWm@)Q?rzA zm<(B7f=An$y6QXzi0mq02A!Hc9Kn2`S$*5LcKAy;jANC{lRIx|3cQH!`tc{fZ%qy6wBmAi0K__-kw=~CxMG>v`k%CYCSzvjcUmM%*H^sW5x z+n{zN^HF-Q{zHm{085X)u4Q)(*q7f*k(El&qTlZ&t8Q6DfMQvQGj68&C>hv^8HCvn z=V`F;jDy!-J8qQSGyR^Yo-Grt&iMPDeKodqjYEv=0(t|$Qx<=pO4;#0skj>D||+TS8q@VSCXv6lo?3Ptq72$HCgLl0mLWK(!A=XHPM>Mf$ z3Im52+N|wy<>_`+*AUA-TUM7Bu}2iuNuTX-unXVR>@91jE6NpDLidQ`Zu4?Jp|6&O zf&=NCE{)l_8S|I?<3~~otNQU6@2=ebb}a=`()Qr|{pWMf=5^C-UjQ=E;OOJ(q7tY< zf>g&Te@_sY%Kuwhxx;-}joMm52QKe-%q;Gk5`=>I`>(lqyS?e$CA$h+Ih z57+XgS^nn}rkEMD7Atv=Oj?i|5Xg4O&=a|z%P(^00Vu{v;MaXz3jX~UT>T$B&Qb}0 zzpO^g(!*2~X!0OUDx)7xnk?|MCVB*$$>ZmJVm2~j!Ozq)ZxmZPhc|QySDwvz#KqPaO8X8`w7q5eeQ7vkUay0 z#xvv%V$8_ERg%Z4RarX0s$AGjUzmf~YfixqTdkylr9xoBM}gb4@q84h*1$7cn(UW} zMJNn~NVSq+Rm6r|=t9A4ZlZ*@U)8<_HNLsDs#CQW4z3KUOc^Cd$fJN(cefVp>yf_@ zjzH-Mw&V1_WBJSn?kPJ(n?npSho##Kgn9PD9u5rY<#_})4^?E7XsPFj?P=5l;Ky*( zlXZK#69shc@_Lvw{`f6Np8T*gxlzMuk=ptT_Te}gM)$U%->a2ubm;1j59>MlWxCVUd#-#*YW&0mj>3c#p zRtUF4&^$$Cc)UuW9QSj7h(lCNtYQ~(N_=aIkoTQmyU5C0VEetmhKoxzB3IKoGlKP4+n zi=vVMDWy78274kgmEOxC zYzO?Ms^<5+zH%lH;ZBmNVh(u&%1EDyBj=*EYFs_+oXE=k&Cs|226J0kgCS&i#Ie| z6`?(aK};!G+X5+EW=kNu3N~Ylq@>FE_B0r2ZXvNsCbXuluUBhe*B=}LYp>icHG+-H z&dJi|PfIt2Lhek~#%e$NgDzotTfp?5_q7wJU@N4KetpY*k<_d-q9!TOWJ_Y^V7hO9 z9c!=oU?=3+Y<>lp@{Q@%^2f_1N1<2pxi+?xK5gSmUB0WKnLF%y^biZl!%ph>du!9` z7zYE0y3WUqjHz=SJLxT3>A{sYZ(~7@;{c6Huz)tPRD=gUM^U%}Qb<1#{nU(el zYcYD?xbNx)qn@5@=Nf%1=&n2M*Q6Cot9K3wCz<1yFRJgV8|J=fH$9asXMNFlGjqE> zbv1Cj%$Q$mE-!iH*l-mJfI6d>hA0DQ%XZ7j33!C~i^y@LMZaENG1NfN{BVx>_2oWO zG(;_Z`Yfww%~>xqSj$#7QU@s-?K?Luy>Hkr3Sqk-jWL~O!UXqk;y`AXZr6L)3GXuv z@gH_zq_=$<%nt_7y`n`ExGOksUA(s6kvBECv??8=tm+(;3wxL%uO!<&C;h4J=(9ti z0+}6}zbY&|Lgk3?aIs+#;t7Z6sbLw~F6$hvQ=~;%5l(OX%#B9uBD=3f7ssestX%P-vX&~ukkcJ9TRQ>}RO>aQO5(ZJY;6k>C{lV}g2Uc+urZ>PMEI6C{oVX(`Le&F z5%#h$T+vH<+zD;f!s7QNZht5dWa1~0=>ubzw(4AN*e}aMMQu70QsbLvK87iC+JR-) z)7(j0ySySRPEvIQ9Q_4dLQmmSULjJ{(Kfbe`PySJEd@#jNJOO-7`LB_R=dJf4P04g zTEdsu+Xz{Iv-tw2wrj%8hS}t!tO1f{5W%`G&MfxuR;J*tE`{_WM`v{0%3V|27p%<) zHvVTADV!Db5U5A{B#CfzO6n^JaTYPeq&l%El^c@xHe8D?nh!z}%(jdX9ZHN)7)eu` ziRE}=knC=YH(pj>GG`-rT5*KZ+xQ!+vP;@>Q#vH_$+~9#IfhCfjP;e$OvV8fo?Zqv zKh6;40d7){>z;#ci#=Zr$(KGI;F)Xqjj>sv)_X>cZA@_`Ds_b$#k{l5iPv*y7hzBB z31g33+XP7W5=}t`nlUIlit7ySPim7&5;3tc!w509H)7PUc2wGXiF|rSqy-?MlP+8u z96D}>hw@_3=D6cX$A?r$M`2ejl~sw#NhqaR>;_09v?3W;h*@5|?22LZcCcb}09$5<<_G=rsdQs=F+ zU7c(%8kg*0eFum62_?|>7ix5(rkW@l_E->G4)Jxy4uutr+>~D=-Uvrz?s{d>x^kd)HT^h&w{`mCQ)$w+#^gV zAe)Ss;>AdDA$S4?*YW^<`-eu55u?KsqQB2mK}Il$N(4N;U|AYns03-(b_}i?0U!Rb zrk#dgz70e-J8WlCK|6z;oItdAZi0;;^)(N{LrRuI&I!3>lvpQvR%glZVP*&%36Zhs zbfA1~N^0^1U9@vjr>TdO1SkfIl{7)MR%$jng&oQbfvYdjm$d&h_!5Fvy0r{C1G**T z^#Wgu1jgUlp&_PylEP1I|)=s*^& zY|PK%DrX%^eP5eQyzuCU@x3Oh9*-H;uD72V73zIfn-&)j53!AC*M!72uURWFa)p7C zgS%myMMZCH()V?e2>C+QD?*bS7N*3l5!nohoBJbvC9nBjg6O6 z0eYW-{jGKj@uFYiZ$fZ*<)$0QBBH zJd}-3(<{lZ0t>%tVH`*_qUI1;IgnWIj6!w%wR45NEjDM$YF8={kDi)}XobEk#Plxh zs=v_uD;+gwk5T}$jj!O7&8u|<;9oGCjyxhDj+>U%qJzaN6-ZL%hl#^h5 ziMcvGzT2K}>0b<=>zv>vZg*M@O@}RxOanmNSVmPVI5+F_lHD>oI+S>yzK;UJ#`H9| z8cfu!oQGI1UAz*lQkBDy%?O|_kmkY?5@aPbRh#tdbeP0)J#O*`IJfEC)E5h~7R_Hq z>hOP+9;!7AkFvDQ{T^PdY!}S-3a;wCx=_K{)|>Hz?0=i%)hEiOe? zy3{a8)s5!nS$$NG|EA<6a=_{S6NFI^K+lPp7DqFVi?(b||4AU8z%Qtne` zi6~3Qp;9sFl_K1B1d6!Xm1kHt@-BqN%~x(F{-RgrpO~XZxpNfAcSyCqG-^)mwMYP}Yu*q$2k6ztLA9i6LzTYf zM3Vc7g|nO3`q~#iYv!f}(!Xs-4MN@iVIc;P3%cvV1#{AD_M?m=djI!(|T zmowPL&dcKI&#j&b93{O~<&dPl4e~q`-#nABn<;K7sDu!V%_G>7zqg+Y5@G5B#@up3 z_gVUIeB5X-!y=sd3Rcs47N*+xdQlJ>5lXU+CMU?|r7~(&2+NRo3MLV0B8ITH2)+iJ zQ@`r3X_h8<@}RpL4rZrIoj5#B!{5vw-r&wXBp-kn2-Vj#G{g`EYYkJAT@s-+P}v@B z`R~ltA}0=DM1%eO2p(ELoIv}9`;{YtSaW+}lJ3%3bM!kbk#$XF-xQr)4;UeLlEu+ys7yaVPO=(tW z_ZvLhZzC0IfmE;4PHU5bN4c;N<+T#g3WQ3+aCNl=5zn~G|HC_v-hbrc1+Gf|AKn>H z5&fUqoKQ}HwXGsLqvzjBU3(5vttNhZ=kL(g-sikPBXwHCGYEfeQdqh*nlVEWN}48Z ztX!(p=h008j#?5%h!(TCwN|KR)WE8zc8<%A?U7M=clL)sOO6A@Dl;CHRg!$p`avvw z8t=2+{tvvPJS5)`hx&hj2lq{RKfEM>jD)i$l!jILaX0~t2qyVvgLo)ZBzn{?%Q~;E z%F0i|^aeemGANb7@Z)UUMj=YMDk8H$f;5kFkQ{zSDPM~psex+k*CY*$3X_CncQf5r zD-xSwP2Zx0`E>mz6y5fugVdGVdnxymM!|vKr1Wpox5DWEiw@r!DIndON0=yFa8*YWk}$b|mooq!XYEcUQ%yf5m|~QlKePN1=ak*G9hE^y{bQ%R_J)`MC1|mYP0@@n zUc|XdGwji6gAoq1^3@M(U3Ex{eUfHIPqa32XeEVvxVh~{06ok_oiDZ>{|-$G1iN{L zFtT0k;IGm0d>36RwHs!~OBg|nh_x?6TD)}v*n3C)CaJP-n3{dpCzdftb;aorTtQzK z%%*~H^P255A6$q2#RaEz9mThtFu?HX*r+Z{%fqX})wCb>PL`t2CFrbR-jK10GIMx< z8n+v!!w&$x>|HKSvETFWsj1N*sgogERU$|iE{o@J0S-n3)-Zo7c>@7Dnv=G zPrMBtE+Jgu1!Y17eSP!lzbTn;@i3yAtHS}Uf(QWhkQY}7+jG-1ar!GM zxv?`7RbGBegqH3Y{8D5jg6h{``DSRjg9W}r`n(23h+daZ3dD_;1hv* zK}Yvffe-jcW$7rE9bjgPK$J&5WA zl`0fFG(cXOZUdxMRi;!(xoRRRM_+GFMM@g)%AFd=k8EaHf(vKzj`sy%3n+FPRrF6{t0AMI%XeHdHkE)HS+si*s z9)Cjr$HWvb7sSdo>8Z4no?2*SZYt`pC|7!j+CDvS7_LPAW`rpG`@2l8)k484TYE(# zTPz&e;N68a-B7kSF@-B)Rf0=T=;X=Ohic#WKHR5y<=cF4UR}VEx+1vBK6^~G z4Izkmsw!-YWDw%CR1z&B5L)>j;U8;U?YUPciRdJ1Ob+eA_eW)Rj*eKQMEig?Yey{h z$+&vX0I;zwL73Pyd656%ew#y6Z&+`uDLtx}&aSR90Wue%2nptkrX54R;`+u=vS3J4 zSe!kfqV&~Hy!dhdcsfq9it%nj8yz=tuYRSQ`pM5LuHp;}k;2(u-0yWnjo%-Co5agO z)r}PeDT80kd~g<9YY zv5b92H`Qw*6PD1kPXZn^ak3s(;szE;fL}RjI?>*BoiX1wuG{pyf;(Fj;;dBzV-Ob> zGzA})p&ouuUr{80pC)Uj7x-ruWD<=*_BZG5PH%eSYM;P}c%spb3cv@^c*xvJ=Avkb zY9k5*kjKuHRFo{krNrPCTZJG*p(02v0KOjM^H+?JYBy@1XsL*JjpC4ei~bKGJ3jt7 zAWWUyN-QiWsGt3 zN$O+GEcQS4=5%5+piv_Ff<+We0QxU`O^Nqsv+`)4?}yLb*&N7IB~VNa$(8U8(HTz) zjG}e}8Jw%s(Ac;)UvD{Gq&R+0$%mKRT(VNVaGBhKLI8N)U&X3m_7w*@O-8wiPr|l?&B8)y0aLukU60HI3Z zebZXexTm%pKi~p+Edd{#Pflva5{$7`4*5P|APW-6&gQ-^aH=FJ*v-^B!mb1F5z&M# z-VO%`jU*=`L#x%r3upQGnmnp%+m9q(L(qIRgkHWtjTgTuV;L^T!+o;mC)QmnXYdKa zq&_KdCQF`f7b|U2en`pYtTPG^+T#|)CS@BP9b5CioU4PN_zDi+g@+rfSr-+3TqH#_ z4Wzr>S!7n5EDF@yPypy=*7wG6JC_mi`Z8=y9AT+3Vmg0|W>!nN3eDYo#4{jjnZ(MQ z+R4oNpx55=fId>(aQ3QLRIMJ*W`L-Ds#x# zlhsNx5qF~W$FMc{q*ACOn}WD~!SY7(XK1Eg-`+xER`SN`j2s*Z@VQn*Iz6;jMVlaI z4kvpB%_Ss7X_FLsT6Yr>lf`cfUQEjCQyo-*g^UZ?b)4&cZRvp&%7=1oU3qJ_+_&My z(g+1x9OEdpSyf22P%6cywz4Jh1DQS5XHoeDkK&69rqK%C3fhJfW^$9Wvez_*bQb?< z3-mQPm3#iV4u{DDQ+b^AM;C|BGcC=6l#jUGEZi7qxG7$42+7=~v)PJWCtcsR#lP%3 zBgrYV6NM6a$FB1iWlgOzRI<(4hSQuBi4K{ty!I=A=$)@tAeSERkj98h))eO2HdhYR zj7e}*6uIiT5pvOP5Q%S>;bB%VrDFzf^`d&{Wbw0>7R07VQo6rA_|n7hoy8hsI0d7u z-gDEO-*34$Ij^!ed2fNs)r7_Vdo-4%bYMb%a*dtX9cRkuQ+?A@FbQZT3L!4p%#$hzt>oT~D0+;=aPPH!~C zcAs7#hxVpAi6zBx4$iCv+7s4K`;goC5r0F4?5||7h7e8DbTglJqjCPSk!Yqhy(A)i zdf(qt(~LzEp)bc!x8YRr=1Z-DYs+sQ%~Xdm2HQ@Agz-Z${Yp^Jo7b-;GNNOcUZblpau~EnTu17r*Gox*Tk71M&V|+)Lf!@% z^1#4jWrUJP(09NWjGiTYfu1`)3jA!)PYa1c1Ep0e#KZ!K>;6Lv0atvVJ@Zjo z5Qgu+6#B7ruVd%;JrzL0WcELd?bu?g_W}DqE|TnjyGW!tdN0#AurKQ=2{tDw@9JP&| z-8-+-EhtnX*S9(JirEoy+64s#Gt$#l;%iTtPL#z!@zHQHc-F+_hD)16UQS9f-HzP!j9GUt=Eh7*qVz6X=&A!10J z+~n5MQHmqe?+N2FV5N9WbsOo2TOTH3G36%@Vmn(!Um@U^Nq2akeBYuOplYTlRp zyh?`q#!&50~v>)5JQWGK zTkrMQ@Sx-{Hl=7cEj3)%MAdL{MXRi?3`AWAQhbn=^w)Crqw`%XpfUU1#7L}5bpT&o zjQz^OO!%B?@g)g@a)gX_?O;!l(G7n(#)uD6scXk5y-crqYZSr8${NAqDUV088+0l0 zYwAm)adiW@qGT7FeQ8Grw2VSm-m>t+$>s(BLLECGuJNXd{3zQ%7l08g&e=06bI2&l zD+t6F)LX{WX1*|VotXAQMSKHXWt~aZS=5`VO*++9*)EwLbcZTbT?IShJNqV z!00VldP+9^h1~MUpKDBiW0#gFSu_Bq~TKLe3 zhr5NlVqKW+pJqaw9S5B7KJvS!nvxRZXo^w7xMzHosex=c0Mg`9RdVrP!ES6W@c(Q z4k%O7en~&;+ej%V^`~<(ERj!8K|s{iI@J~EqCGw+^oLcNcw{Bm7)t64a}Pk`CnY5f z_7#k2DcY)jll7UJtDJFb!bP)$C^kO6y3tm9l`y!ndup$5Q$ovs*H%Xj1%!=#ch;GC zHzhK*u@U&z`A>x>vAA~PBz%gl6h4Jbr0g)wylJN4^oeboD1CMB{ZzOjm;ex3Evjma zZA%y|;*97vBV(TYuKO&h^tUDQZJ{fH@Kim?k@T zf=^k`_Jt<6)$QPYuM35z(lDzBEdLx>zXZx%lVqQC9k$0Lku0D8u${L{IT_VE9qmoEM=uB z6G+gV$hqPFbi+hkc6!}5ms%IkHk`JTv)c1 z216r*{y+>a+{ghJ7k6^=B?CYad1fe@j3du4$`-{rmd$>@=yQE6_tL8ZTfIQXhYvUp z<##k%zIjTkb2x4u?-4=jsLG=PjOy9hM!}%ADC)*?rzn5gOp=;ABXwp_wo5wP^R&%b zqFfIuO29qs4I12{;|RUP2bHRdPd&k|Da19eXoNLkVGp5C39r?(rCj&4rF?>W`O zbUsQN6&9ncXw%+M&e4My|K#2{1va%HfvR$#qQOfN3Ki=EYV^K=E@r`;#hUZ+u}wl6 zeh6br9EON;0psuYLzcGPv#|niZk(WM5=sf{T(VU)-nqSmPf?@lJTUnbeFkO-LSF~Dh5fS@r4vOKTlV$4h{GJ-^Y!w*7=mtpbP#L6WWF@xt5=8bKOlg`;eUGh5BanZr&q4mSZl?98s`jHzNQa< zRY9~xTz-(W=eh%CnS2AA*lguoC9(bN)7$6_vjg2j=P>RR#na0PhBxcIqc0n@6%L8z zGyD(oT$oc(OeroSSThj3rGswdoSVE^;cHF_T2zi0=cOEsMAVzhf`j9Gad|a0!OM*u zKE+ZzlQnlqnj|2v*94+XK>tGfwoEU9X=Z1vQe}%J`A|aysrYvMODaYzoIF~20Aei2 z%-pZ>)xN;ZTK<( ztUH*Wy+{NtgM1g)P~4w@C0q0A>M6|oF>RD|ZOcBDy7H`5mKn<@ZRed?(rIO}S;Pxc z7)Q@vg~nT5ap4S@(ctLjN&rPBsekd)YQniA&Pwh55@A}%O)ClCbybb!pTus_cM2gz z4Q)mi?b>JP3^b{xaJ){=_^bwhd$W3ezw^v=6k8ZNK!mrl@6?;TIthD+HPE@|bZFyX z*f`&VMkmow#{#^=R4FqkgMLpoI3mzOe?ylLFTM#^2alqFBB;1QNfM$<2y`1Xv3gXAw7WRienUw<2y4oMYX2WTq*IgjyKiN%cq_OPe?8rs+l!N_z z4*8`Mr@SlMjfJJX+boa>r)JU3&hgxfa%m5i{Kw)cEBi}b3fccnGH7T8Fjsx)5MGhh zXobS_)kca*bHNt<4GF0_azN+|JW-sPaRm2%YvGzn`fNb82;4d!9+gFp!UFZ?;1@;KNCS7S-6R`yt=vPnq~wSTTq zg^Mw9;cG2Bc^-d%i}lJp3tq#(S`kZ~&mHODf%XlbiVMXctONG8ll)mML744QvgZ81ExP=I%7zuJnQL+nKhZ`W#RtJGAL&^H&=sMi&2^{x{OFBuV1 zme&hQySbQ+xDAeu-RNAnGbI3FG#P!AT@N`>rZU(i>JUT!Leyi#Yx4EQ{RJ)_sJLElSAGeW*sC)Bir{Gxr6nGy(pfljl-~${&P=Z@KDTQ2#?V zhe>hgHldaDG^04XM~r)nQwu<-#jubhi;4zZ4Myrw+tA>0>^*>n`R3bWsH4k4;pn?* zJ{jGxz$4a!PC9>0*TgyjuVf1Z8gG)H#_RRHz!<3r7?xNwg3JAS0RW-Y$)=&>K}iWzF9F0P5Y~xq%-+L9k6?&12S)CMuX<7P zTPWrn;*xe!GggGfVkP%VUdDW?M3OO6`bdI$3*@EOI9QCVD!g{~^h7eX-1$yD+A%aV zWCh-i*8mG6`uakgA_ORsmQ3ZZC6e=siWz(o5mlBozGbyB<60y6!(OU@6j~M36iN}?4qs#?#2kVv9gNb)joQCYQl`Q z^Q%9aOjV>sF((6}_t~!jeJWmaI_(?t4w~P0qj0jBuy#b;ESf3teUzboA`337fDVM9 z!%gZro$otz>1w_|oETLBkdtb&AirzE0pJE_-nhRmpZ zHrj*-tCqV|x^g{Em*-c*#|7P_OtCTqt(1s99vtA!%fI+x4?bf^@d3fPNo8zoda1R{ zi~j)?QE;LDlRPq`Xlmry45(`+W9?7~f;n_)EA7n*+JKavs~OOS=V++O%_t2B&_IGD zO?|9PqqGr zYtH*5rUvaJ4ht@;r%3If7Z8iv&g$+P*sw3Kt-C+_E6TW3RF+xPro4cL4_h8R>OHba zIeFI05m9tGImE;NZ@oG~sZ3yax3|CVhbRzSQJ6h`%{NCW&*HdzW@S}|-8u5@Bn$>Y zBuR=Hyuy9K0OAA$dU#*#?`>^;V`#Cm-AE9zE^_??uif0-%$NS8q@Z9lX!##tp;+%% zOcMi_)35&;-mN@uQ{AD(|0YQw7ljx7us&+VWK0*MHi@TnS{c)nV&_Ces6^EGpVGiz z=IHH)IcV2(+6*hg`kQjTAIu&6aU*)k<0sLItGeCaYW9jDTu0>vpuUbU+ zpFbh;J8JNbuFl)nz#PZRR_RB?|H+T>KNBQj#FGn6ZNw7mj|`X% z54(Y+*#9Aqbl9OueZ$Aci2J&yvC=p1f`4g;qxvnA5v{7)ywiu~mlaTz%a1HN(>4W! zd^5=_-+t?A`q4v1xf;KPqNEvR%Pq^vyuHqlR*+6B+?qLHc+Hlt^KY#~9urhkX<=z; z%(v!!YJ`8U2_sFviLF{V9{Aae5s4rGyo&>-i{~`3R_kW-hTGiWztR1esefiQ1}qj$ z5s`7M^ISy#q&{@;`3N9G^x&_lqbZONqvZwSncD`s!v+gUg3%yf*O4JRK8XB0?QUKL zlX<7~r!t5TD~A2QPXj=hUzBB$aU0#H48*Q%uwtk`{kD^-hGbcBIy@~;F;~}kQ)c3; zfo_oMD*`&HA{TGQfq=U3DSRvxj zFR-9d^V0T{fC30HXS9S^P@55i_$cjO^L#jHaeUu8Q#TDy$0p)u6kp745c9@cKTp#a ziX^v<09u#mfCY3xs*X_#a<@a)THj*korw>qOrLy9z_XEk-T;}hlgCNrK^Ovouf&+F zhE~we=Lp%>m;>L+R0_1m-k6w`wO`{8#Bk=#BFLnfR`JCMX4H!>k!*n^6#fY+wQwh@ zggH;(AfzHIpOdg(e%!h3kh=$(9N#K$#B2}M8e5>yZo5gZKOmw6@+dVpVIqxE zZn@RdSiUTkK5-O>5mbv$qh0>2A@>#)#te);xJ_4*4#xQm*c;D4$Td@6(GXOM56SAz z6HOg1TTNkoPw(pSxUzyUQArUlDEU3kDG_?vXj3?>;0cmwHd5}zw~D#w1i4r5M{PDr zRepldMyl@_=|bOU zc2`sUISg9(oRXIN*P0=^y`E|0x)btDZBk$%Tw#9!qDx9aIng{9g#n>>nuMRyLJR)? z^Xf+IV2q;uzYcUC<-e0~yWu~$alg1zN{DQ2ZN1O`%r{t1^B~sm7t8@~Tig#;;^N}; z5~5*R>{p2vx|dMV|8}2%y*{>4 zts3u^Z1}@eZ-pN=n{1J)D|r3;w|Fl8AGTXTj%r05%#xw8{Q0CruGfv1nxE?G%nw&O z_AA4lW4kHkmOX=X$3^cGqNpKi(7a$ty%35LwK=YIPN(ocLwOQ;bzs>Kx1{y%F;%s* z>TxcKrc+|p?qzC>A`z9n_6?)P$i~=;K&q#GUS1c=?kUO14z|2?J-)LD6aLH4s__3w zHYRJD&Lda;S8s0}R@K*b3)3jwEhXI{DW#NjZ(`FWu>t7@0YO>;K|o1q*fg8&Qb0gr zvq=#Ir4*Eq^qc7KdCqg*@BQlf&UO69>w>xFnrpGv8uvZ!agVwFhZN6us;YN0MwnAo z-{2Ld=vu%-9@3Q$5H=P2PdT2j4^Tb-{lw8cPUPN|IPW18}QBh2sPgV|Y1x71h zEBHNRl3kxWS&Nx_NwYd<+VYsxqeM*ruSm zvGA3<()FHSX``+Jl$4Cd;va;6TpNvef)j(;*k?m~V=Yw1#Nay1{BgCNjXY3}fPZz* zo+V;87@JF2D}Y@HY&NrE#t7On*O~K(s1&4mQXi%?hG!i@eRe&aFnslvp$5^iJ zjWtOxl&RQ+{-&JxWQ@)~@s%q6R)`0U`#8QcsWbia1W|$R9{$F$h?g$Jht1V5LlqC7 z(b?mw{}gO96q2C8m^@M>H@49JkRx&{de1RgbL&uzjt406e9=>G@P3F(L*`{4Jn?y- zn-kEPdBLI0b48%Zdh3zboZAWW!J%_udU z@ObCdTYj-GHUN*rH~DdVKy}}S`CYB5z!Gk41}9Nv$`6zKS;;t|sWI_GkkzqK6|X6a z#BY^}@8q&;aR<-e13X$$u-3?WAW&aqV3a&e-)WKZI);DHHJX31$y zsQhU2YxUEnYD&I+h1o~GFjyV;_P$qM(A3*o4G*L56CLQ!vgWhjU(+4pGe6}KEtZG_ z%r}n|h@^!m|D=uT&R^}sa+l$1iFx;TcZ3`sf8{eMuvx+uJ3OgK#`YvArhb3whi^Zw!f5qI;6Y4E`I`Qb_+NP$MmTn67;PzXP@G zDVMu(m9QiNZ9rvGJ}?3qlF{JTJfM1#0lK4&Q*@y~z+~zhf%MNv_^cFI%uE1^G%yTJ zGxtvmH(+>;60ff1OAR2S%Vk1KyB-5tso{o{Z^FT$KdL}J_yxD;GZ^!~fBF_p##bwT z{{G!pXFE&Sng1eMmtM>}Cxz3_Lp8g*aUD?|Ce2Tm=uYGj2CIkvm`pKw%7h=LHEo^X7G8rwQCEn z$w$glY46AvF+`9vo{`zshP#7&@V@rk>J;?w=}8{ev5?7c{9P{^p5W=N!6OF+L$L8k zp%JoIx}l-9SF_fg@&O_?uHT%wPQ+ddb-Bnd8Kz6OVujL873AD#S=Kb$%=ZWm+|GO} zQ^hLm=brCey{!j95UjN01}mM`NgMnN2E|7x z5JCMm{WJJjeVG}FFGk}jbV?Qi(9ZS!`Y(#`Edd`YAr$$Eh?3Au{A zeM5VM9EdjmiXR;Rg2i4Nd_rCTi;;oW=Swv4O&Q)k1S#OY*_TGUaD{hUX^i}um&q{8?97gK+6=40zDFP|BYC@!#|&Y z281>A{rGsFdv;b9denYRJz{df!)G=_f^k^vL1Xn!P)oRK>+k(q9HJ$Wg)fu6OdS67Q2eyERE(+yRG!5djqCg%=N?C+jkJrfdAmmy;db69e2SR z_g3zmskSZ6(alqe%$5&b;uvDI&oYhBp3oKrA0*b|+1-lj%|C=Rc+d=D z1$uU_zkerjAc&%qZocSjyjjGuKDkUQhCwRBbKyy%pdR$0(TZIXL78k#{0-S4)O z#d39{BnvkLi8EzIeqYs*oV^-H;SqrBZ&(ZE^zQzktp-!-GatPJI_oz;>oylihGxW3 zfR*G$eW9P3;CrX7-;0?5nPva_lhx-(YqduldW6NwcD!o^^+zNh+3M4&i!m(wGQ?)w z-h0(@$mSnxlQSQVzdQ=k(|<=J(p)_o9b}bt$B+Wih=O$r@iI#FG#;+@Vz!iddWsn< z_PnLTPVk&bxVcdAA=JoT_9=u7;bm%}SLx3XXz;199k9iEh@Y&yFNSNQIofJ#Nw$)e zI~zowX4tRKtZXPRbWkGWRht5348@M~Bwr9h5wE}c8_E3e%?M6hzc zFW9(8*k(H5BZXVo*=ezn^*^*&<`-BGhctLx$<|A07B38+>OIWDyR}pOboQg(whj5y zsh{aISUilE4rO*3Yf{pVvs$f(H{f0%5A3tmc&_nMfYLG4p}Gu>LNfT6PonxAL23dA z)xUXNYFa%fK)YFX1dtx3En-m-J`#bD%-p=l?j;f|BMi)<<#{X}mz|~ISfJ-nl9pMA zd>0GgHy`625Gi-euoY?Gy+sa zDN&d_@F-U4e#1;?@* z#GU|5=Mn--zzAj@Bl>Tb|6Fpux1@P_dAX+hO4=X21OvMmZJW)7L&D)0N7XA!tHcJNp8~#_ayoNhG1zQ=UBanBf={z=zlOU$* zeooml!4s4q<@&hsZ~fDT50fdH|FhX>M|ulJZL6-vbj=!f44*KJ&>wrJy=2Mhj!*5B zv0n9f&a(S``w3zmrMPB1s_tvyTmAjV|8sIF6qg(0Wz~PvL~@PhPjVR8qCOH{US2+_v!fUmUMnBF^UL`k*;N6nfS};|$4L{7;)#!UUZ_`NArRzH*jVu! zaPRS$mriqDzkYo+pHSc?Z`I<>_xU4Ln;a%J>75Zix+gYG#k3{Jp7Kwd`k_Nfy@*C47-#E$>T z3PlQ*AAU|j{cyJv$azf;<;Hjy?T>;qG&Iz{a&&a;VZNxW;?LjwtA8?&Qrc;&etPcz z;+&Wm7bB<&$2^9p`e=_vNc!2OZx`Jm116{rUJHI-ky83}U@|7_XnnGc@{kCLe|%Fh zOF`2R$F|akuG?Qj9oD+4yKSjJ!jMl%G&)2|j^C5LoiUpB^2B(qF30mL3Q3?VZezu1-nP8XisMDNtB(K14j~fB zl~r=td;EmLBGJi3tQ2iwk8`%Y;*$%uxL^{2{Fe8!sWa>%b=B|{Vd>iIC+=!-@FGH~ zG)AwX1Xy`pAeNwMs=O$A=NBe_XqD6 z)Z3vBQ-3ZXuvz$}_V}L*=x0>Oh>pgNX7uHHPCKpjhEox>bX1FMTB8oBsQZ0Q&uQuw z9UuF3obl!0^rQx9qt8rKS635AjQ;wNj>;bRb$?(yPyd!mjsuTRRXZajT($hE76Z@0 z&!Imo+z)dj#~2OOzcd`3{|wUjuBu~MJN^#073wh|&fMfAiCgs)dW5lhyma8fn(~Z} zJNZ{49T^Mn5U+}b63|j23m`i-RCOe>#F~4K9l?OZrnEI3av2hJ%-Jl?qBBu z_CA5$3@``SY2#h5X=+L?Wo(&e{9;MdAsch0R7e3yJ7rr3nu)RAXZ9eu)1!z!8_&%$}RjH3mAvP^8kE3g3-#}X9S5cRZ7_x95< z!iM^$kcIcGF2)njbITOsxUb8&X)?2I3~u8QCO&|&T@CO z6hu0&lxNr>^rq(EED@~zR2k&)>|#Rpo7emSUocg2ThqqZd%MaFb#^o2ukN^xmO8t& zRumj6VcSx03Bcm6@|Qa95$K%`9uwHU_kL!Z)rB`aj~VF-MRHYrc0Ikb!vj9c2NYZ! zz>&(oiNOqH==63ufuK-_2o-tS8-+Wfv-`vG74C5Q6lhU!0m0=t3c zROFcnanS1^7=$Ndefy=(^nyCPVoHKih;m;X@r&Uap6&s7A`AJ-CqlW801R|<9+S5Y zwz@9BP%I|t1WoJ?IM6ijeHmUdh#&*?P@dkW~`^F8yCVEn{%lA|2h*Yers5peoV_I%jodIL1D{ckhzgV$?=vxUP4Nlx zOCMdEXyMaXQSpjz{67+&zI#^2SKh%+B#AOKsw?J{w&f)r&r-`x7?9(G<;aDb<~$T2 zz*ZD>&0Y9enm2zsREICr5(}TL7~7AAE9BO)%XL?DS#S{_h&lV)tCf^Y`?K%Q8~dl= zPr`;(;kQ*&6A8an`VAocls~~jKKI4I8>pYUCwEVuP5=D8E_#=#CTMRwr+t@=qw;pg z_dx6)k^DAAKqFFK6Y!~L@IkOv;B0U2?AF#V${K~$U*dVE!rM;{Dv6tN>*Q=WMEvZ+ z`7(i0wG)+*gwYjL6>Fd4L5+G@tP^jFYLoHW8~U~Qvc^0o=hg4NY5A+XJlBiUxPrP$ zV9zmY#2Rz_7~`}D`6q|E{JCaOl_J=%0<+qwbe1kZ`B5c66BJl6>gb1NLj{z`<*5P) z*F2l8r8JxzN&039rCLhz^y9=oDe33}-c!|mUsrUF&f$lal@sP3Zcoa&$SPo@Gfi^( z{85=%AKLPcwH@Nqm24`Tf5U#Cx`u!B;-SI2+BL+ ztqPaEnK6XeZG1^#KT7g-C2O#amp2y_Kef z3l=^W!sX;yG}Z83B&69qx1*!Gt;9D(_zn(_sUU+Vi%K~K9Y{mt+#ScnY^nU_hmt=$ zqG8O)z(J_qpMMkIn*mRw!EvHMRLQ9JP|&!a7~gqKJ%8hAzXUsA_R5(djwFO37)?TC zxKL}%k+EIPJ@$$|d*4hM$8ekwfuIl%_f8?S*SMZK>i6*S0k)u7X|F=j$P)DaJgSvrxJoV-la zZT$Pk1mgSY;O>$wGtKMlNc6jEuwowW%V6z@4r|`pkOI;e75hhK1bOF4-%NU1+5z!9 zk0@65zE^0!R$Fhyz+%gy>Gx6Tr9p}wnx4%lcf|mbs*j6Um~;dNI%;Vi8(c!9vK<#3 zvh}FmB{JOp`Mq5vTCISKUdB9=T&)3Zxt$uF_N{bZ2YT4nLq@|?^)Ka3Y&-frmkcu%)8x%%-2w0~v zIW*$ncGD2OBB>1)6j(t-H4vfV?N!DgcU#R|6I{s*D%K9oL$2Q&5>nZFCb)X!%%Ahr zt<05z9!AqabbJ=cM4lsqXfUaQTP-ima&9sMoG>%QV_ub=M$Y?+*B zr_ErL2nbnuNPwqRnZwjDisq$TL z$!&e@Xjp_Zi!!hc3GdN_O#9O(_+O|5WdT2 z49R?ZLxb5`*y=z{Gb)Qn99)c)l&HO%*~9~_Vu)`-@CCp3#zxVN8Up4WWrSv#z3hB%H zx%G@uwR~17<0W1T1OO{7)D{<>q(baz@kI@$`>Ep~s;Pv|_3@bRGE(2A2-Fd(UAblT zBe+21Ac=~%gt!421k!m__Qp#_nSgx9vZ3mHgt@J8R73OImp5Li6__Lk`74rWtVB<0SV9nhHPjBacF`WgTNnhYimdsoapnl zBFbY*)CSBrb)D<$NW_3fWo8dBBrq-aCV1|i=Yn+wwFy&z69s}HBd-p>k;^?QOXH;6 zkH`4N>$e~WY$=OJEfi+vC>afzzu<+49bx*4;SxrnMk3x;YDNB6n@c#NiCQbbHi6<# zFL=W@qF-!?!mQhJf=;2>W)^H$3#--!MuQo-s0VaypEzvOsN;yADy5aU4#Yl-i_?VM zf*Dg!AZ0&s3`(A|Abw0aWpUM|Cz8IqS#J>{Gb_Dc{zd6C-IDQMEPO8>A|NksQzQ<| zIH=W@mDSpsY4YH5?v_~QC0}V;387l9BO~G`^K?cZ9k&8hml9j~-KC$r^J{;RWjgJ{ znVS44?p@GkLkb_QB0EdGG;5Y7)r?d_itbjl4i9>+^^51c+iIK@IJA?dHu!m^Ufz=n z^%9mxHfN&0zU2q<39O))@Hs>ox_TDs^+J4&p4~{P0_Ed#^RQj~m3>0tl+^(nUf&ir z+({6P{Aid3MC=T9^6Ba6XLJiQC{E#Fo4a(}NQYxm5@F%^;&Q6^3xa@H2d6caoH~v? zKGyTlg4`s$cl#7Nx%O(%Pv*fdSZwRqmv@)E9+|!Oi0MN%wnw47h9Dl2Fk0xR{Tp(G z{5?e{zs6jxD+!qr+@C0OoH?cQN5$&Z5u;M?5j1F6(l6@-my+H4Oxm>&{}{5(`oO3^ zgXc-A?E74t^ms{sza7%B1)q;+F|~7v?#_J_%O+4FN@PWKz3?3|_y>9xcf3;bOsNk^ zQsSc6@%*Y-k`dfeuBqojYrcEJ0tnmI^yg+%avJZk3{ZV96PFnVGDHzro_xXTK4iCO z*}mr28d)xj@YP1;W`7luw!!a|>Xq}1&CUP$*qAL}-NNgkM$Os%LaL~Fk0zmq7Ebv- z>4or%Iy)%hjP{ftVnqI1rG>qW-F!^0NYDF7JBHHoh-0?kBCbr_gGyHCsCnad3A$1f z-s!Lnuimux3nr)`GVg8M=7exwie;|pcv*-`uS1oe#z+DBE3&W6tS)6Y4jEK<$#1gP zh|`PEZEbtf&3(`tBo{9vvGozcO*4!t8$S7*7W5RwqJhKcYsL%129^dHw-LLFKitSi z@gypCN_V+^;nxw494&}&^icPxLRs%wa~U-+nVT!AlC0b>c>bo&u0P|V0qCc*K)dL3 z`~nil&?KmL&-**Lcy`!Dd4P8C;^M`e76ndW$pu^cDT{cuI?7AM$|Gjh01?;LqKqRA z`ts_~S06kLb2Uet;XT{pTQiekHPPM}G-WjK0YgSZ^<-m+oTfK(ddNuGt)H|tKfgTY zpH|aB@f5Jvb~X(=8gKUN4$Vxbkk-9url`l3!22?jkhBJYiN$8HGgcJFn^Pt58+!RF zg-tc`?g-Rvl%&y!ciDRQUN*Wj*IsLuB)hRndb&F2ji4(5=EE7={6s)!FX5Ls(#09V zLok>+dJ~$IPZk+#Q%}F=dfP+F^_41X&d7uBQ)5M=6lr!E&A*zqE7srDlPEPHDjfgQ zJM7&JA35cwJF-T>-NO&q@bn=tDhGfH@21qA>kPw`JPpsByqH}th_Y`ZY|jjjGiv68 zge8H&(4NF?yrlzXTS7bKo4ff4z67cS1F_iiUuz|dvtke=GSd(Bw)i6}VvXS4$L<=! zcl1Lk<1cPx0u|UC6BUOE^P|m;_z~nM6Bza+9LTm+^^2)J-Y$_sX|_e~CWhs!6GTJ@ z(}iF_iEd`0ys`@MVahx(!z9G|yGHL_6|5_jV10jn=-XeO$w8e|c2-W55}POTk(Evl z%8!dBtesz=-gvF17|7kiXRzz&`HY^CgX>)=QyH`UWc)#m5V9hkHRcT^ZAAR-+o?C{ z2W^H~PMzEb;W!ys2fCDdjIM4LadK6qj^3PzR4BCP$&V)ml{ivz(XKY54@5^87NwsQ z1l%N*dhAfw-!|Dd@`>|Ht93F;1ZM#lfd&}g$i+zBP^;SzP?kW6WXHp?7xtgT&`b~Mp(4^@()ce;6#73#dUMixYr6O0L$3P$qV*&O%~F}6g{d66k7bvZUB+E>I_ zL(gXDJP4q~Vos>~6}D=5FO8(g@~#5M7uZ6ry+LU$M@5RndW2cDHO!P;NABFx+4M>G znt|^3gQ)MO9oCC#eV=IlS@c>h*knbylPgjv!7|kCjoIdnmvWXI{X-_2LFeAEmCf3w ze%(f?iz33K;Y^_qI5E~0a%YYk)43l|mJb{8>Z0x+kGa}e>zRBQ!ZmU{Vi+3E5Js@e zlv$^+l*K{~ZJJsN!vY#}A1=|--K>m|O*@-NWej9{RfnSU@h5=_qwuh-$4#g*!swGh zKNDKUZb@{y1Vc+LLz145D4DNuU_&1V-D14DR z$IJ5maXOzl)sJrIx2BS5;mw3Al{6fjvzh{)Y%RLt*||o;6uzoJ9}jc2hY3*XqNKln zV@BB0*PGDY5~G!MhWfW!(jBlJx5i`7s{Q;rg;}>zpY;LiNVz&BCIBsKkfPO_Z{i%0o9%|(8(#3|)~H;Gm`H zH(Ixi^RjhUMrv>tr;1O=Ww31tmicKF7cu^0sjBY#s)uX+!69Mu@jEq~_TVZOmX6l6 zsE_?AT1_+3_nLaCHua5eG28+D$FrGz?&I&`P2a&Zok)1`l;vkOB3*3IT&O-;CsM*FqU4JF7n{Vmdiac-5njpF3sv4)*7R#_vp6ul)jmE+O*PEdNVR!S6Lx zie^1&h|x%scrJ-7QeN~VE$Wz0NEGlpWqH3pSTDrOyYAq14qQBKxUkaOH6R z=F(wy!#Bj!NO!Ibn@3k)HZ_$Wn(EC7ge1Qoi`Ct`J7TqM?9{-&ac>NqEM2GR&O%pH z287zyBlc^0STF=O_?^=7k)ul+Zf0zcZu&4ZVQ>~=u+??}m|1S<#> zv~qe9ScoJm{K$!uIL5-#l34h&sZP-yoh#(JHY-=>%_me)J($D=i%X(qgRt&Y!<(j) zgC7i3FeLD^MQ%(V-GcADcVjEec=XDdmIH;|8XCRy(e&eYp@wH^b$Su{y7>xq(JNMf z^*;;R-eW`ybQ4vKV0Srjx|K>bwbNt!G8VQB-5%kM4OZn?5+dy5 zS$*S-rxzAF6&+^L%0h~DA9hVX0k)9rGC#F!x$g*N2-D+Hup$iQ^{h0?2;bqeW|1>v zar5O_NASOJGi@4(?_Y5474gnNgt;*-dYO$UMT93t+pJmzBA&Trp?uBd-b=*ImrW)% zP*8B`!npA@vV^E!*`C}mWUCL6w&W#4XTT#ze7M9C9`?+K843_!AV)Zs>rds7ZOizT#T zQP=4Y;B|Q~@yHg*6ZaNNnC>cGx4Wu>9#EsG|NT9n9y1ZIBs~5RAHn|-(Z*%Ron?*2 zD8V-L_wG#J&v9~fT?De^60KLcV3PnmHPFA@8GPdBG#e%7A_*w1Ezb<~|^L25! z{>`p`pD2;Cr_p6C>ipr(&5^Aa6Q;zg!bQAlExmD zN2t14G11G&zPJLS`H_XN2VW}z1qP)J#`&5L=(2G9m|i}3kPO7Ir9kQH?!ShSGqCEEu$_lDJ*FHF)LzZ{o0EVj zxQ^OmT=1YOb2>XX7|?4S%1;ezbxDSAyrKdgR3ISpfO|rHF*DCvz(rvG_eHsOU6It_aWOk>+b_Z?0AQ{ z-n=yU?0uL%{IAjTcCigJ!#C&WD6a=^?JGwq;`k@T%p5aO>h5mnB-MYP{IKLi&<5X;iOVe)Wx5UWO4hoK zR+Ui#auDiaSY-^Nb@cb7y_QQx=bP8U=k<_$WFtlQ#@dl>yZVP?wALL2WlkHV`H_MV z!Hr~=U%14KCgsEJkJ7+{`N#Kd6djuRWh<6HplM~JJVh{pdRu3Ql4Z$=4H?ZS{^6C9 zX#PCE*qUB=#f<0J*qBL3Bl`=N_<@Wsbo`>4ut7Q_wTyKxn)3e0g!jr!*AbRo=OBj% zJ-qp{+&1&Ip=j$s%If`jry$5Jb^5)Tm6htux1gh|1fwj^!oot$)vH8?Vl!K*jzrpJ zJ8){P&HPqUK7R%z zlP|mR6FrW${=nCU?OV6Ss6IN0Zl>X=Ww*`-{*NY#|J0jmguLo;9~1MqMQwnW*jw&d z$1Ek>%v$*-jYTET==%-w)51q{SI2attLk+2zPetNbiNqfjX5OXQmtmcWA&NPM!X#3o{pkJlU38{wV-e3 zc@spcKFIW!w@GuAV_rt3S~-U0@y0ZserQ8O!WX}Sns}2tFe$#*Dpa-2)RWHqi{jOT z3ovD!XuFjBOcMOJBk?bf<;1HHhWP_PINKDR5uzwSGhnBXW;y3;@t#xHpn7JLuG~vXo=lfp=Ic=&u@G?CQhu!ouye^** zs8~P1515){reYl7cNl1spWQqm?B_&IuLcdjO7mE=Qc$Ngt8EuSYXx3X4LbO&td%@X;t)+pSJzt0_j-o_rjOZ=>^-UA`EBV=MP~lADup7 z3eGZq^|0u!vty%3ZT}`#cKPys*6Wyg)=V*; zvHa_bm6Af}7O^G=EXZIES-2-i)9}db+n$5KBEkyl8ag8og_OB!WTL?Y@DDDkKp2E? zN;1mVrvcJ*W(U=I#KE0_)f5CJ32Cl0{ zw)Mzr>iN`L{+qX`wJUQBH_Y7ajAy@IJBTtj*JMh<+#32vRR6H56j+GK^U9MwB`cjy z*@2NvaujM}+sKHCt~rB5JvN`3!&1=pt@+>hZO+i`;dZ0GG&_t1hsNCau4`2iI|G$A z=Ws^AI}pCDEy+{$g#^j)cd2RCjEGQBzLiH1yct?DH%#ecNTpU1;Z(@ne0CU@QPcL^ zMMM8y*;nP6kq_}BX54P4Gm6Tl&=2^fZmvZ}Pm;m^q}!(P?{S6^sN*t)1w63I7t!p& zv1_&bNeEO?i}k5b2c%YkM69z%ye*FzN8=vUvFd^0F&W=$ZIVEk_%9COJ)Y6WhWX63 zl&(Q#D}}yaC)D!3eGY)%-=e8+9Z2ZKBfI;C$8W1?z0VnXk4zA?*}*R^f6W_5N5T)i zCa!tr7od*}a#PNSDhn^*x?62=;#q4c20Jj%Fp4D3Q7*w)0VMj5jgRQ;4^szX}_v=a9;5=irxN&Rq+peWuejQ-_2zN4t9rR0!?7ZdeZW zN}Jc|$ZVaf#EQ%Z4H)Hsw=G9~xxy7Fw5RiQ#k(;4iu7@@kE@KIE@f|PZhL0lcPF*3 z$CH^)^s|?Wi_5dQKNWTX3&u_nzsrRr=z24!_Hhm?LZAFamu#6rSFISR^XvJmhkVry zsWc5^g<)CcRZF+1_YWN?TKv4WP;*oH8Lnyqa zWUFXpl$@mZr1;?G!-hFfSdVxNI$({yj)?gzR`cI^5T*S(X_1~46(6S_Zl6q>c1qFZ zIY4Sj1nxc>ZVA0 zIq!2OK-aM^p@a)ftQiX?>4&NDLwS?^mHtl zt3?zEJ0z%wYhwtT4Ym23VgBq>x?iaB`7PXckKv*JA2E0O7iAjP{FSwUVVsZdMP1p% z?L4r{3)I0PxyS7bFro<2l~4z{Wbw6W^^dVo0NgdPTAHenq_5GiCN$A zlPgI>FROj|1$RAJ^VzCCU9HjDRBa)V*7yVpf&LF&qAc*B!@=4-j#@f8>pC|ddUpIA zzdL|*S-|WMx(}AaE3~vpEh}kp8=cO13+>K7&>(m>6_4aAGy?l8NpBeG9pPKiSybMH z-@f`8NG`pp;vX(6{J<>&nU^L|eT>7A7-A!ok87-(Fa*&&#zIh~K;NWsJzXp;dq^fc zYgSk*_ULSEb#-SoVOiAjs!$K9MVv&u`Y~z7Jlt4@?lBk3BAnt!Zg5Sw0b;^<0pNFtz<1cOLY6;yWr|1;eJ|UW)D>A zZ8^?H(;Pxs7Gb9QHu$ntK*&viNlO8mqd;bL$SO>L?vN+5+3$so1fR?_NZQjYbNu3#3$>2?KuX7MC*na_P9pXw)~;ZsdA% zi=&j8-k@9z7OgkaL)d-1ng*h~&uv(f82ZlciLHGbm#Z%swA*SDme0+(hX9yF2&$u8Mz_72 zK5Hh3dLc6^o$)r!Potr?Tv+9ZI=q=%>-Ah|AI1UkmlPC=O!Vttm@;;~tz@6o_<9pO z+VXDWiGK@V-22(Mt`&6B=~?r{r2~~h30But)$xfX#0aNL69Vx7sy$A&6UH~D4B1yg znyFIx-Zpb3W}vezo?9|m8w0@7+LzJgMc?v)Ln5|#8MyF`7S*YEZTXd&2_|8$Ae5Jp z^)75%ErKSFjNEnw7=zUyi(`Zijkw)=O^>1*3!30U?hKegGddmlw^hQYvjr;O8tpbE zY`noRz1EJQLx&)yg(nX?cpLerkklF~U=1EIie?VQtfEu;ph$l`B1YU+W(C!_Gd8-( zE19PCTp((Mf3TK>o|o3>RH^7lyg^LZ)n^qBRCS@ zp=KU=(-S7QXEyqn9o!D1HOj|>z+MgHeLq%2?UKtCuK#Z70oVXE9-jb^?l>9CwBylT zyN({lRzdxo|GTW{W+onJ;u?U&8KZ%?Hvp8$M}sF4rNCyJ|DUD>oA7QWSn+vM zf~mni6-8*zjgHgf@rXAvO@X5ri0skof%LEjlTYqnJB|LoFbuo47a!UMajOEXjtD%{ zVB62@-7ke4UjhQlHo^CL!>#{+US=bMnC;$Qv~_eQKJ`C6p+1VhD9q%7sb{Z*osXy0 z5L|p4%B>#;)?Z5KdYTmC?4Doe0rkPVL%E*gzp}NvE3>j3`fV($H-Y+k$g|5e>OD6% zu6hkqk8bZ1E2F$ikD89ljXVMQzTx)m&0n8fW-H zf|YrbQ|#lg1fM|2=)bjbP8`)|11-J z3^O)n?(kv^jagCo?IZ~r|m7LJ7%R|n_kE%qgYzRT8} zIv;@O$3b?`Q6Q@G0_QS?zA_%E4-MP7H~+JFG`CaWdXJoRRsr#qgo~^m;V95G09h<@ za)X=o7Lne75fY})WYHnI8uT+}y{Dt*DPY2`hFK()`oh|Lv%P+HBHHqZQ z{2qKZTJhb1*BB_r#nZ+x;h-A?ekQlD|8{@hWnx`0tZ_^x*V)VscJs$=vY?VcUs`QK z=*P&}w5Ff*#3Q*umAf1MeZ0uJ!~SQN3X*G(QT+giA5X11ZS2tma5q1Bx9F_A*RPI+ z8#Qy=y3UeuB%rwk_yhzPJ)9PGaG2PYV3k_gC%u?$thhn`S=Dw_llMKWGzPB4>o9*e zugk)Z4b7G?FW9=s>)UUrY2XQ}x=mzBnys$sDyGHgJRc^X*%Si%4PBECHf2Smie(j@ z02w+N90_(AOXsbWJduS$j(6qwW5}6DRq3^>IL3e3D1V;VwK9sSDT;x6RK0NcZcZ;F zj=!@NlRFUe;diGg;*s9Z{3_Nv1dd|e?9~k~nFchk|74yp*I7J{Bn4|% zd$!94Q(f2a4w?arp8*KdqN#?xHx3ixt4x6*KG0_=jgKUfAHITLn0$KAX20L^u9f$J z3#uZz7~HtWSknd)g{dUT3ByE7Tn7H#BKpTp)-+V66Rq`0#Pyl9Mw+0d;Fa2D1QPV% zKth?^0$@IOF1>#@2*I_bhiiYg*4KYI9)0_X@G3}kIEY>j0y(aWv7A1b$7HSAGzid5 z?j&Rqi_9BHS7-Nc{~%0b_S1_JedO^zt-`kz-8gN!YKrX6qEVxxB^k z+tzxX4(OO;7>QI})3&%W3~ttEPijvNl{Q7=z=!_0|M2wFDLIx+v2rmks$ zW3Ucx#G82sqw$lgon56QaR|I>*A@4WM)PHwHFDVRwPEurS55S~H?C1U@+jiVq=$^d zXw0|iwvDfuwpRxI$OZ*9%~W5fev9|{l-i$E|rqq zWP=nPtovy!MFf8DKLg`Vn@1i=XtaXI5|L%k=;X$3;52*kA*RNvvn{(JyqyNV@S&7- zYmwz@Vda@?PuXs(qR0=$7Q?VJMEH`=Of}_}Jwaboyp~e2)JWOkKX;)MywE=>tQ$o_ z!0Y_+Y%q}KCreo1?j&Z46FIDImAE@(aipgX+lNNcA^dE;p7Y!FdksEN6m?B5H`A1v zYn#sCTErN4HxHVk9G{e8^?g1#xP^-yMP%6?<}y4y+U574*r{7w2H!&^qbfiiv2LjO z+@~Rj4uPzB;c#Ac@sVas}ce~bt?*D|3N^5| zs2o!Gxb*By*MwrexNx1^|M$Cvh!dF~=swu6rlLBP^%y&nGjQTcc z>jM;}!u8rK>BT=YvvUAdCeRJWVt_HzI`TOwEi~88G=|K$cxa{$6LLQ_iS%tjPXmzu zC32yGjjT_Wynz?a$OE6mCm@Qr-tZia7tD70CC|gz`JIb;;_E*@CjIl{s*YnftHNM zp09?EUxiUWGHHD0twSGv^M~Lk!cFPY+{Q7ugu`)+S-7jpM{2 ztNoMWS8?sziNAW+-!8>HJ--_;@J`!^{BZh4}@(rY`po0EY%+Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>Dv~x*BK~#8N>|F<7 zR8`h~ZF*1dy;n%+gpQz61Ph84?A_h9{p+r-ySlEruIt)6hzO|kUK0X=^xk{#ncnBk z%=^!qfk+7wl0Xu8Z?ib_=GJrXx#!+<&-o5IapDA-%q?!xjDUz(l%h}+MVN=707HR` zC_o{I*=Q`!KUG&*Vj)S&j-az=&rP{)jnLiCN|Fcy37ChW07HR+DS)CFj$_Tm8F}gZ z>~;!W`|SUD#?BzkW`f=KV9mW1E(KgGRkhVSm8eiBx7L+L11YrZ#BTB4xTgp;l1NpLy`@{{h4e^SW*dS7Lovss8*>8P z$h{`myso;U%8b3}v;X|=VnSdDWwv9*_K48P;5pGk35PP6Z8(OaI8oJQNiFE{(U$qK z)iwmX#KFq(`eN~O??>oWfbT^dMWFJDG`o~un z*N#(Up)J}gK5kJQj!{;n%4#zpb_@|oQIUXE*-NCKZjI69@CmDZApWsgNgIW-Sj2gI zJIZdg+JOCl37t$~gYO6m`#|)!O`zRkCT*a{z%Ul%$55-)3i6GBL)xVu%5JyX>^M9a z%BTyk`CPi;D#TfARclRSVJiCg8=qd#d)TlkY=TCwSyPh5h|QukkTL{&GsQ2vP4{Ds zoX5w-LRL*1sxK;tKpO0@zdP@NyCRznB?NZ8i6T&(?T{L@Xn>R~#Ee=gEB(?TPB4^h zGgv7Oe1J44V$t$aD|gK`UTAy-XyJ7S23GHaJ9~Y zIXJUlS^--+Hl>Vgy5Xky)$1@$u(jPyofVn*gqe$A^96sHY0)NkU%49YUwaG zHyhYi5?XGLW5i8_Arv$6Ph%Z=u-wU(E<%N~qCLBZT z2CYV;=uxrVAAaB_S!-jtT(46rRcdwMtOXCpi3$o^O(0@yq{e6u4Nt!B<|q$Wuf({7 z3BJa>5@o`ix$}HYxh2g8tDQ{{RZkoBD?_BP{zrK6)JAJ_pV>xH&t`Shp{RQ=8r^hEjH=GrxuJHek@8G< z=8nm(l)WzJz=oV|8;J+UB;1vJN9+(Z@+Vj2aa$&+}(RBKHieA7xC;S zi!5aF(@XvK{<`y2qlxVi`G>VBjk^!_gieaJRG#c~N%ZPFn9+!aPFxmXJ#@UPku)jRFCerEC21%U=%GYH$>_m~1Yd0kJ_+yIGHl z+~R^gWWk9mCrHwM*}Jn^>+9#*TG804ke@E^4ojK8(A&6q%l7Rh`kR-{3E-d;CdRwk zI=3AzZq`~6ICC)%LxEvaz($g=*@6eNTgh|(;1G{mjdqhUgvUWC`zOU+Gk+{yv#pA< zQ|Wc8**}!b`njT_Pan)D#0X+H>p(ZqpPe)s>;lgjv0mz~&gQBHt28{(gYt$Zq)S+z9IP`NFmRB19=%tocz8WtDo;p)3|$;}HRy}f+= zLj8EkuI{pmlBPbD!Aw%;_w<5}GhZ1B43Yw{*u%M=$KzqNX6(E_D{eEG6rFKgPOOyE z(W5VJQk3=S;RFGPn}Rk~X_FGM+UC29aJ2$A$!+kQ&8|0DeM3S+_;!!@Irq$uu;J1v z$QsKvM`wk~Y#zTi3xR5we3uz@$@knO*!ua87|Eib`wu3yYo- z#HP92ZUt=uT-CyRy~#|W1ld{f!GFI#P_Iawd;6P@Ec9jpW6`G>P)O947Vl5XJ97N! z8~^_9c!QY(w^b$+Xx=H?1>I!6G87m(1)RrcJUVFxd^-P+!p*8)Jb3v8ABD-#rL!3A z_Ub;vo_a;I#w-=EA5ZeK=|%1NX)>}CObwiQ?Z86>f60n@`mDY0{*;zmT2|J}xg{mI zx}-KVHd57GZ}UoAvoyST&k3b#$Ruwvr^dW=^`Z!}^H^q`k&WXvQ=ih7Fk`A8tEaSH z=@B^LxwUiT6_pnEU>{psqfRm{T9BF795XFtnx{Fd-jXssxavq+iPdLT1h=?Ko{$vM zn48sN^_&|gD>#weB@DWCqOY=D5s?&YXeuo4u@M-E*$cYKd}SywL<%6Ji8AZ($O+Rf z&@yaZyUIQ3^vsZ6$dpT9HLdW=47N4T|o{qLLFuEk2W`&7Ty`w`$w# z>kDf;Nu1U&8&sX;wMIDcPm7P79PL_gY{!;UJzjp^9CO?8Q#G7`*tt{UTrK*J&X$6* zrhuul7fp$Y^COP!*`3*La1D%_=wjY?Aj{?(eRE3Ugjj!lU1@%c-os1U+fv)2vf&_n z7*!iXfsvp9On1a;W>Z#l`I{NXr~+pdE8JaTcAFi8t56DTsDwa>;wrMZi4JM zyrnm16rJC0hrJ?Pt=i$v3Cv(&-!@DQ3zMCpKtBb*(uOpk1_iqGfoCr!3(Pqo`h*1cCcUkOr{Q!&NKecsi1U!jv^0C;$w=vYQ2n3BCEA zCoXER<7j<$r{gde`)$_)j@r)jATq!LZ9H_AX(K~{k)!}H4;!(tELwE&-H(lA!Aw~U z1+I4r(2|F;2*6Nt#*%vn>?~tSdA-j8rhh}E05FdLw@cY}^sNuS9AX(vHiiN>Bn1c- zyQjUSx;P&-fsge-52L2Kjwc2)xWgG-4huhgH1&fI{)6@@HNz>0$;(h+kQBIXI94PQ z9XfdM!~gteoTq{gG1}<<9 zY6W{qhoLj`H#R6h+X^y&W19kCA4d=bm&+MvlBa)NyMFcn=P%XQYrn2YAdL$m@Oa#7 z%{*gWA`fDauY)cOqL)KSa^p}2&O8>2^;$E}xL8zvf8tyRT^I^y3?k1BMHw(tbAUcE ze+&f}3S0>VoGZx)OP(7?a1UZ3zTT2BgL}bfCFG(+NVQ|G|U)u~<=?)v#r`FbXh`JCN5qfPq#MCx)3S?$wWo2gi z`uWlOumK*ly}eE5>Url~cfyauVjhQu!T4kzh+j{j2*e|CJP@XY&tcJk+9*m{Ek=l- zIP6PZV4k+N4i=6w%ro@4e9dznKYkpc6qQu0zIXMF$vhCBfd!!# z5haPZ%OD;Q97_znO}W*b5c3D(Hi$g~Vgfx_7>!gvoX5#L1cpRKhCw;1hU^{b6~L!# zp1?b1(!7LV6w#*sx}#ZVllf0wu`mV#v^JGyb#X$cMR)=mch%JB`5x}4iO%_-E@_0``V`|M~Vygaq>y*D{#sAB*naYd^}il1 zEXY0}+jpI=pOinJMpPbF_ELrq?u7UnsvU~IQzhqVC?)%>-`{ZX+>x~wRyzJ`_ z-fu7=O&Qy^rdHRT*!lT)={869j@$zueRRNNS8e#uo4fNmAyeA=w_pDBu=CgcC-2J_ z@BhQUk2ad&jkzu7gV(>Ua+H7cn}7UcOTJZ6@WxB;6>6L(1k3jKUj1T!?bsUbA+?4M z7dp`6JPVD;T?>t2Kw7W(l61hUF=v~u!5?f?Ap3 zkCa(ahSXC{5XXq-S2jXgB)8o22T0?1o?kM97GjQv#5ofdOvF!~EQK#D7MsgKEhhfV zhBvgKTx?#|XAiBv9)sj-E6<`QJZ7rtdy@ zUfk8*d+uyAmhNo}T0Pl4`^X8HH4qlrFM837j`F{Hb5;JgKXt?|U*gw!@Kifwq7?a= z-Le(8O&`*99b1{4%p(?yC1)RyIp=B-hCNYHo84wKS?D;oSK}F&_eY{8h=Iz9o-VxO zHnuy&x`y!D5R2ZbH^_Hw+OGGVK08|4R++hFUru{R%l_R*DwT@-)E!^#E$wY8+q^Zk zjU+2`(@)m)wH9aXNG~;Gm`SVFnQb7126!%v#i;!L|Nep`J+~+lKbF(!Gj*l6bI!O< zpX;T|&MensvL!25*$VfS_2`6LID@hHV#DrrG=tI4OMl;ADnC2RSg>rREW6*mIW4_O zBj$rbj}S<#C%3-;{F&5$-gLSZK?KX!+@U%2eW&m02`-4nXg@cU7@+RSgpu>7bKY3> z`skZ)zUdraXL*;JhJ&26 zfOsh6^JBt;uGqq0!0ucu3P(gZ4p0{ao#)Ar*jTH7I2P&yf#QK493H1N|75z-<&OFB zETg)jv|bP~^O?sUnBrn8spy0-s8Q4AuU)$|m}OFETJo|Rq@iKS6Fn<3@@n*W(zNNZ zK3s@@df8#B+x^vcP2^u6PlgkR0r=`S6*ey76LzD;Y9VpI=#)5d=BaE0#)W{XRtpgp zH}iJqv*MN*AAzc+Xy3kl$BU~VWGX5PoH-@1`1pxlI}Z+Nq#5&zn6lzb>SdFHWzZyx zhV1NU*O;{M8KPQ3bXMnp!G#88Hd;~FHE(_K_Mh%g38g;y!`=72y+^BTJ$ZQFzVt$k zse9L+V_j_xNAlY`()Vmh?ZIiCO8>*+aY?Pgny}=q5KB(E(q2?kD@(XZa#<>j?F7I! zz!{b+sqgZOxCL zmU7pVukZfl@2*9^9INgw%z_H)8eoxVvwIV@$J2{L;{A&b?&~HAHo@XM^Q%fvRZ0AO zScqp*tdD5t_Zy141vf8_xMt&c^(q49F`Lagoo?;gwV!_aX;@g;rcIlcFJG?FXe^d9 zBX&XUmtQz9(OmqMSO4$o_2lZ8jIvqO=^ekM^$2Gtcyu&2cUxFeOp#yOjUk%+($4Uy z(Z<>uy-QHMhdJX!z3=1+5sAvXA#R$E4jsi2apB7ByvD*3uhDAZDCFkm z0zd40@0s<@zDp;$wKp|(=&fYnGp{`0rOo_qOVhmDCuMK{w#x2hRTi&XcOc@9N9P2n zKK;u-^9?+fQC@T^y`BiZ>zDL&ZVhm$4Fd zoI7`JY-}vZ960lExm>wi-q)v$i;FwA9hX}h?9o={WCd)KM_mXMi1IUmGAxp;fK32+qWd-?@TjPY*D$;oThMNOI*D8L%3 ztMqI)ZwcE5DADIU7cfsxLzB^FY^W%$tg33VxXewNv-qZI&HHy|lvP;V=e_ybY6%9K z3(PkvBEUn)@k)sB75K(Yi3{S?Rh5=koq2j};`oGcE7JK;nYn9#Z6BjK@5~b}uSW?v#vi#{k-V#X&1DDK? zDofp)Q&!z*3H`&1PXwKjEPcIQ{F_$JbK^O77N|hRAxtcZr%Yd#TMRWIQMdl>#XH50 z{z?M8Q4Vk3k`)o|kH6KPkIar8Nsg0yYMUI8v=O@%U7eF3m4x3KYp|XS@obP}8;IQ_D(-19c`hUHk zJr3q+XA?MMVK9Otb+wB+dGaL4RoJZ!Xx;`wsj{-dj{(Sxk|=nm+I%m1OR5~f9d?}lqu(apxNg9mlu?e6GU%sZ)VC^mLlGWcG=lE-0lAiB@*WU)g;w=)X=%NIN#5rBD4q^JF`?z>?z&*dv0=P6)= zcUjqfuAm!!cSp9g;k0M$v}8EFxF9d=(jBqGhPYvKmc|zdPNb!-|Nh%?cF!|{bR0}k zmoSn#?}mDi*V2PDt{Bc0uwUKb6M+xu{<%xdfTqj#J!?!U+Ep_;0lMHQD{$}a&W1cR15_e3YEy zal<-B@-P#Ep}-I*K%bpxM;7Lfp#VdH!Bc>~lZOY4l83-_SCnNe?0T=L!AxR?0)wQ$ zRcD^7b6gu_@0euQAqB?HatzEfY^T(5Sb_syhxr?q6o7NaSQtCnBzeSb+s4;gcPS$j z`Z(x#M&wRye9>h5wiZm+e&1+~cBZu3!Bt^6eFT?>^y6t8z&z&$S5;q%d0-6GYE5fv z+X$kw4rjoayn~?tIPGY)TD$!Wh|tA@3-0USDiigbn>X+zz4&oDV_)DhJe<~GqpP|Ap`&ap*mk%; z&t(w=%iLSJZTBe^0DN+A1aLetlm*V5fc3{_akv~Fj}2G_Aaigwi^u1%Ss4A9MZiy7 z9;BmzKLH;Uv6%q}3eNiko6X~MIc(rc+6@YT2HUw2ct219YoZtd>sRcH+A z-Y#&UJzdgdu;F|@q3LcpomtYVvH;MgS}CuotjsTLP@1d+3sZD=n)vR)-Yy)<+S^ul zGPAIzOGDvoF3aB0Se21g+NQIzaeH51cSUhULzk+nqqS3QAsXRXevOq{!rqipMFZ39(~rmLa81t1QsO^xkFtG%l} z@8E%h>BUXJep*#uN#@DivKGiznSSWdv0@qwmw85x0?z$HKU^q$03`v)oRO=+Q4)pV zFTe8Y2p4)K#ew$2qHIn(RngehQBquB%*xxB8j*)C0v5Tx$idn~iG!zv08_5HFd zTVpYlS2asQLSf2pJXon$cI4J6L&Al}w*7Lr!WaaG9r=buoI5R-*+~wG(QCK5^Wvw)Kxb zdrb7;6zYQ?cHm_9w;z6|AWa*-T+ff2*0%SbfByJ{Uw9nWv-^jiOX~Ad^DLgzwfq+a%X|xI> z>Nb7m^u@E2gSdvgoO-`$i~sQagF)u@;zlKAV^3Xv>w<~FiAhuAe0gT_lHrif^gs}P z-@KrTvJym5l%e&V5$|m!O(xVUWiq$8rKR$4x@>xkCxMHCW1{?mO81vg5|026LD7+u zJX92(JbUfaFWhna%K6?zTSKe9kD7V^D{F6$-}TW^_nYrneQ$Ke$KO^v5Z{?+m=tj8 z&;X?kPCl^k{pUabdGEdVKK$@Q&>|R(M!-F!{RoCxhg0e}eCkG>c_^EelDPZLm^5ML z)QK}E1_^LG%5#qmace87s_ij_O^kOTOa&yBaxQ0^j^2bK)w}YX_^`e^NWt0F4l2F5~IU{!^7s!o)YXP z;*M+bFBasv#_VM%fk)dTddFIfRg;8!sRd;<|OOMX1w96JRn4J*q zjVoLB?a#G&1y79d!OT{Xmyd^#h4DomBEPtJPn*`raq)=_cI&Mx*`HD8J89PZ#E5Wz z7j1jZ$%4+fg*VL%mY5B8cYi-O4q97Qsv+EcWgI|JKbH=07}IKX0081V^Ee*1rw6K& zgx<4qrr3evZbrpP$J}Un=I0hOH8|_rFL!6RO}Tw#w5MS7m$AXmow1tIi5LpFhpxv99g7`@<<^jZTBQ3z;|1w2Bn z(!uP7P*Y+NPXq8mrCn{$@XSk)R0Tmree%EraKVHyb(XlrY&sjh}gJe?Mz`0393)9Y1A zWo1<*#CD;zZNmVd0&N?p^@@v%z%W#Cjp=?l;RZqg7l{QB4MeLmkT!@E!V&V>8nxC& z13sQIN2a`L1vIV-90_+Z5>jnU%ga%Rd-TPH0KdRn|A3&B zlAFDEx+|CS-Ck6c}v^&_@Z!Qi1mL_J?S`an=r|rwj##PJuI$XZ;uhJsgZ{ z-(b=VfiPF8*des!M$U4TM&T+IJGSn%FK;Asj)&>UQe-ey-^kPY+Vs7D6voBijxFZ7 zq7fSMbRXG)xuQ1Tn2Co#@NqG?v|Htk_DAdZnZIjDfon2jA=7Jy0>h^O z+RbR&hEGMNgd2$hPHo$U_3Ic_^(BY>vhb5P(u5fweT5;rtWgM9eUaqw5krsvz%b9H zCIyUvR;LHtL$HGy9}|aZrNhgB_T&NItN|4c;5dzAAUA@80)~wk$}rERC!E8dr;{|B zLqmg^X*pgfkYA9Waq=WMz+I}reSLjiK7K21TRv>W(Cd(SQ1G>&ao8XV0!sVpSZG(D zfgTj2{p3g*nsmSin5VtHlg(yDgfnovev^yZ&RKYWOOj-D*BoK2x>ETLZ>&DT5LSdD#!r=7_T7q+Sgm~=KnN1J@45yZyL zrLDPQ$JYI< z;7QTmqJFYbl>D2=pZNHg@wOF{SYw=+X^@&NV#fE+k*+oEa7bMdM= zxL&i^XFN%-C&>fcVPkPTm#@D2iTm$-{E5eAd6@G{+X*(-+tyfERxh_uEa`PH4j*VR@=>dv#@|Jdu*J)huF&~5+v z*T3F=`)&C5t+(F#_~VbyWu6h4Y(Tj&5ms@e0_C9?VJCa`Z2IMY-~ROe$3NBcqn0GQ z=ANnvnzj6um+y5|v=kMUo~$$9wf5nc@0%y%pcXrxlrksLk8dWO=mr=6ota#G5mj*T zt4G(;&trevHRYi{OyKm&`Ow-@f-jMnV^UVV z1^CDBzWd3#uh!1=7PeL2J;ASy+?LNjQ`S{aEBmy?!EWkZ@&3v$&w{KJv}3f z80sVv{?g0Wi?)r%ZX5L-mVzBUeG#kj@DE$} zW#lPtf93TRF>ZFNGVk>9lUbP=8JX;<5B%eqn=eofSo9h*;WjZjgweKL4JPeJ3GJT_<+;QmLYQWI>B6~Z_=z;@4l&RmkRh)4!8bX3N2NR>Ln zD*F5T!|`T7#L(*Ms^XH$RjckC4)f54q3Opr{ILGI+P0yH9mEB&LjNud_G17Ws?qAS zI-N#u1Z}0+Y*cG?aEGH*Yitz!O5=$RI8v}OF@|kJn!}@(39#4e>T0{YyR=%+8mQ0x z0}Rpj_V&hx#xo8-Bhf~1Nw|I_&+ib#akYGgr)XX|=j(vEXvEO&-p-CL7Z(O@cO)j- zH7TRJyBl^>a3wzAAr`hFuzd>*3Z6ZC=13rh(k=<(gLwvzlshuRgrcxz*6R&62c$Lg zj0*|?wm1jOM=pJsf#nkpLSV8uOvKQ%w6yQP`*z$g&$S;shjYjPmT}jto0brg}8NfW7t}BJXjWT*nKgR|I#)Ns! z2${Bxh|L942t$EEQh-6v85?UD(~9v(0cP7a9!HI7^O&Fj!#rbR7??(kM+z{i>hU;g zOq)lO0ym1P`dVMDj^>!&aCMK?;NB>z>dUzr+;9`_dg}0~4=!^_dYS2jfpI7p8i0%9 z^)&Bpq`m-;(e4R=VK6+yf_|FyOCE-Kes3ND&!|Ju(iS{>m|`Yhf)_5sn_XBONs*n14BYWXlP>u<8W{*_<0Yh^%xG$CJ6Z5{cdiQ z#bzTksfJX?P&#`3=Yd5KoJmVdOJNWrqoSP-DZ}aZZwiCKm?A(;mq`ZhF&KehHq>ZA zSPqa*x_uargUZ{Ri%Y8;x^)~j3n2}1g$6J)E>#+h)4^hM*$%+mOD6+cXVReRl8~?Ve{jk%==E-HZc$ND;C2i4)wosH+}N(S1F!Z!P$m7G{eI@_Isf@B zsYS2sY**OMo}iq$&d&`fiPWIRzD^8-Lk%yh1@K ztAE~hvKJ#bFy$S1(lCE$H#H29qhZ2eES&0Vswt`OLjfk2qG0)_<(9+S93~H)!jX^7 z!aB;2{N-PpD^ykikJVd~z2!iGkpt+{bU8FwwUc)pSy+$>1%N|(LOMEu;PCS77e`2n z0s2O}P1)@dv6#jhg}i_^*>&=dPyIbdY2|QW9U$m7v*~^yC=T33W0C@oom&zDVg%}e zU%?Zc)sngOKY!n-f^s;HWC#Wypk)M|23QmP!8vS1wdG%b{G~(#fZI?U-Ea`FP!~-K z7Su|+NL+p}E;m>=-tXWqEEEdC)yIk6HvGrAu^zFoiYSia3I!%_#k3fT!&no_w!)v& z+GgMR;2$1)_{sU+xw%E17Vz=I5j&~Y8m%a5HJdbgGfZ$0BQ*`p?MfrTv6p9O>^@PZ zu%m1aq3P|aZSFC_%*X9Iy{@CJvrlJ1fSXXDfyjev%&%^?U<7R?0Upx?VKu7j8=LF9 zR8|CbhonxgX>03f>eT_lR7-PPr`qC}%1B>(dp!sNn4Ku1R>)N*J3T1}2s4P-5h&br z_=`7IKlsr9eXym+fOGj=Q)k{gPp*Ds?Ss$%^Jf*x;bOhNy#F#_hrju4ss(ef6D>3` zHrtl_>woWBeea`x{jS?cfjGulY(?GCcmDigy`96+760R=6^}^PUQC?i> zz6%yZ$GD7CL`^62jKo7P^N5`joWx%;pC#tN)Ws=#XMGD67#Z!3DCJ7u3G@H-zyb+s zMSwrlb-(<2ypJH{Raw6tDKk>?!+Q?yJDIb6*Wpu5-3q0yyS=SdMJl^0cOE)@VE5r2 zr>X(vdFQWNzTKADAlHLmGg!sRBrV&Es;^r91xfUD1QDy*|%VUMn|)SOmtksG9RKi`5i{k@EbUDs}tmcmK6F;qiZb z_SQq1iV9BrtohSomfd}ad+oM2zRkJo-OoOI_kr5&|K3;C+g^F>aFgd7@BM8~xC`t( zhP7uOi4kYY7%}F7XdVd96`L{*jh{lq_#qJBf!>&~0PF{`m`Iz|XtoPQ0xqx=O?A7K zw8Ievp_T8bR?}Ip;M}re<(&(Y0wnGe6T+v?oRbp5$~uxmgin3+{%Nh*+4=1{n~9os z$I6AV-Zm3GbpV{*jti6G+{^PT6bPFDafa~|r_Fn8^=&uD%bJ@zG@zE?``r7)nmglI zEoS$ppMP|cSXJEEaOy-=(40m0EeS0+a=O|;OrJY<0w9JCsBC~RB&0Sor$CUfbh5YC ztX22SNe;8szG`C{&zh8az{f#wcd3iPMY@ulC{MnJd9s({gWXvRH#*ZuoMn>|$p#m_{ z^Z?}~mm9PIlSLmmxNKD2BiAAv9&Fa_>b(5&9-{-A!Lfs(|X& z(u(^?+yY3cqy?djq?N46P2X~?(kC+9l?PMJ-AjT1sMHyiF^YiR%PTl0PO8f*=|tdA zg4&c_Rhzc$IZ>q)a5+$o%*!3M!l4fL4fHTuFae)U0C=IvSaAHnuH5d3xG+A7m`r9s z&-wjb(BPHSEX0_#@7oVP_~5tkOFwX6TUw=by9=&G*+M88gZ)4fJy1IIdJTDK|*r{{Zte6$;>mlPIMuP<=I^}?gq8ZQ% zQQOQqN<1yrqp7^KO>gV1$k|hDy6d6aC&=tN6A8^Q=ybqfz={T)fwWpoMgz%``gpq~ z&$;!%TP6j$yNWqB0QfSJ_JI*YA$TQAG;`YIjuSthY-l_6^Tq?|rBvvYByPjcTMu=0 z)@|H++o^z-{*7~P`kxwW&q-cJkPI6M*QWggBS|XGc2KJQn;)rC-X#Jxts`o`0krLadnTL zh=_~vBs&kEDpIlCVgiCeu5?%A|9Yg*?iF?GP18IuOI=x+Mi{mzCAc{&bI*w~!aqFL zm##6cxE`V2LY-2@^@^UG?4EZpHMO|aB_bx$ja#0Z1BjskGR_6)ZFEdht=49xB4#WM zwv>JT^_C8kARu;P%7XdxrU#b({Ke+|$H~yUUVHwwh^SDi;rKV3_gZBWZ=D;*p-2#( z0O!v4jtJtlfBxlF#zZAgS&+hSJ@)l?KOafU zj=lFU_osxTn#x_r8>igy*pguN4nD!|6w(H421WS_y(-CW8SKzi>EGDpjz#;d9wA3t<}r7@%T_(4f;h0S%!*9-j{Z zLIBMW^jZ>$7*u#_olz(NT_k;E2Pp%$ksLM-7>ELq1k?+F5G|34O$I%jI^Z73M!`Ol zG#br72vGiCymN#57yto-s-t3JL68FYF*FxcZ)T&3) zNQOeKb`&%XAY?(&f>W1{5_vU-@H#5f89kxAyv*CjkIiBY^U&zHB+b~odDGQmo_@;1 z41-l1CMfLo0pk{~E@&+Q3c*9^1;A#5vyv4i4kUvq1h-hgLiD7x0s=0?qN4M{83=mo zM5*iVwgVOF42F>NZVdKmkOn5b<6J@ChV;LrXTSZo65tYrj=%`!YL2TU8kudN$Z&vn z+|$z?5D*}f$(+=uJ4OHJw?mNgW&igJM&-QsjxWwu{WdL~!BfvZzx{fCy8ds&9QEsn z9j>4&Kya95t*tEvgTd3&d-%pr*N%A{v+MjBaJc7&LQjeQEBU7(O<9)w7Bl_44&W&^IUg>&vfWoQGj8dYei|Mp6iYRSKYR4JW~us0M&%SYa7o)cwXBn z8v=jBetx?o?doDw)dP~#!8v9*YBvH3$KdzD!yX!@fRU#FZ72-p!fY3W;DI0WFe{*$ z=M=4~Mj7TAn3Na`k>^nzy8uJYU@(HEHY0+DIp;<%JDBIQ`8+nxbwh&gM>%f8lt1S% z4|7R+@u-3^8)ZdnKJM1>8}0T{DOskyzyAbCU1c?T!pLHAJBkzt;zpsI#t zySa!sd=$$qMomswP&jeZ5WFZ(2ytt5?#Zo3PNkhX_0^`sWo-s7K?7sbh@K!YXs=A# z8x+t9;g3UD+v@6?6h<7dHR;?=08u&>oyH-);3JQ1$vblBaA_yb1Ed~%)#=n-nGFc| zf78&5P^{Avht5I+cw#skHP%!lREpAMr><5`0?9c?KH7?z-y4$36}#|=?6U0!7m7~KhJ)pg|9P_-A+S> zvM}(vL+RpKAVANia=2-O`}o;xG{h<5tkm(2ZW}vu-E05dT?BZDG`47e`5^qywiYl_ z&(?Os{TC2Jr4oriAQ%yUp5OL6BX3v%Gg5EFI_vOmHAc}O?-rv8Le`phH8juy9AV8*jh zOU~U8a=AbpIZOwc4Pp>|7-+kX5sbq6`HbvQo zFFdej&4VxhXO9u*;Od%B{{Gn7wGaIH-&*`_O&wZ%EgpG&C^ufP<3TKmXe|sZBgCw>xX&8=w4aHg!rp}yi|Mu=%Z~Xbe`|tU9cfP8vVCS!? z2Y=kKzfw!}WWW8y18di;edot4ipA^8Kla1BuiW?0<6rJ7fP?d;R*EqtD6U3hoX}46 zh>Xx56gA?K2b}%w1lNVTU@jK$abToCrW@g{oX&$3}SF_hEirt>>l&rAd- z;6}7z45xnCPs+WCXo_18bxEN^?A-eEu4z9XO!RikQ&C?fVF?1cfsXq(2 zyts?iwZ+vwd5N2KWr?enKdxrtT3!(RJUu6U6u<2L{9wpQA@Z%)HMDx&nIoF{r^>|7 z?)o@Pgjpbbdmxw9XffgDyap(e^DSZD6|iyYA4lR4di#}R7A47miy@D%1+~Fo(+Yeqy3Vjq?k0VH1uaXLvQc33JL~vPNR6V zxEWHGyJ&HnJPa@a#h`P2l1uoA30wFLEc3zVc8DuoCfC<1gKu$V6O3Ep+-Wa)kjoi^hi9K6 zJ^KN&!N}@_H`t6mt;J}fQlUDLX4Jhjdt9*oGTVKV$Dx9oi>kAI4wJ?CWmjApcCN&$ z@CR08o43^ZTTo=|cs5nEg3ar0_G1?V{UpDpVp09KNN2N&oNqhlq}gS*aQe;#GzL)6b+1tGC$@A}%H`Kntc!K4QUO{iSRj>K+P(XiM=l#N`Eg6_Or3}ZGZ-cz}z@$73Gq_b*18zoe45#$dZ*L;sy95q}u7{batv^I$ z(z}KoYu#^i=lIc3QgVrZ{|+&=|3dQ{ zU*x#?05Ovh1SsDXl};ffPmJH>Tv+of&(q&TX8c0=xwwx{e@1|p7X%h8 zsJ$4x+GWZ4If!y2aV1 zfI3`cPl=x zCPjgsWw7~GAxXmFP3*g4HTVc=t$e!{Tn?*Ddl8UwgRg#=+Q#$g1zJ~q!#q{n_gTzr}8&o$2G|-#T~8LzPO*IijH5iq6tP=F48YOVd=Q zeogMVsTGTd!1IfGyv>IGWm4w<=5>cdW`|jGmhCs;%8@d-U=ZB+PaIAy_P1I(A$hu zrLh}WtVqAazjZw1Fc7fnn!9V|Vg}?B&<)OZWM`nYb+u zDiFx(0%Bxws_U~UFPj2Wy0~Vu1`IE<-Low^lyN5kn31vGXSZ{+R_b1I{_HU(jgd+9 zFY4`WorgH+7#FaL<$8U0hvK|r7uKjuip7PM!0fb}Os=q>nrox#F+3@Q^!Sg&l*+C1 zRYmRZ`g7!ma01Eiq1_;}nCdR>+JZTVs5T(*W|27kuzwrTv^}77^a8N#rbUxXJM+?{vrXe_vX)+2v#RPBT zw;#u}&#pws8d)mT@%}Ez-*^yLF=9=mjgQFOLp&~oq$Ne})tgOcmm}aWOn-~C= z`#c{@?@cl@w*Rn0@a@35o?wkgx8Y&Jo^4$eYyj->=t&CIg5AOKhe#3pWK3>)0rh4_ zH{X%|rNHXVyR|i;@5vHKJib0rxwT)I%WCy-)u z9|1gp(y&R0s!Gq^EUL3(4{sy413XK%8WVc2XIKau*LrGMlUHkc&;;yXxYi(`iChFn z-rR8(ALDW#SRKldGor-H0d|j=a3~}nFYu^N8joPP-vUCMV<0p5EU)^ju@9Dy5|XlM z5K@h`O{ZspxZQd zOqC3n4%I~`z@9<99lo9h60_jx2n5;z@+5aKF?bE2gj5KTgJ62?PFsMGO|0U8u zqQWo{1?Z{$P=BQqBezk&rY!PB73vh*^&u=M13j>T@rCSpG&&K1RZz)lNWKf=tHLZ& zLUY1gNWHgqHtRgo=3v=cvwB#q5U3=NeKZE#vM$xOYgtMVule9W$uU^SljWt}yC60c z;?r44zg0CrF_+)hfafU%$vA2T>j4D?Y1-QGQvp1Jw8!8O40Seg%3ypBxRVEA0L!a} zH-UFI53O_*u@$CiunCOi6mtwd(o(A|%wqdKz6JAL8(V{ZpQwy`WE1To31$jghI?;z zhJ-jPaSI1D3j}>lq{nO7DX-GKbv^bF${~)knV6)u{HOknpH>AO@S4}xp@PW9r_g8h zE&Yud{hWnwvuK#CiCMB}IDXxVJo2?Cm&_s;5e8cHoS-sK;fVJu9^N@*u}^}o4%TZA zW5qrdunybL0(2G6+VuTPCAKxZCKr8S52;2?e@7sbI5KKuBpIA30~Re(v=}(dZi7=v zbKrL}9Jp;w5k#3J`ICk2CZrApX3JQ?FWT8?v2K4V!1XZjqy`)JF%P)I1_drG*Gb&c zt~JLfeA8dHx;m^6-XIWDAn*+RxEIcMoP$5SM}iU_+@5oI-|;ghpl$yBkSckoWxL6b!S=cR}9GS*{0c6#LjXe4P!+X z1%`o%=6^+&`>OEMeab?VhFSfwl81ZS!n zvtulu$rLLy7Zai0;fHxL3Xb>ie6^7l78a_jt0|Kr!<0>O9HoFL+&w(P!ooW{JIt5i zQ-Z0+gr$!o6juV4J-7~h`S}78J<2d);6A<<$!w)69Oq(Crg5?(qJ-847zSyxv&zWG z$ax)e`bR;tJ=KWdhi8Gp3YN({xcx4Te5@vLI$Su}YvEB*a93#vh=>Fq2j}kT;e#Sl zY$BO?dOsf{fheWF70Nl*AikwX2LP6S%P8Tzb!o7pteIX55PaCO3y}yNfW<>5N7MuP z+%3}pH0C%6IOQ>)>tcRh_@bvr~ z^5YAg3P~WK{JdjB{9%^!fX~^`_RTYE0y}b-g zZ8su!8SV82nE-o2fcDGr1l)g67f->^>mlB2d_OpUY4kwJ2hhNfj_piK1-s`w|D5s2 z)eMhCxAdqtB-9z#a-`+xoFT+Jm01qVpTs;JKy@V!;q5IW$hD-|e?zbjRL^EyznV;s z`jF)s0@A_6M+0aM0qWc>6!Oe-Y-#X^@xWONcHDf$?c@D(F=kf~wShL!8vKC@-EVs% zRglh%DVG3}umTH2T`;+tU4i-n=@t3vFVZ8oy3;iJh2?-bnCxGB)S*WW8X4pOhh9O1 zt>akS!aywzP0ba^SC1a#0p=zs>xuy_ZzVbpKb7r%1hR{?v>%eXlLyA<&X;<(FVPB& z@Eo>+YI#H4HX5cWR>7fQVw3@*Es16+5`|=_jCSKZPInHD@UV)#5O%YcDOzZGT)j02 zf9_myvq#L_St)x=rUA^cQ=HL|+dzQ+k{!z^1}+)w7ZxelcWzcFQeFrI2ap1`;Tm}f zwl|m0B!boQA%Ey^tFF3hmH((jz#JApQBL5kRI(wkNRyo((<*8o?W2z zo$9qX>YG{C(*Ir39w$@$-Buw?br*x^Msl`T6E;LRPPUo3-mzz3Vq+PaxUdW$J>!G6tq*RBDQ$tb7WTy%qW@dsTn2((bJ9r|agHAdLG@rs?f! zE^xP``DY)ms)D-T#j$8{#qzR)v-|NBA6tVn-9!!H4&mGH^>u-D2BD$0?Kddv2>DWT zXBchw-matyzR#z5mp^Zv$jA!q#zQ;qn>GmAAAe^9`t>g`i`Q{&`S0e#DWvpnT+X}Gs^w-)B=g4jX_GwV-SO^vnkE!K0XKFCb zIJr@z$iFSaAT%)4!Qw04Ob@$(SQF9&;?h_kv53hB5g#z!n_cfXo0}^%g*W|-1fp4S z0GD$ngYQ1;KjkQ>DrFx9JItbfv^`pcE*7ZO`4D>l`ykJ$m=htV8ZH^nCSaN)H3hYN>}<6VMru;*@~|`yh8DWoYl6fU1`mJ5hrlH{d(&OL9etvU--G}iM2qngKpNlhFmH|=k&o`~aCc(nRfecRb3kd%%90v?&4?yV9 z8Ai+7z5n`Lv+>-bN{m;ghPwNkP`1Z@XA6~l*L!{w7d%iT>zi!uFIGX*&4QxivBXkx zxlAlFRjwu+Pf`+ifO}B^&*KJ{xyY=XiZLH<#yye;#6gV z`A=iBKuXvyEgo9<_m}B$66k4-Fr~Q z60@7nE_?5ku`%tZ8)ItRj6ftPAwter?z+f_#AVI|qb-UX#;8s?;AyTKv*#lrL>~wZ z!u&bU>9|bKML%XRR#NLJZCu{|WGBTDD!hLvpD6n~AQxa=9Dkk5 z=75wSs$so`bP`TpMU`0(0{iTd8TF!)kCRO7N6zp?`avKL0249)r&sn2Oa=(v;4))^ zQV6@LUaW?-yn$lGMdmCZ-B&u4FX}g{jQESCOkW1m$0e|)rlIRM*liAeH0QZ2!|#lW zme0BdaN1^VglT}HTw;I#+*NnLsM!~Zvj9fX-whxViRKXU3sG2_n>T!6y`t`rrf#*sK^ftU?)w+pZxdm$27 zcR)wXuD2;*f)>?|QvQ5?w)5Q%Nk`2eaW;FxwfcCRImluJ4a&trjt`d_bNJIL>W!h)X?psKQXHyKE znYOJ^5J*f0MC2!LNL?%c+9X{5ebM{6WK(9g1)5T^Mese&ox*GBtbe$6Z|X|O-&F1z zAe-L8fXE&go0>~eP-<0oNXv{rJh`OWanyYm@zM2k(8BQSqr!r+@e&Odvx9|Ow_+8bA~**30@@vxQ3FM=)Qn;_ z$uX~i;?pL>0N`IMR&b4y85kv}Y%0W;Sm?ZI!;NOqaZ1KDE6!<>beJFZjbQWiRsZsg z!Odo7^xHTaVDq?y^8#!_wDK6a!Ga#9ZR!EY*gIg~$$yK23$KX3Eq>$96tW?Uk5c?t z`GrE>2(odmyU+KSRp}NUrn1}x8P-7fDH3;Sl$UJ0Ep*n99Dx|zR9Zv zJisC!p#t2o?lyYB1MuNZd4nUQc;GiM26M9)3FRm>Kx7qnb#eI$?i3WJcGjNsaBin(cg8^uQc-NZu#>&7 ze~#n??_b$+GEdiMC*@#sr_R?d)xCr(v-S}*ZXgA9*FS)Va902w8Ssw2_th)Jf|}A} z?u!?&B)IsAjg1Y-@n!}x>#u7gZ8_cT-Vbr<=c2gjWm*?i;& zrXBf*WcP63gO$?2&+f=HRG+Ch0@ZcciHWH8}d2Amk*B`1EB(E?Vjjx+^2Sg_FViKM4qbK`^D8cvV zPE?3z{Hf+dQOI@M+T#zKD@ zPaiJ=X9=M|g=Yr*?u>eyyUgQs82joZZ=3O%jwbKC62^|x8+^y1g*0FXx>r$Exn9yuksiYuhD99{ zQl2~xL%e|oX!X9jmK6^D!=pnF$fu{Lzk7TPznzk5GejI2EYE9HKUb>UMG zrvWt>Oxdty(PLhu*?u8j*#w*#^c6;E+Hbc?fIi*|YXxrq%2;FFVVeDOUwgKhM~Kfc za}X;e@#$SzF#yDa;ff}2a;#Q+3Go0}UGv9}2_oF}=(MO`Jz0vyy1duknp4l;m_ z(0O7&mtl!nW`xvlQQOUkgTYiZanKRH=)h%o5Fxxp80sArBM?s=GxY<;SJW|}J17JI zi%gpSfddoH;|B$Tq8?6&N3=PYg#p9KIFR}q)b~0j44B!G<%uB6FT|JJz{fPu;~Cqz zOYJWq=^7rzCulgwrtC*pu7yVtB^bsRYH2!JNedv@mWS=6Ru8QIqMUcWZw*je%N#H2 zdPk4@xz}dVa2`L~2}CLd8j(ybt6z^T-X&?Ox-oSV%U>Sk#+ocGX8omJydn=9in@C< z6IRqn%VcofO4ta8%lPS`lT3bi)XU%U<=IWj#%|svI_kG%4yelZEz_b(6AfD~8zRV9Ul^NxAWo1dsQKLpgIvv?; zuSd_t3^?b{h>p~Rv@G{lhNo+fAxX$_)s*yKPpCD7_k5|aZ!twzx>*V#>dT5m+Lo$oxW3cUAW3F2fM} zerPCxL||N|)QpDWkIFW>Yyf8`rGYtiRzkabsVPB`@2+o-K;5V~xlWO!xi%ZJQe(wiN zeoxOy_HLPAiMxN!DgAmfIFR|BRHUrGP;3Pic?Qqn)$q$~0>~cw7`c6n?~hcuz7B?s zrSTIx1uridNWsDz*3FGv?3UC8r8Om!_*aLigs>bfInAz-}eFuG~ z=;a}<;kFmh%uC2$^x#;~cWzqwL$&SY<T61loS=Mf26ve6Qn3i$VjLEd6N>|3j746C$jF9Pa!p_5_J12 zk2Vg*VY!U>dpL@J|9-Q^7k#Sm&Tefr`t$WuZENlR!k0~@Wa3Gnvd!Z3HOah$-ut++ zc{1bBJ|jKRXY6A&r;wp}&si&`1X)-7WN8-J2B~%y44P#T(P+cuqQ)I{; zcea@|L!B%wMR&1}gN;Mm%kATUR~n3!$4$^UP-IU(q2GO5_6;1LF2K$`4#p`En^S(8 zYMJO-jB$oV=^B*;&78@{VT2qjsoB&%A_ZR5) zK*Olt9FH~Qp7b-`f=F0ZQyh!Qs6CUjvSw|gUov2oe3MYQ{_mEgjoPVAgtti=R>hIB zMkvW9=rKabKffoxDda#m@dAlkaqAz=P9)l%sgIqatdYk+GBZ>9K)=0{&Qnn<42wRB z00|GsucEthdXULQ#N#O>f;e9hpNj|NGfJ+2Iz>m$3j*0r?;B}75Xkp)7q8=A=5^p|mywy2d7yeu9 z-au=;y+u6FhtF-LP+LmAeU9^VR^OUU^~2BYdQ%fLRszS_u6Z)m-m6w%7`{citVmtw zO<#BW(f#mD;S1eDp!zK6wG`643l*44?Y`t_wYyFcNyteSS+5Z_iY9nipzx<|ouGhK zF&T(N!JtX>rqlkfJY2j@n=9FoP=kMP+BoXx&B#ilfHv#%PWo?3NdNCBT_1CD56_iL z9?gDrEHQjLU8-L{**QNLv|a5zrt;-f?U+CrSuf{It4Ai9&Y`3RL!C)yx&E%~X9wOT zq>w~*)%rKfozh@*_yvqPp5_i~DCCrsm{jI^}vFL2Kzzaq@<@E7|F%|seH1uBE z=SM;RvOVZ2c{o%j9?-h{&Aa*}^2FBdt-GbGNO>hn3x;_vqg^+IQCTq3T@0Ep6$Fe! z5AvNUI`Q$Wlzk&%sGoVT0`}YRGm=TqN*px-J5rt-0TW3y8CM>;2;s}V{XU@Ndd2Ce zm3lIOF&3pUw(r8Rb-C5LUFIg70<#rW$A72ISSJb{=?SGPhBxImx+_Q{K! z;b+mITx(z!CX5}X-}822phZ5rB_$X*%bB5_ojL?TiXyx^!sL^oU#}#q=5yYV8?{me zVXM^b_HQ6n;};sD2){YVni&haP#yi_#x7XMY&D{RZo*?wBQ#XElC>dmz>%efAv94r zp%;k^`6?>|uts-+hVWe+ATq&6O>x;!n`qc6yEMQS0+|CsaVyYZ+ax*z2ii!H)(qy? zgr-RXr`%p*0#;Q#`nlpcCkBR@;Hjh%Dab$TyNSP!W!5*V(?%{%ykeV8bHZE zVi5uWwkff#z-h3k{{6n4UYno@RhUdf>U?s7z=^;WSV3V~c6ezpA53-vW?=@*M9332 zjUq#FS9~4=Pbh39DQCMTML>-Bcj%=802ewyxD7wHP#IT)0JkS>Hh_d!3(tmu;5={_ zzo&rwtbjNGk31k_!VLZgvo zD~&6(4sGMmSX^W|ep-%WC;{9kP~-?d2m}s=IzhtTTlq`Ew&IAY)|My8c7kU~9 zhG2Ab&m1L7B#{dCGx+*wHn-dtU*#)3aRhnD&Pk?%A>96Cd{18;X8FoiaE612*?nDT zz9{eibr8y0l|QzF`%eiQi*&eTH%mfhJu`NwAz(64@kXN1QOu@5A|G5f1qm+T9Fztb zknj=llgxO{?b#Bt5h@h|FjiSx3;kh;Qpu$7n z%_sB|J1hxS_d_a~?e`s?Xfe0Ey4UMeeU|&eG`ob@p-4DhVHqkdO zk+kyN(PMifEPgxGJu>{^2Sv{z-uemD>B*%S0;KtDxk_oD)54`BJG;7Gsmusub#Q7= zF7vN|KF&s?Y{=RDY3{F=;!EVE~GAzT9B<+07I&>WjGiJCr)tSfVO7}wR5IJ-^Zv)TmDhJ2cY##~7S*8_X zUAxzE<9RxinENZcv;<-$hQLkwL2iThM3Qr7eatt)chn!xwS3gTck|3*)CaP46e9H? zgR@II47ipQS=4IAJ8&I=6UJ;qLD#*LCcvbbm$BW*e$8?0Y%K$IM>I?c4bal+CsI7@ z2#pR%fx`P&jdg(qP}_!R0b{~cK^$wniiZj4l=!W^O6Uj2$9irK{r+8_ z#Whvr9Qqu$HT#C3!RF7-2EXB6tESU`0|2D4#Ln}z&+4_+VYpaiw?b4W{4DqzMZJ`l zUg#|hbj+;Dircg{kFajTy>O!UM(3@Bq_*G7mKlw;n{OsE7?^Hr?4MP(TfWUZOJtI9 zmIFKWU$dc$pwF)NnH(ciS)*jxo(308zxPf57DC=)G|55ek&4+k6Z3uBwMN6K>x%92 zr2XM8-&=pdUz)+c7^D;rZ9X4|Pm!|9ZX@LHlJf6|k?_B-z3=@}VPuK)`?h?6tagRu z>=iR{wghqKK<`3O!iKm{VyKWY0P9fNE4=(LI==UMqs6W4HaG&FYtJtoDx3C=lx$*I zu4Ut+r_Tb6MIGJ5=Vj%<-j6@OXZm9F%zp9mY3Fc)=-s8UuC219i$}1fOkM^jr3ue} z`Q?c_8G1*?L7rGu}@bZB&~G+JBG<6N+*5b zLihbqV4s_q>h*EBzLNCVd)KCOn&8q>l62``|gdm>Axs~xgk#u zzLU+Lb^;uR$JsKwwx1>FHWSmi_G!oV)TB-!ut^{UW<>ArEao9~zg)>2L#(m)96BOm9s|yOX={={LnOem& z?u!#e~~jKU=UX_ zfszgIzxx|~`uYCTRiM`e<6ccN_NkKN86Net*7JH^q;UWI`Wx}lOyswz5~QGYJw8|a z_Ih9X-b$G`6w?<;|2Sll!KFJ%j!=rbQ_T_BJZ^s(;qZSw+JjaO(o(S3ZmF$mY9sC_YU?mq z`(?bMhQ#%J;Z4+b|4Z^RV@*%FKUOvZ_9l~`=J&zTFM_t}dvVP{23-v3uFaGsio5ud zDv#}E?SrQ%%>XAS4$A2mor5$}LN7%Cm}I5eF@i)-Eh)o8-ccD!V0m3Dl;b1vEouci zZ{^;V@XB5F`G!|;NokYoRL(}|24F52N7_)wC88x&j1-ETdHqvL6BbYU&JBvv~fd)wDRtW#3!ZOZL%~=j0n}QHU~=@3fFrhr6|HeRr;hJ z^=3s<(njy@k6W@h(C>p{|5CO4wkI#=<18=*WP6<9tn1tGM}enx=_Y2{WvuvBTg;?T zh@YI^>b;Yam%6XIfvoK8ij-*S_gdc%`oLT1lgG#$V${fxVq{=$eo6CRc-%Ln{!$Pf zmk0_4PR1t`Y1n{GiU+ir&L6+f|4=*|U?x&Xci)vE@^d#2iYQ{HAtF9GCT3PIAo-KP zvkUhk(3}o!N4>+63G@dqmI|oJ*|X>Sq0#S#oQr#CS~WUm#}YkwmWcM+{dC_L$Z&gg zqwVu*`$(U|La`o=$QA~{t{6Nh(r=kD;RQntK?|NB#BKPn8~$rbmzicY$r}78Z3G8| zn=zR&A<%jffGVar5%5smzWs`3x>nfyDjkh4|qI%c2haFkYhH=|Fh!V$$D^`m zw2I2}w!;vekpmX5#b-zoT7lWlha1T2EdZ_)B~18P;UKy+B-vTI{GK$k9JY$O26N9e zn&0txLiPG4A(i7?$AQlu!_6O0Qwm&XwfpN`>=cx=v=n-+kFWZE0-81AFpB!lnkdBZ zI{{HK40pFlB6%Tm1O>%(iF^tKuLr5d3s#B!yI0Tnt${dcMgcF@5=m}-yQbWK3Ed$_&i4-w56_9? zGoR+DUm(5w3-tcH8uIEC8*+-&5}>NO8}0OFzb7>L6}?gT(h7r z_;8f;IT1e$m9~HM4=XpL{W(iu#v1zFJPk?&Mxf-{RLF)9?l zmAH=wtQ?#71Z&6kryJgm?z{ub)eUL4yR zXOMWEWAlm?m=ZZ=ohn@MstMZ2x>(&`E#NWM`Gs;GSmY0>9mF;7Z<|?I=ZO{ZicPTerDKc6aM4yRHur1Qh(xZ@>kuEsH+W-oP5S*1tw7x^iD8pZx>8 ze>9}*xEzsnUyRFNS@1|WNS{ol&EiI{muI-MjFJ*JvOYu@q}@XDYGxB)7oBgvcd%d` z%9jVDw)PSRss&f>Anm22#851mOFW(A>o$ zIf1RStTXAOcpXXXksdO9*=m{Az2b0nH4UK`_x4UvQNkVEuhuKAhJz?fGsmZ8pnYp!ImL6Z|7E z6G#nVw+c@->P*d91>fo&Tuen z&HYK_Q$#yy#u2I=x6-Pqpj`R`-C_~n(?Zq*59~WF3Yf!Yi8!F_hB5yq7?krUk(M!w z%}-;qs5m&t0Oy__xPpwh2vuqq!+Au}K+n3cs>1ROy(xZn=%3IPbABJJc%B!>+7Q0g z;)wiivGknlohQ=|whLz*3e^#NDu^*L)o{@R{amX{p`kd|m3STZ$^IU#-PnLVMAc$f zuV!nXDx#95yU`}vY!OLmG}3i=xA#A#non0~V}%rA-nzcN$T*mI2NZSgu5_>MDfnE- zHM(&(8=d4R6NcN)fpcbNKP0ltU5cn3QrnD0n4(w@jqb=tX?WXi7^4wo{AIT?$htW^lBwY0f7y;wLpM*Slq2va`B z==2pqIah;-oU5{cnd!X&(ffoZE)6EPETp`_s8hqhK1O>%3!ECV%B*2V1l7jdT1Czk z`GDvQ4;P2e--;d|&y#+7JN-@qO>9Z|&V`P+;7f&zz^B6lDRDjyE=SSBT2)(HUte8Q z-*3FT&SxH0&jS)DAEm1>dzBc3@`QkdF7TCeYMx5Q78IAWv?Th5hH-F?!Av*PQT_W* znG!0i<7fz=_Da$6x*jbw`&ZdN4rJ za@3i6Kci8J)@QFar}&qs?}%dylA~R{OrkYD2kWgoFH@jGH)%LY0?t&Xq==)|8ET98 z7~E>Y^)7N{7HAO}zTI$KS+fY~A{9DIaLGuRA5(ee)8(q#`DNUBZ#L)nnY;8WY2Nbzq#wi){&x(+zsklD=ltJDdJ;O` z^5f{g^yo|>zsl|ZJ18;HXK>f<1IZC=m3e*G2IdvBP&9ekweuR!`^rU0k1#}AKGitp ziLv$-sL#%VMV$SVS;r=-mEk~!LjCy%3>=)|+ek!O;@4uO^z`jN^0sz%P?Nw~s5%1! zMvY;bTcL*DWHd9MA3u_#qsOlHr-;*_`z`$;q49r$r-e;?jdS{@$?SbdYZneNl72>vEDx!(--6m@hEl)Q)sBIXtyxqV-K5AvVESt?M-Vp}6o zX5l=fD{>Yo5t%3ll7iPbd^h+d)B?Kc1irbw^%RX>*rmh}Z8L;MKu$9tRDl>)XZxZU zgE6qYC!`S2zBS+G7qNh`aV$3H+jZsZeS=dJw-hS-IL9<&qs{D)+YHoetiE4a0zA#E z&Diclg_f06{qBc>a&hBRKS>9X=opkfAFf096a#pnP2`P?NVjA1hH7p`)#Cn&VIhrD z(y#Dw9lEYiF?5<&2u@i*L&YH+VK6JKHy_6WFjS0c@zV( zV3m4IOk97M(0Jsdc6}2HU7F9?bZdk`PH(iDL2q6#Qu|WjIyY`uh_f6gXJS%8MFI6k zH6c@nXPT4etswwgJ-wRA6@HJ4{hb8?`*f*Z4MJk9b?x9+kUuF09>71oTKgK$l2M*e zv&}m;Vw*vw=flYO{aRkA=%na*wir~KpBC2TQRr6@7a2kCrB+gmnc@NL{~TBDXyxmG z`Do$D#0A8(h%jil-w}F+1g7Inuz@655TJPI!vLvXawBJ5UC;A<&+wZyM`=#(%bmL4uBxJCtukU5fv|{9~XX#vzm@C%10z|gy z5W>OOXB^mnxGa&``z)G54}e}1YCo=fncCD`XF6&-eG5W|2Hng;B2xLA(rY)Izq~&U zfYNwdtB*T-{}t8MCGDp%{jHChiM~Ki-cW?tw9zk1F4S);shS?e7^(c$|*V1Sv!!|{K>`{$OEOOI!ElqiC5K9mJ$q*ROCdsjX*~Gp~jJ} z7-7>7m8T_S&q}?j5|=}3rCish;jQY)BWpysx$r|}b$M=MbEcTWabWq&QdMspqM4AA z?)l`R*^*wsWgNSY^=A8zLDhTC2{%QpMpbuf-TJVu3QE<~)ls6{>t(dVzWe@HDp4no zm_@m*we8t#kf5-$sBQFWSjN{nz)n(R`dPGf=qAEd&OO=1kf_XH|QpzF?lurwoi$CG$cmebS(c2 zZr8>?WD{ z4_~1caC-_MYdO9&0pi4>Dh%)sBEIVSvybjGt{ zG_y}AGiI@9wMP4Bs+QSXI~4faGI{B({n13c;dgPpuI<^TH*sh5*ZY5vRnc1p+d~)GfOwf0AJ6PB$c?<| zcWr68i-J~psLbEjRa#nJ+J@N6zjk%t+f~83B(3;w;73_np?}0@);3m@bT@_)+oe5Y z-*?54A;tIX9{)UzOyzdsPBte{8(G($CqhX)Pv)VdF>q#H z#(IT$So3PD+BTW`GNcrHLh@vGZ8?yaA`W1`!8u&C9^O56KOMghV>tm|(1lks@iY_A zI`iPV+^G#M?2qf`Y`wvs~!6DZs2sbbx4S&`kXb)@LfUb&b(uMdIK#?kc)N@o! zt?5vQAS|S?;~}aEoEfNZDyk)`^2%0vSf<+gd>WLj?R*&GH$?o>zAS=2b=4I%X+3}V z5z=#7vgekAdA!{4&;hPzZ z*Il+L6;7l{Qq$GZ6!#DxPe&12kpgC#nv>XIR*g0)BLz(I&@Y$u80xg1)tDxDT#hu( z_qW|c@e)(>|s(R7>gkuf@6Tl-?+Vy#?wwv!I2XMyq)_s-Y# z59Ql*V|lZ!Aumc39d6lbJQ86&Ps)VV4V%+dz zsrH@cohTRw>Dw&l#|Y+7A#ez4IW0OW8UdlrA6sSY()?freZIyO~nhx<5ZE_PLGo{Mi? zI8RVMl}fhKN^RdUu}>=$`1BGJnh$UF*}Qf%AW$HCegHxo)QXWh0r9jj&hSStlnpU` z7EH>~BNpuGjFrx%Rc4*?3S$N>18w;kh(x|#K3nG92lsNhxgp0Lqm`o^d0%9WBs+xChu_C%Mrm?8U)%9!_GWJ-K9Wl_Z|=ndxraaCz{S%Zm*oI zASq`(aF?EB4E|JB-xSAYuN=|d*)=kniH(7V`wx#=814q{q$x;SogOZrhsdqfHeUzi z`^R(WYv)H?-7;*iz(v=$|MYeiL2)nv79QMTaCc7#?j9_-ySrO(XK)Da?(XiA!QEYh zyF-v*%YWS3`}VP`tE;QJ`ttg{@6j*w2@dHnIA!y)h%9O<&CRS9Tl{HoF)D)&B6H-A z|KSqM6dTeB7aHn~#Qe+g1_AyjE}gd*PtNQe=H}Vh7lOr+ffF{0BJQcwe;%XU@LwEn zuAO#F6iM$PEo21v`HLa*7u0B|0FVEa$ri|no3+19sBv*|!TZ8U+vy&Y;GmOJeDX;W zqr~B2lSht(9{ek5y-)dU*kmd;0-$%uGLi5ux?9{8x9mgi&=4Z%F=nid`o@+AL)BBP9;N%z1Xlh z4C#H7U-4sR#p|_P32Lwl8l}XFT)`_=wIWv!6Qc<^lWJG~3XJ^Dft(jC-5ff(*GSIW zvBdyQ<~3(XrG#6)lCRPD^*|bH!2sYGL;)MEZV{-`U_u$PYg~_45Cz5=F`cPkhfR-M zfYi`3M@pT>MSvh3u0NSQ0Z@c(Uz>{5D3_ZeQ32WYYnEh+Fy<^lo!B#=%6(t>NLp+p zvY_vkoC)D#6B_iY3?#YJL&;xIMsSWpG$}&6al5sGw{HoE@oMy}F#r+dbTD-|x-$J7 z7>M8Tca|1S^Wygr!iVIpdbY73TZe#MxIKZ;&I8tk@H4e0SR-t_7zlJL#A{4IT^jiF zsl!HcQz$Suuh;aBsE{yo-~}Xulx}GTa-I)>AKO@1#KmhEsS_ayk(|Uk$0f~!ngg>Vs%2$mMMYP4cUWy5NpMK-vj8s_H{9!-znnLO zElO4A)cBJhh>*4~RMc%YGsdrr(BejZeEgmLXla*W6+ayo6~RSyveCUi4#DNbEg>dV zayV<4Y#&0r@A<`c5!dke-r@T+NBy(!CbjQ%qc$HeFIfQ+eD02cjKN=oK2=8qMDDPL z@^S=;jsAX$f|cG8rc9vyel=%>!4R z)?{D~u2~_KdXV7~AR?Epf)~BINupeYZ<)y@LPbYsCy_QF=U^ENjnG|cM*3~1Jq$^p z4{m1rp!m2fIA3JDFG7lqXtH5U>OE{Z%PXJH&#FcrH#z3BVYp&Hdj@}R&btnOwhlE) zg})&MY&q5m0si6!XBG~kLm;B8umq;yPjM?kqEx_Zv%_CP&7LAaP61O9aV`ctp$_o{ z2g7&_Wgm7F0UoevVOtxhna+7}Kf(NtDcfnhoc7ZX5vUhsR+3$2&(K`Dvx-gB)nfw< z_}<*(Ic@XZEG8=Qd=mKd?1ncD*JFDyUphTLSpYp}|Wg^R>`u+T`6 zpS>P?SvEp$-z)!3n@og#N{W@eLnccogj4S&bAe+O83hhY4pQGf39FWuHs_Wc@TIh2 zw577h&WT@a2(WE)+Y^Imr)2MG9Os4*uaR*li3UdQizm|E5C-R9`_7fM=DzP^Y-@kbOAlcD+?R>vnw{cqIgcZ%EvY2f$yL0ZeD zuJUo*Y550ifP~MO++7N_w8G_@&O#-2C+s(EUWbxQzoQ4yi#WLRwiCtQ%ab5h;&wMI zyvzxxWn5EnB>^t}pVl6dvSNywrgM|CeYZG!6IMB&42ajkhY9^`aSRMTp@oON)=bV~ z^^6nd=sY;M7a|68FQ+o$#Fh_J2{SlN)vpzI{sNBomOoQ<#M)|swWIPM&m*H*5+;(i zX;}5xM&ja5p;8;VI|=BNQiOJ9G&qjYV?7l^f~|RE3h1ekX&E}}ovxOWrg*{e zCQ6|hp>C940R9{;QZj#nZ%r?^?S^ePYu=b+rwN9;TB1eTd`R&8G8l?f{=i(iI>w_h zu2RGUgON4M0yZJeF9$>fEy=!TB9kBX3QWLDOk(xn!awP@j9ShRl64W0V<3{T#(uBm zO8+*+ph!hLF)Z=ZQbZ;k>8If{GmB*Zp z8C7)=cYbV?t$#lYo!)37igG^gGq~ZbNFsO2<{E4BpNLiy7Hjot`Cnvwwn$Lrv907k zwax1(Z77!JKeg@uEHsSL#*3nnnyOttIpcL(>u)lJ$2x9>BNf?-Y>XP0nL|H$#>U>g z_k~OAL%)Y@FMwr^_z?`r90O=Qi^6Ang~UH>=SRs7z_2Dp0|%phI6d5|vC`K-!WiyA z!Px?w5D*a|{m*yqrqzr{2o&!K)*f=RCpE;sfVE*BoDL2Sz#@QlrxHej%9}69Oe*_} zMSv*cDqlN0JLVYRH*;iWv|r>PYpB>w2^|3_4=lhM5I%1MP$o109{_-6|92LirZcx) zsE7|*f2VPV@CFp;J3#1%)gXag8O8kxN|9!e8JRO+Ux|r|?+hpip^E`QXG=>#u{BN-}cJuvFNFf>B zybuDs_{0x*;vi%IV_dT_BS4gFOmPB|>f)O)i2%_-4#cFt|D~VD(2}N!^!MaSZ9~^c z0Obr0W-rVTD%C@!h&3H!hE187eKATCl7^#tsr;!n^APar4MJX8-IKNTFWP{x-NNlsX-BjK?f>vPfJfFs+JU39^NU2pb<8IJldQH<_ET|JwvRs#omCMJ0S<#Sfw(9!~O zCZ@eq;+Kqj-KvZIMv(ibVVz9#c6l2> zkziTc@unHfFRw0^DD9UVsFmC9+{AnQ*!gELFOQXE?a|XqA*$x7Ibd0OZCn5Bxm;Z#50T6P-W8~^hA`8O9To_$1p&)wAr zXJbL(>}_;s2JK{$-PwfRC@XwQoNz2dGF?-8OsDJNpm!5NFu!S|?pa>>VD2hG zHH>$L=zcX;wuD$Z=1tgKe5=Uskx`(JCHk5q*Y#XqjQI@_20S7HAcf~zRL;)b z9PRWToiqY&#P?rVSJgPoH*>M4h15^;v&$GH+?<4xeg;;1&6SDv#wu(q6I)%m+&MK| zPdYPB52y^~IZhpa+PYZI73~Wb*c|O(eBei3{iY8%zu+`p;w)ZcvmNqrknvJaN9beM zuGxq7+D=*U~=UcH(n4gg?KaNz8**CRG+!RkGVD)kq%yjmOsb9>hi_yh>%m99)-v(`-IO^Ka)Hq4eAdiWTs0 z{ox=b&IA$H-!?h+@TmLmsWMjZ;at|8p|S}N^a$3CzeyFyyUQUg_T-pf=lFCS4avj*}yoF15X44 zsOln|vM>jn?d3UfvR@Bex+@KUXb;YHwT(LK)}0{%JD7cMCdMM&)Hyfg4TodD)S&;%~-R*LdaZ zi|is7KLxD`GzW4XlMNXImd42Kzu56sI_My^;eE0;PlA zURTw_2e;98dqsiSAX1ETYc9+CB+*0w8nJEK_(20GK|2YMdP|N*mw2=cgL2$F2D{Hou4P|?T(@c z+=i;CsVcTr-)5sN5j&h?OZt9N@PK2>t=3eS{P!>@dd960`5g)K)>>ti0Orjde~CmT zi#kU$^dr?Pd0Fb$Bn^S+9|yGv^&;cP^&a}#l9wA zUqqUKdG5nQA@z+mXV@wQ8OyeNP!XuiO<;%BM1%D4Km1t#K>p_#Nr+JK> z-|9lQr@2Bl?7IN4un?*YY2d{x(>8q_$o^jAAKSVWEHr7B5Zg3fDOhr~Wj4Ud77sd< z1|i%rdM(LZ3~T|#U~lJ(yWGF7(|3Ayp6u}gzfz`Dd8w)y)CKJrU+|qf$CzG%OeE0O z`hwFa5G+h839&s}ml)6pZ8po{QlYoO&s`U9qQbyu1XpIy_XYbWUq+_43)K{M2(0I7 zv{&V%ZOa@lAJML-@U_l@H7z}18_8p$ct;mb;#}}w9PYcGjaibkhS2@q< zz5xUU`0vfUEtRQJ%qd5y1l)}JIjr!(A}7u2DUvi43j)$93n5|Y`@UX1kt+pDcczft zx`Qd6@T;6^wS7~m9q^%*O2m6?R|H&W>Ynh_SD$H&z>3p&Ba$?aDZDEQ-UrmN&@?{a zuVKr;>sv^OX=~0i6bVj{@TwgR8v-19xW5g}g$M+xy6;L=+Rkw>g>JZiI0di=c6&Js zw~Co%hZ;HzPM)Zi8{Km0i?S3Q{8vBpuNHbfKldJLkrKk!(P0Ts9J3Qv|wEG2A( zDvu)U(W@KPo+n>|4ts8$4l70%xCxK}ShD9iA|WB|T~Dcs8Mr|9 zV221Z&UA<>COYGz3geQCkW5NB=Kso9pM&HQa;$R3LF!}`1_V}`tkl($y;B8;mojrY z5(NuTI}(ar8KDDD-~-`$YwKKJh@rP__%CXLP7T?y5kuGvC$1-b!rY~J;hi2R7j%); zoG%H}eDJK7%bTfs6A^_wR-(15{@LhxfOQW9y9%+6#B;_mKTQIuk#giwb(l#Ac7_8R4W_-(FV<}`U3 zVGUbP@U$^aW4vw?j}j%K1RW4B8MZOHRP8ipK~A%Xluq)~7mKYP)HPIW3+9VU4b4HTjVZC{rK!=*8KElKiLM?<5R_^jC?L{OOT#c;GScEL_}eKkj`{ zF>K=HsUqMBZpA!Jo##!CP;`RQFjKje*!E6Rn}Z2CjrQX@UxaFR%Ie(icTT8t!s?KI z-RqKKh2MIwQ}vkGQ`nZj%yB?&E3QlDA9g+EIF;DWj4P6;djaw(k?ozE z{tv%&y1jq@yD6d?FrZ%c9HV0JHb@rvcf4lip|kW}B{-J%H;vVIun7Dsi@(#|wt$pW zo>0SLJ59Nk3o(j*Ms#hu*5C-`g@HLlg|6eD?PV+DZ`bDyDxsYAYttMF0>6)LXB4$T z{J+wj_)@wJrYA*YY!Q>%o*&LKChT^TUt&zx&OOnpyFC4(bbff_(w%EELOcr9o2x8t`3*9ACD6dva(sfSxmK?{FI!npRk6qSPRg_<2V{WVRW%FjQ^o+9ZV$ zY*h%(A>k42*qS*~J{pb`&8q{^xDw#0f4W0(LcJxo)>igf?p=d1e+BDoCPXa+h5ULd z!b2ZOViQ5Os2F#k1jM;yO-Z9z|Fl!fg&5oACrnR}x-wE)g)_V5cSVY@A zEVTM!snYr4Ax=g0r`tunsI@XUGH_|lkhwRzYt#_ZwQ&*BNV2?U>@~>j5W6yUFycZx zeAvE~ut{6kAI@#|%tf8hiu3qe@WP*IfuBQtYDgGeRL1DK72xs_7pb$XuQarYI}!z8 z#j1)QDS|+`T2>{B_uY=b#6wDc~=QD_HF5zb|-D_;E*3@+E5qT_2Inw{S2Wq(Mx zyQ5ECr1%f%Hy;6R_3DTV-1EUkyh#Qt7hUGH#VjS&VJ9T`zmL}`h}(?Af2I?et4BEEJJy5k|g5 zaqkKdL@LaqC`CP>SilC}Q9Q_C$6mEs8EHTn{-JO_lZusogbWc1jvRI4xAp{C>%{g5 zTxARelyWB0+rWWNP%3W_D}b`Yj4 zydbz%9+U3~PI9R>$)$&%tDX&47qeFZ7t6IfmV5p;dLcM%u7$@7Mq)Ll^v?kDp@6XC z{U2)x++K~+ik@77tlzxNPg7`$fp^RAsYf(!=3(r(xE#hsW{4d|bj)jrLC)~LHPJ11 ze_>Mp1-0*ynr8G;b$*il=4Z*34Ut){?|fg&QI$TGjVomW8NAGy+a(!#`0OC)l{d`5gUa7kv9%3 zs}GfO5(Fnu^t!G*|a z3*5q%=SM^65XX?T`e;udpg$1A4|}h@LAW3_VM=)Ys9jWgr7SrR<}CFNj8SXJqIItm zCj|usH}{Ga8CPg2+g&vPM-HD6U_E-A5*4;g)7*wP><< zhhISJv|njQA-%i~EJ2o2Lvd8#u30vO#WshXupmGNif@YW+ls4It7{8|KoQX9jU@1T ze%H4Wtqqv$^~%iDQ8mZGRdH+Oa{Hp@6f$NhjxM9nx&bfGsct9k>8bCTxP%H)k%xt( zQczuA&d^d_EpDu@`ZuNj4W`$JA+)6Aw&}H}9Zj&~@Urv${g|kxrn026RKre$#oIxH z7As7hVG%-v zw)Ihj(%ZpS+0o?`v9~Z-ZFfNbG=cmqSW*MbR#a81{jJLLaHuGabxb~4KXWN)Q(U?c zrLD+VT;xFM0}J~aoyNjs>$~U;fhV* zCR+#Bb|Rc6QCVR>-m)Ow-rm`hzOSgpp+_DuJ66!t1Zz_!#P=d;t|n6{h3B_iJ?~O% zMcAmOBD$jEx28J!E0#0$lEK!Rp5CFJ($}?GS|lNETg82Md#gZD%|fj>ixg8bd#Wss zFxpi-#I8+4H!hghfBh(G8j5u>*=*x>@dI^S z*X`Km&1^?^v)@g``9or^-@6GLHV}6}pkwE45`QeMh)(b3^24{apDY43@ubD~?{9&F zmZvAOEtr)a4Q{lwebGDsk*fs1;Pj^sFayOBkHiPgb^2Q%28|#;6Wrb1B0u$4A;QQE z$2Fq8MG$iu#fDGcxaV!^c_`z2!oD~Aaf8Zo>ixzrWcpr*G~-MyLnY)XSiD^eG13c? z=7|$q?N-EmiM!##uB>B~Ht`=`Xr#Q8H&7@Z)rbzY6o1Ve?@%s*qqR=yL)Zk0}}pi1^GWSbWdMQrSDqcZhXuKEEIKK zRle*-;J!DOhi0~cJNKs6b62}txy>dES?mF<6XCh!b>NxCDD1=hzu2H21agTe*vgQi z>2J_56Z7)z-y|bgxHNE7UG^}pgmHb{>?6kt>h}`LR<6kmaH}d$9iu7S=FXu>oqxI! zqvPRU*^hp;eT#oX!~>Qf^3t^)D{?_n0P0ZRxU5W}C+*+Rw&sRims(T&@XLq#!5zA&7SiB;_`G;wO3SJ2y+hoRcM9WtU&x7x00 zVqSI7V|1MiN>)@m&r@1sIw9CGkce%XCQAf_jGID{FU(Vs-1MObSyr&V_t}*jyL33@#PYc@dTEsoi)A74)@~ zDzEd6UF*cJgB2@IVg%y%gF~dd7alCU#tG4{Lxu!D_4vC@8FFUkb-;bP{It|!?Ryp7 z>#>iHy0l`?oH%O#6w(DOl<6w2Do%G9Gxq=JYAKiZ7X)s98T^B^TtXtGS8m9pUC*6_ zWN5>V(HzzqyRhWU&?>)AOhgFH3Jx(z9-@J6Yxj_kMzUp`8m+N~mU5YI7_7Hv_-ah~ z^Cb8Q7;e*dhZ2bf%=lZxPKFnF%+A$x4MoEcj(wOtRe3!3gGw$oAs^s*fO|aDpvfGc zNh%p~cNO%>hi1G!8X7cfCm>-r*aR)`x7+d#(zEc91x@IdYH}Tv$z^OrEu?y zF(Ac#7qOFNGri25XGjm3T9h|UV17Xvl>^lY%$FSKqf6FCvNkT4Mf{$ZWSuQc6(~sp zlozdB#LS}|LWiHD0V0wC_@s6)$e~|B>+)jdioMCDal-+l)~p2gpizlC$^mF{a2=$J zeI7saX|r{;(jf&0p$j0o|MQOzSOHD(*D!7n9>fP7MvLu)jra-S*fHb`(Pm1PVt}Qb z+PURn2xo=}*bIRr;DWPej!zOBP@!LBAR%Qpb7`DLLxZEbJUM~DmNPY_0+1jlZpc2& z76TknH{#S)RYCDn@LP9LX-1a*@W8Ny^qdHJpdM|OoY1f6J1DAvGZo&204QBVpc6W=%v<`+<@7V7?n;kIY~k~Dr7Jq``z1CoMvB)73*e1TH?g_t`(z9^*6udJhKC5! z%IZ4(SntGG;jUogXX|9^+!N>)+3n{EpG*8p8RLAi>gRCh zm6hp5jm2U2QZ$@w<<)rsC57*YqBF7082|XSnf-89d<3f56 z#X4D8o`kL>1(_Q}0hBPUX$W(Y=3agm(E!?ZK7ced54NtC?2%)D9gOf@9yFcDA)?2n zKk$8LPk{7d!?kBOzZ#pK7u63LBD9uO8H~oD5x1!!g@`+;JHw-02udhd*vg49eU9|? zSc60pyOeFtdH3#qpB?5L6<-OCPyI9ncJ0}GZ3~6scq89XY-o6WFxHXro%BZOPr2W4 zRkOi4@m=INvgYO25--LUe|T7$O;F>4UYllzb>jNgxXkHtfQ{R+Mj+1-ZIQzGM^&GB zWzHx6=SoMNrJT`Ib|EQCN8GfOy_W~utom?Cs;r9f{#dYPy$b23(Iq|e*E*%pHv|t= zyM(ESh3~Pu0zn)oK}7W2FR<%VxxWWW)N15yYI@QdZ0%UK-rN%Tm~{AE%FLdJ*5@Gm zF?l2Xx^AwT13EctQDczNlX{xl#Vy8-*WY~&$%^~XxmQ`MFm)Gk-;%Lqyicb(t5L58 zF-vLKu4*QDoS(QG4F^%0I*rKc?uQ7SWO+ps5PV?gHQA*R#TO1aV9et!gl<~OBkeK= zc;D@2g@_2jwK7dF;-{?b=W%Cwpu z(@&DtE$bF5!c9uSPCn4m&fx7{3Ovx-4YPxyKhfglv}9r^XZUTkC^CKw7Q>U#QtEl- z70p{0|I^|(7|N%PtDOiglAa!uYK+tHw=-I@h_J^qz?y;gpbEg8za1y>Jnp&jx$C|1 z-IFn+$2zlJb*|GM(bqJj=_@Ge|Mqo!d86Y?u9e{1G8$t-%*I&7ZAEeU#qieK5%tI4 z_R*UBA;x;!ZmU<_T0WjmLx{m={_o)xs85=7ck)0*8$PP-0(p#0znn)esQ5`-$KBER zt&w;f16u*E##wj|!-gHQ{wOxV$ASHUfg&Mzov&@~V{DwPD?&J6#{hgnucMiGmF&Xr zeOZZ#=U3M6>z%Zx|8#+7skTL|cXO`fOz|1H4ucW`f#29nb`l`1yM7*h^`4d|NJ&0K z2TDT^;?CjN(-nU{pY1ca$}k^(4@_-B#wKnp4;Fj-k!Y_7TEvMpK?PZ{fK8IEE7ap} zqwLRr>X@=~u4AKhPjg-Nu}c)W?V3+~pqf)95}k2zn8u1cKSmV&RcdK#%bq%rE3sUn z75=fvczrn}xWg*41&k7=KiLt;=Wf;xV6D`b|wN{|0PMVonnZmnRyDp<5>OfZdoVWq}|4mp@h6RlER(8qOyx@(dR`fDEZ|(z2CgP+f15* zHhYFGD*V?ck#^mW{1=pGR7BObm1i{-cgfFPnoiEnf~r(c%FFYo6@R^7p-Ij5Wto^L zszw%+DV(H8VJZDNtK9fusP(+ExY+UWf=mH5WAr5XQSUuSi1bZ%178RsL*NIaCy?G9 z=JOnAp25(j*&h>&+BpDC48!R0;>aei;P38|ogz2auvYUvp$Hfh>SMXj1w4nR&fD=U=J=0BC=$5260;j67v z(Gf_|;+uSohVAJT!EiH`IYteEM4WlZ zTZaEyjMl|as|4A_@^vjl3Da2O_AH(Xwv(j~O6)8vnh16m|E!k96Z9dAvd zOqo6nDHi=txv(fA15TC}n@Z1R-Sv=OsQRxR?QpNtDAa3Fh6V*INPap(Okm|EZHpi z>m^CHPlQK|9zJ$*qU;NH56YWGu`~65n}Ko&z8EBGnCI6?8QobwKoI&T=>0meOy(ur z#+1r=(%~y;^mL&~5c)gqk3z%b?>OFZ7HR+Q679EvYq#jW0PVw@*ZKqTdd})9_f!l= z*81J)*J**yw-b#I7HX}@x$B(Nob~tp2O**g+-OeXKX3oo6y8nOYYAzzg5pDOoxLC* z*bA-M;`Ew{A^A~Q1BP4FkG*-Jm~v7wyzBLbT}7V9zP0nFrn<*GJ20C3Ha_(8S<$?j z_h;B$SmjS$SG^gQh{vZ++Yi7G!spxj4(tG)VaTNy7cbRK@1bcUYS^thH8SfO{2}Q$ z$`8BqiH1Y-Uzzv1pC(1F`O{{U*(YYmbJ&e@z%Cy-ywd`UwBII_j;Jq?8>0Uc~ zvFhu{4h^GHaHfsF9)$`82JzOx_8NIHtoJn6=*{Iwn@H-UY9`2 zJvl?58m-E&L4qleydC)T(=E=$ngHKvptWeiPYxQKxEcR#2s-ypW;!qlK5pq`Csxr< z+=!i4U?;b3wHuk)GqTJ$Rp@AXF6*hCUHR>%J1`ZNPGz6jR0#QXG+W#kMot0fY-e5h zO(2X`)rfcG7vYfHP^|NPbEmjBPii`Jvry66DRgaFRC z0w9){ltQK~_wp@r{L>B@#Rs=+xAImuGc$L^|*sxu8Co&cjj5;H0pCV#ia$06sSTIn$d~D z>*)4)mirs(tq%VUhhu?A~!rwq{rL{zvGUj9u#e-{oYxLfZ()R{5R$@F~4~xrITjX zSLn3!@bLGWR>OY94i8FDkB&a9PO-3n4Wt4Tm1C7LPYY-uOLT749+_uI0gi+OoS4kP=7D93UPM0%FW*oHW@U)o z@hZEQ#7F>O&@zPf@<5EAgBEcV@CCnbq0 z;p4|$EASm3^QbW(mX#w8xvm%r<-xvuM1wg1?4JjM5flyjv&f;v`9SQ=Fx8vrxYd%5 z)$;N78w-*7`Z87s9AcV{l!pOwKn`aD$I=O7?lgA*hr{|X0%UaeZPkIviCWKun2>Qs zMmZtG!8PT(KWxloMS!bt?h8a;YvJs z?s}L0h#AYp22Mq00{HxF1N)mOt&rJQT)YHUOt#nCtW#hNB`M2;@Ca6E+SQeqQ;+V_ zvWF(Ix_;vQh7UCJaB*?buk$+(3Lb?tc(}h8PGYA*h`T_n?xsZct}i%t;WhF|PfyqA z2}KFTf_DG7x4XLf!(y_7C7byRiexB!^vC7(n{nj%Q_Q);pjuGsPDN$qAD_EPXvkRG z9dt2fJR(w33%o<=rG-9B*mRTDbrlsAh*J$~dvI71DqPsW+2Z-E8zWt?dCWojo|Y7b z+-YS^AxZnHq>2~GL&A{2#M2X~@}+ z!!zJHN-}%r>KY&nHe@DyW;4j8C3w_T0>nW{o}3>apPmNVp}pP=lQPlMmseB-VkJvL zPcEtmzfA}sf?9^|d@L-E;wgW0=`(>~{`C0ry+U8k78HEy#7$q_Tbk47J>O?ZqCNDL zwpD)N`Gg2d%q(^A@{ya+lrM{Z1_4cmQ9*F$lL07Hq2yY61Cid({2V89*&1k&C~hRm r+YRvj;SMVAP+A$^w?UP{tEx|E*S;4Gp4=mNfX^W%E-zLiVi@>81{K_1 diff --git a/docs/en-US/images/dvswitch-config.png b/docs/en-US/images/dvswitch-config.png new file mode 100644 index 0000000000000000000000000000000000000000..edce6e8b90e3e82fa19c4233337a7944b64a4150 GIT binary patch literal 41955 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 B26g}d literal 0 HcmV?d00001 diff --git a/docs/en-US/images/dvswitchconfig.png b/docs/en-US/images/dvswitchconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..55b1ef7daf38aeb023fb22b42aa5060d2e8a9a95 GIT binary patch literal 38642 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 O00K`}KbLh*2~7Z_Kb=+p literal 0 HcmV?d00001 diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml new file mode 100644 index 00000000000..dfbaca11627 --- /dev/null +++ b/docs/en-US/vmware-cluster-config-dvswitch.xml @@ -0,0 +1,193 @@ + + +%BOOK_ENTITIES; +]> + + +
+ Configuring a vSphere Cluster with VMware Distributed Virtual Switch + &PRODUCT;supports VMware vNetwork Distributed Switch (VDS) for virtual network configuration + in a VMware vSphere environment. This section helps you configure VMware VDS in a &PRODUCT; + deployment. Each vCenter server instance can support up to 128 VDS instances and each VDS + instance can manage up to 500 VMware hosts. +
+ About VMware Distributed Virtual Switch + VMware VDS is an aggregation of host-level virtual switches on a VMware vCenter server. + VDS abstracts the configuration of individual virtual switches that span across a large number + of hosts, and enables centralized provisioning, administration, and monitoring for your entire + datacenter from a centralized interface. In effect, a VDS acts as a single virtual switch at + the datacenter level and manages networking for a number of hosts in a datacenter from a + centralized VMware vCenter server. Each VDS maintains network runtime state for VMs as they + move across multiple hosts, enabling inline monitoring and centralized firewall services. A + VDS can be deployed with or without Virtual Standard Switch and a Nexus 1000V virtual + switch. +
+
+ Prerequisites and Guidelines + + + Do not attempt to configure VDS by altering VMware traffic label when configuring + physical networks. This will only work for Standard Virtual Switch and should not be + distributed. + + + VMware VDS does not support multiple VDS per traffic type. If a user has many VDS + switches, only one can be used for Guest traffic and another one for Public + traffic. + + + Management and Storage network does not support VDS. Therefore, use Standard Switch + for these networks. + + +
+
+ Enabling Virtual Distributed Switch in &PRODUCT; + To make a &PRODUCT; deployment VDS enabled, set the vmware.use.dvswitch parameter to true + by using the Global Settings page in the &PRODUCT; UI and restart the Management Server. + Unless you enable the vmware.use.dvswitch parameter, you cannot see any UI options specific to + VDS, and &PRODUCT; ignores the VDS-specific parameters given in the AddClusterCmd API call. + Additionally, &PRODUCT; uses VDS for virtual network infrastructure if the value of + vmware.use.dvswitch parameter is true and the value of vmware.use.nexus.dvswitch parameter is + false. + &PRODUCT; supports configuring virtual networks in a deployment with a mix of Virtual + Distributed Switch, Standard Virtual Switch and Nexus 1000v Virtual Switch. +
+
+ Configuring Distributed Virtual Switch in &PRODUCT; + You can configure VDS by adding the necessary resources while a zone is created. + + + + + + dvSwitchConfig.png: Configuring dvSwitch + + + Alternatively, you can create an additional cluster with VDS enabled in the existing zone. + Use the Add Cluster option. For information as given in . + In both these cases, you must specify the following parameters to configure VDS: + + + + + + + Parameters + Description + + + + + Cluster Name + Enter the name of the cluster you created in vCenter. For example, + "cloud.cluster". + + + vCenter Host + Enter the host name or the IP address of the vCenter host where you have + deployed the Nexus virtual switch. + + + vCenter User name + Enter the username that &PRODUCT; should use to connect to vCenter. This + user must have all administrative privileges. + + + vCenter Password + Enter the password for the user named above. + + + vCenter Datacenter + Enter the vCenter datacenter that the cluster is in. For example, + "cloud.dc.VM". + + + Override Public Traffic + Enable this option to override the zone-wide public traffic for the cluster + you are creating. + + + Public Traffic vSwitch Type + This option is displayed only if you enable the Override Public Traffic + option. Select VMware vNetwork Distributed Virtual Switch. + If the vmware.use.dvswitch global parameter is true, the default option will be + VMware vNetwork Distributed Virtual Switch. + + + Public Traffic vSwitch Name + Specify a name to identify the switch. + + + Override Guest Traffic + Enable the option to override the zone-wide guest traffic for the cluster + you are creating. + + + Guest Traffic vSwitch Type + This option is displayed only if you enable the Override Guest Traffic + option. Select VMware vNetwork Distributed Virtual Switch. + If the vmware.use.dvswitch global parameter is true, the default option will be + VMware vNetwork Distributed Virtual Switch. + + + Guest Traffic vSwitch Name + Specify a name to identify the switch. + + + + +
+
+ Removing Nexus Virtual Switch + + + In the vCenter datacenter that is served by the VMware dvSwitch, ensure that you + delete all the hosts in the corresponding cluster. + + + Log in with Admin permissions to the &PRODUCT; administrator UI. + + + In the left navigation bar, select Infrastructure. + + + In the Infrastructure page, click View all under Clusters. + + + Select the cluster where you want to remove the virtual switch. + + + In the VMware dvSwitch tab, click the name of the virtual switch. + + + In the Details page, click Delete VMware dvSwitch icon. + + + + + DeleteButton.png: button to delete dvSwitch + + + + Click Yes in the confirmation dialog box. + + +
+
diff --git a/docs/en-US/vmware-install.xml b/docs/en-US/vmware-install.xml index 467e1358638..fd88fc7c0cb 100644 --- a/docs/en-US/vmware-install.xml +++ b/docs/en-US/vmware-install.xml @@ -327,282 +327,439 @@ esxcfg-firewall -o 59000-60000,tcp,out,vncextras guide.
- + + Log in with Admin permissions to the &PRODUCT; administrator UI. + + + In the left navigation bar, select Infrastructure. + + + In the Infrastructure page, click View all under Clusters. + + + Select the cluster where you want to remove the virtual switch. + + + In the dvSwitch tab, click the name of the virtual switch. + + + In the Details page, click Delete Nexus dvSwitch icon. + + + + + DeleteButton.png: button to delete dvSwitch + + + + Click Yes in the confirmation dialog box. + + +
+
+
Storage Preparation for vSphere (iSCSI only) Use of iSCSI requires preparatory work in vCenter. You must add an iSCSI target and create From 38a8db9046eb680f368335634be838499d7b2aa5 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 9 Apr 2013 13:32:08 +0530 Subject: [PATCH 70/81] CLOUDSTACK-809 --- docs/en-US/added-API-commands-4.2.xml | 38 ++++++++ docs/en-US/multiple-ip-nic.xml | 134 ++++++++++++++++++++++++++ docs/en-US/networks.xml | 1 + docs/en-US/whats-new.xml | 6 +- 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 docs/en-US/added-API-commands-4.2.xml create mode 100644 docs/en-US/multiple-ip-nic.xml diff --git a/docs/en-US/added-API-commands-4.2.xml b/docs/en-US/added-API-commands-4.2.xml new file mode 100644 index 00000000000..3b765f259c9 --- /dev/null +++ b/docs/en-US/added-API-commands-4.2.xml @@ -0,0 +1,38 @@ + + +%BOOK_ENTITIES; +]> + +
+ Added API Commands in 4.2 + + + addIpToNic (Adds an IP address to the NIC from the guest subnet. The request parameters + are: nicid, ipaddress) + + + removeIpFromNic (Removes the reserved IP for the NIC. The request parameters is: + id) + + + listNics (Lists the NIC details of the user VM; the API response also contains the + Secondary IP addresses of the NIC. The request parameters are: nicid, + virtualmachineid) + + +
diff --git a/docs/en-US/multiple-ip-nic.xml b/docs/en-US/multiple-ip-nic.xml new file mode 100644 index 00000000000..fb9a331141c --- /dev/null +++ b/docs/en-US/multiple-ip-nic.xml @@ -0,0 +1,134 @@ + + +%BOOK_ENTITIES; +]> + + +
+ Configuring Multiple IP Addresses on a Single NIC + &PRODUCT; now provides you the ability to associate multiple private IP addresses per NIC. + This feature is supported on all the network configurations—Basic, Advanced, and VPC. + Security Groups, Static NAT and Port forwarding services are supported on these additional IPs. + In addition to the primary IP, you can assign additional IPs to the guest VM NIC. Up to 256 IP + addresses are allowed per NIC. + As always, you can specify an IP from the guest subnet; if not specified, an IP is + automatically picked up from the guest subnet. You can view the IPs and associated NICs + for each VMs on the UI. You can apply NAT on these additional guest IPs by using firewall + configuration in the &PRODUCT; UI. You must specify the NIC to which the IP should be + associated. + This feature is supported on XenServer, KVM, and VMware hypervisors. + Some of the use cases are described below: + + + Building network appliances: Network appliances, such as firewalls and load + balancers, generally work best when they have access to multiple IP addresses on the + network interface. + + + Moving private IP addresses between interfaces or instances. Applications that are + bound to specific IP addresses can be moved between instances. + + + Hosting multiple SSL Websites on a single instance. You can install multiple SSL + certificates on a single instance, each associated with a distinct IP address. + + +
+ Assigning Additional IPs to a VM + + + Log in to the &PRODUCT; UI. + + + In the left navigation bar, click Instances. + + + Click the name of the instance you want to work with. + + + In the Details tab, click NICs. + + + Click View All. + + + Click Acquire New IP, and click Yes in the confirmation dialog. + You are prompted for confirmation because, typically, IP addresses are a limited + resource. Within a few moments, the new IP address should appear with the state Allocated. + You can now use the IP address in Port Forwarding or StaticNAT rules. + + +
+
+ API Changes + The following APIs have been added: + + + + + Parameter Name + Description + Request Parameter + Response Parameter + + + + + addIpToNic + Adds an IP address to the NIC from the guest subnet. + + nicid + (optional) ipaddress + + nicid + ipaddress + networkid + + + removeIpFromNic + Removes the reserved IP for the NIC. + + id + + true + false + + + listNics + Lists the NIC details of the user VM; the API response also contains + the Secondary IP addresses of the NIC. + + virtualmachineid + (optional) nicid + + Lists the NIC details including secondary IP address + + + + +
+
+ Port Forwarding and StaticNAT Services Changes + Because multiple IPs can be associated per NIC, you are allowed to select a desired IP for + the Port Forwarding and StaticNAT services. The default is the primary IP. To enable this + functionality, an extra optional parameter 'vmguestip' is added to the Port forwarding and + StaticNAT APIs (enableStaticNat, createIpForwardingRule) to indicate on what IP address NAT + need to be configured. If vmguestip is passed, NAT is configured on the specified private IP + of the VM. if not passed, NAT is configured on the primary IP of the VM. +
+
diff --git a/docs/en-US/networks.xml b/docs/en-US/networks.xml index f877aa55584..cb7493cc945 100644 --- a/docs/en-US/networks.xml +++ b/docs/en-US/networks.xml @@ -32,6 +32,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/> + diff --git a/docs/en-US/whats-new.xml b/docs/en-US/whats-new.xml index 252f87d0543..295b53220e1 100644 --- a/docs/en-US/whats-new.xml +++ b/docs/en-US/whats-new.xml @@ -21,7 +21,11 @@ What's New in the API? The following describes any new major features of each &PRODUCT; version as it applies to - API usage. + API usage.
+
+ What's New in the API for 4.2 + +
What's New in the API for 4.1 From 5e445f9dd71233638fedeb91ba36aecaae769e8e Mon Sep 17 00:00:00 2001 From: radhikap Date: Wed, 10 Apr 2013 17:24:50 +0530 Subject: [PATCH 71/81] CLOUDSTACK-809 --- docs/en-US/added-API-commands-4.2.xml | 19 ++++--- docs/en-US/multiple-ip-nic.xml | 79 ++++++--------------------- 2 files changed, 30 insertions(+), 68 deletions(-) diff --git a/docs/en-US/added-API-commands-4.2.xml b/docs/en-US/added-API-commands-4.2.xml index 3b765f259c9..4bec148ba96 100644 --- a/docs/en-US/added-API-commands-4.2.xml +++ b/docs/en-US/added-API-commands-4.2.xml @@ -22,17 +22,22 @@ Added API Commands in 4.2 - addIpToNic (Adds an IP address to the NIC from the guest subnet. The request parameters - are: nicid, ipaddress) + addIpToNic + Adds an IP address to the NIC from the guest subnet. The request parameters are: nicid, + ipaddress. + The response parameters are: nicid, ipaddress, networkid - removeIpFromNic (Removes the reserved IP for the NIC. The request parameters is: - id) + removeIpFromNic + Removes the reserved IP for the NIC. The request parameters is: id. + The response parameters are: true, false - listNics (Lists the NIC details of the user VM; the API response also contains the - Secondary IP addresses of the NIC. The request parameters are: nicid, - virtualmachineid) + listNics + Lists the NIC details of the user VM; the API response also contains the Secondary IP + addresses of the NIC. The request parameters are: nicid, virtualmachineid. + The response parameters are: id, ipaddress, secondaryips, gateway, netmask, macaddr, + broadcasturi, isolationuri, isdefault,
diff --git a/docs/en-US/multiple-ip-nic.xml b/docs/en-US/multiple-ip-nic.xml index fb9a331141c..561ba0757b5 100644 --- a/docs/en-US/multiple-ip-nic.xml +++ b/docs/en-US/multiple-ip-nic.xml @@ -21,27 +21,32 @@ -->
Configuring Multiple IP Addresses on a Single NIC - &PRODUCT; now provides you the ability to associate multiple private IP addresses per NIC. - This feature is supported on all the network configurations—Basic, Advanced, and VPC. - Security Groups, Static NAT and Port forwarding services are supported on these additional IPs. - In addition to the primary IP, you can assign additional IPs to the guest VM NIC. Up to 256 IP - addresses are allowed per NIC. + &PRODUCT; now provides you the ability to associate multiple private IP addresses per guest + VM NIC. This feature is supported on all the network configurations—Basic, Advanced, and + VPC. Security Groups, Static NAT and Port forwarding services are supported on these additional + IPs. In addition to the primary IP, you can assign additional IPs to the guest VM NIC. Up to 256 + IP addresses are allowed per NIC. As always, you can specify an IP from the guest subnet; if not specified, an IP is - automatically picked up from the guest subnet. You can view the IPs and associated NICs - for each VMs on the UI. You can apply NAT on these additional guest IPs by using firewall + automatically picked up from the guest VM subnet. You can view the IPs associated with for each + guest VM NICs on the UI. You can apply NAT on these additional guest IPs by using firewall configuration in the &PRODUCT; UI. You must specify the NIC to which the IP should be associated. This feature is supported on XenServer, KVM, and VMware hypervisors. + + You need to configure the secondary IP address on the guest VM. &PRODUCT; will + not configure the acquired IP address on the VM. Ensure that you assign IPs to NIC each + time the VM reboots. + Some of the use cases are described below: - Building network appliances: Network appliances, such as firewalls and load - balancers, generally work best when they have access to multiple IP addresses on the - network interface. + Building network appliances: Network appliances, such as firewalls and load balancers, + generally work best when they have access to multiple IP addresses on the network + interface. - Moving private IP addresses between interfaces or instances. Applications that are - bound to specific IP addresses can be moved between instances. + Moving private IP addresses between interfaces or instances. Applications that are bound + to specific IP addresses can be moved between instances. Hosting multiple SSL Websites on a single instance. You can install multiple SSL @@ -74,55 +79,7 @@
-
- API Changes - The following APIs have been added: - - - - - Parameter Name - Description - Request Parameter - Response Parameter - - - - - addIpToNic - Adds an IP address to the NIC from the guest subnet. - - nicid - (optional) ipaddress - - nicid - ipaddress - networkid - - - removeIpFromNic - Removes the reserved IP for the NIC. - - id - - true - false - - - listNics - Lists the NIC details of the user VM; the API response also contains - the Secondary IP addresses of the NIC. - - virtualmachineid - (optional) nicid - - Lists the NIC details including secondary IP address - - - - -
-
+
Port Forwarding and StaticNAT Services Changes Because multiple IPs can be associated per NIC, you are allowed to select a desired IP for the Port Forwarding and StaticNAT services. The default is the primary IP. To enable this From 26695151f69fdf336f76c5feec94b30740ed21b8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 18:01:02 +0530 Subject: [PATCH 72/81] appliance: Permute again, give some more megas to /usr for systemvm Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/preseed.cfg | 4 ++-- tools/appliance/definitions/systemvmtemplate64/preseed.cfg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index cafb9c3704a..d456256a37d 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 850 20 1100 ext4 \ + 900 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -161,7 +161,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ . \ - 100 70 400 ext4 \ + 50 70 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /tmp } \ diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index cafb9c3704a..d456256a37d 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -146,7 +146,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 850 20 1100 ext4 \ + 900 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ @@ -161,7 +161,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ . \ - 100 70 400 ext4 \ + 50 70 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /tmp } \ From 11042121b29a96df2ff392f8e0030f5cb9fb79b9 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 18:04:17 +0530 Subject: [PATCH 73/81] appliance: only build-essential needed to build vmtools Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/postinstall.sh | 4 ++-- tools/appliance/definitions/systemvmtemplate64/postinstall.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index c0a7e87da2f..0395f380fb2 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -66,7 +66,7 @@ install_packages() { # vmware tools # uncomment for opensource vmware tool: # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools - apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` df -h PREVDIR=$PWD cd /opt @@ -77,7 +77,7 @@ install_packages() { ./vmware-install.pl -d cd $PREV rm -fr /opt/vmware-tools-distrib - apt-get -q -y --force-yes purge build-essential gcc + apt-get -q -y --force-yes purge build-essential } setup_accounts() { diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index c0a7e87da2f..0395f380fb2 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -66,7 +66,7 @@ install_packages() { # vmware tools # uncomment for opensource vmware tool: # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools - apt-get --no-install-recommends -q -y --force-yes install build-essential gcc linux-headers-`uname -r` + apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` df -h PREVDIR=$PWD cd /opt @@ -77,7 +77,7 @@ install_packages() { ./vmware-install.pl -d cd $PREV rm -fr /opt/vmware-tools-distrib - apt-get -q -y --force-yes purge build-essential gcc + apt-get -q -y --force-yes purge build-essential } setup_accounts() { From 9fca393f01efac58cdcd2b760ebbd42b5dbeafb6 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 18:10:03 +0530 Subject: [PATCH 74/81] appliance: Install open-vm-tools, vmwaretools alternative Signed-off-by: Rohit Yadav --- tools/appliance/definitions/systemvmtemplate/postinstall.sh | 3 +-- tools/appliance/definitions/systemvmtemplate64/postinstall.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index 0395f380fb2..176c93f914d 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -64,8 +64,7 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install iptables-persistent # vmware tools - # uncomment for opensource vmware tool: - # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools + apt-get --no-install-recommends -q -y --force-yes install open-vm-tools apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` df -h PREVDIR=$PWD diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index 0395f380fb2..176c93f914d 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -64,8 +64,7 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install iptables-persistent # vmware tools - # uncomment for opensource vmware tool: - # apt-get --no-install-recommends -q -y --force-yes install open-vm-tools + apt-get --no-install-recommends -q -y --force-yes install open-vm-tools apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` df -h PREVDIR=$PWD From 6bec37dc046b107c3de16a403adf0a9d7f0c2e18 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Wed, 10 Apr 2013 14:57:56 +0200 Subject: [PATCH 75/81] Detect if we are root based on the effective uid instead of the username. Allows sysadmins to specifiy their own username if they want. --- scripts/vm/systemvm/injectkeys.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/vm/systemvm/injectkeys.sh b/scripts/vm/systemvm/injectkeys.sh index 49adfb34cb3..c17a3c61935 100755 --- a/scripts/vm/systemvm/injectkeys.sh +++ b/scripts/vm/systemvm/injectkeys.sh @@ -69,9 +69,9 @@ copy_priv_key() { return $? } -if [[ `whoami` == cloud* ]] +if [[ "$EUID" -ne 0 ]] then - SUDO=$SUDO + SUDO="sudo " fi $SUDO mkdir -p $MOUNTPATH From ec7583e2a5162bbba2f184106dbbc1dbd833abbc Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Wed, 10 Apr 2013 16:33:23 +0200 Subject: [PATCH 76/81] Remove some non-oss dependencies we don't use anymore --- deps/install-non-oss.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/deps/install-non-oss.sh b/deps/install-non-oss.sh index 74575a8dbd1..0bf8e48d70c 100755 --- a/deps/install-non-oss.sh +++ b/deps/install-non-oss.sh @@ -17,7 +17,6 @@ # under the License. mvn install:install-file -Dfile=cloud-iControl.jar -DgroupId=com.cloud.com.f5 -DartifactId=icontrol -Dversion=1.0 -Dpackaging=jar -mvn install:install-file -Dfile=cloud-netscaler.jar -DgroupId=com.cloud.com.citrix -DartifactId=netscaler -Dversion=1.0 -Dpackaging=jar mvn install:install-file -Dfile=cloud-netscaler-sdx.jar -DgroupId=com.cloud.com.citrix -DartifactId=netscaler-sdx -Dversion=1.0 -Dpackaging=jar # From http://support.netapp.com/ (not available online, contact your support representative) @@ -25,18 +24,6 @@ mvn install:install-file -Dfile=cloud-netscaler-sdx.jar -DgroupId=com.cloud.com. if [ -e cloud-manageontap.jar ]; then mv cloud-manageontap.jar manageontap.jar; fi mvn install:install-file -Dfile=manageontap.jar -DgroupId=com.cloud.com.netapp -DartifactId=manageontap -Dversion=4.0 -Dpackaging=jar -# From https://my.vmware.com/group/vmware/get-download?downloadGroup=VSDK41 -# Version: 4.1, Release-date: 2010-07-13, Build: 257238 -if [ -e vmware-apputils.jar ]; then mv vmware-apputils.jar apputils.jar; fi -if [ -e vmware-vim.jar ]; then mv vmware-vim.jar vim.jar; fi -if [ -e vmware-vim25.jar ]; then mv vmware-vim25.jar vim25.jar; fi -mvn install:install-file -Dfile=vim25.jar -DgroupId=com.cloud.com.vmware -DartifactId=vmware-vim25 -Dversion=4.1 -Dpackaging=jar -mvn install:install-file -Dfile=apputils.jar -DgroupId=com.cloud.com.vmware -DartifactId=vmware-apputils -Dversion=4.1 -Dpackaging=jar -mvn install:install-file -Dfile=vim.jar -DgroupId=com.cloud.com.vmware -DartifactId=vmware-vim -Dversion=4.1 -Dpackaging=jar - -# # From https://my.vmware.com/group/vmware/get-download?downloadGroup=VSP510-WEBSDK-510 # Version: 5.1, Release-date: 2012-09-10, Build: 774886 mvn install:install-file -Dfile=vim25_51.jar -DgroupId=com.cloud.com.vmware -DartifactId=vmware-vim25 -Dversion=5.1 -Dpackaging=jar - - From a4a129ef94f161590aa7a082848ec77d613db0b7 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Wed, 10 Apr 2013 10:43:01 -0500 Subject: [PATCH 77/81] CLOUDSTACK-1837: Documenting additional upgrade steps. --- docs/en-US/Release_Notes.xml | 70 +++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 4535fd8c7e3..b8c5c01fb0b 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -33,7 +33,7 @@ under the License. If you run into any issues during upgrades, please feel free to ask questions on users@apache.cloudstack.org or dev@apache.cloudstack.org.
Upgrade from 4.0.x to 4.1.0 - This section will guide you from Apache CloudStack 4.0.x versions (4.0.0-incubating, 4.0.1-incubating, and 4.0.2) to &PRODUCT; 4.1.0. + This section will guide you from Apache CloudStack 4.0.x versions to &PRODUCT; 4.1.0. Any steps that are hypervisor-specific will be called out with a note. Package Structure Changes The package structure for &PRODUCT; has changed significantly since the 4.0.x releases. If you've compiled your own packages, you'll notice that the package names and the number of packages has changed. This is not a bug. @@ -50,6 +50,10 @@ under the License. Stop your management server or servers. Run this on all management server hosts: # service cloud-management stop + + If you are running a usage server or usage servers, stop those as well: + # service cloud-usage stop + Make a backup of your MySQL database. If you run into any issues or need to roll back the upgrade, this will assist in debugging or restoring your existing environment. You'll be prompted for your password. # mysqldump -u root -p cloud > cloudstack-backup.sql @@ -61,19 +65,6 @@ under the License. If you have made changes to /etc/cloud/management/components.xml, you'll need to carry these over manually to the new file, /etc/cloudstack/management/componentContext.xml. This is not done automatically. (If you're unsure, we recommend making a backup of the original components.xml to be on the safe side. - - For AWS API Users Only - This step is only necessary if you're using the AWS APIs with &PRODUCT;. If not, you can skip this step. - - The file /etc/cloud/management/db.properties will be carried over to etc/cloudstack/management/db.properties, but the parameters for the AWS API are not carried over. You'll need to add these parameters to the new file manually: - -db.awsapi.username= -db.awsapi.password= -db.awsapi.host= -db.awsapi.port= - - For the AWS API queries to work, you'll need to copy those over to /etc/cloudstack/management/db.properties (with the values you have currently). - If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . Community Packages @@ -110,12 +101,51 @@ db.awsapi.port= Restart the agent: + +service cloud-agent stop +killall jsvc +service cloudstack-agent start + + + During the upgrade, log4j-cloud.xml was simply copied over, so the logs will continue to be added to /var/log/cloud/agent/agent.log. There's nothing wrong with this, but if you prefer to be consistent, you can change this by copying over the sample configuration file: + +cd /etc/cloudstack/agent +mv log4j-cloud.xml.dpkg-dist log4j-cloud.xml +service cloudstack-agent restart + + + + Once the agent is running, you can uninstall the old cloud-* packages from your system: + sudo dpkg --purge cloud-agent + The package names have changed between 4.0 and 4.1, so upgrading the packages won't happen automatically with a yum update + + Once you've upgraded the packages on your management servers, you'll need to restart the system VMs. Make sure port 8096 is open to do this. + There is a script that will do this for you, all you need to do is run the script and supply the IP address for your MySQL instance and your MySQL credentials: + # nohup cloudstack-sysvmadm -d IP address -u cloud -p -a > sysvm.log 2>&1 & + You can monitor the log for progress. The process of restarting the system VMs can take an hour or more. + # tail -f sysvm.log + The output to sysvm.log will look something like this: + +Stopping and starting 1 secondary storage vm(s)... +Done stopping and starting secondary storage vm(s) +Stopping and starting 1 console proxy vm(s)... +Done stopping and starting console proxy vm(s). +Stopping and starting 4 running routing vm(s)... +Done restarting router(s). + + + + For Xen Hosts: Copy vhd-utils + This step is only for CloudStack installs that are using Xen hosts. + + Copy the file vhd-utils to /usr/share/cloudstack-common/scripts/vm/hypervisor/xenserver. +
@@ -856,12 +886,12 @@ db.awsapi.port= # tail -f sysvm.log The content should be like the following: - Stopping and starting 1 secondary storage vm(s)... - Done stopping and starting secondary storage vm(s) - Stopping and starting 1 console proxy vm(s)... - Done stopping and starting console proxy vm(s). - Stopping and starting 4 running routing vm(s)... - Done restarting router(s). +Stopping and starting 1 secondary storage vm(s)... +Done stopping and starting secondary storage vm(s) +Stopping and starting 1 console proxy vm(s)... +Done stopping and starting console proxy vm(s). +Stopping and starting 4 running routing vm(s)... +Done restarting router(s). From 7efbcfa9bf721c105436cd11171c5095efbc39e6 Mon Sep 17 00:00:00 2001 From: radhikap Date: Wed, 10 Apr 2013 22:02:22 +0530 Subject: [PATCH 78/81] CLOUDSTACK-772 --- docs/en-US/vmware-cluster-config-dvswitch.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml index dfbaca11627..3468c1bea4e 100644 --- a/docs/en-US/vmware-cluster-config-dvswitch.xml +++ b/docs/en-US/vmware-cluster-config-dvswitch.xml @@ -65,7 +65,7 @@ Additionally, &PRODUCT; uses VDS for virtual network infrastructure if the value of vmware.use.dvswitch parameter is true and the value of vmware.use.nexus.dvswitch parameter is false. - &PRODUCT; supports configuring virtual networks in a deployment with a mix of Virtual + &PRODUCT; supports orchestration of virtual networks in a deployment with a mix of Virtual Distributed Switch, Standard Virtual Switch and Nexus 1000v Virtual Switch.
@@ -101,8 +101,8 @@ vCenter Host - Enter the host name or the IP address of the vCenter host where you have - deployed the Nexus virtual switch. + Enter the name or the IP address of the vCenter host where you have deployed the VMware + VDS. vCenter User name @@ -132,7 +132,7 @@ Public Traffic vSwitch Name - Specify a name to identify the switch. + Name of virtual switch to be used for the public traffic. Override Guest Traffic @@ -148,18 +148,18 @@ Guest Traffic vSwitch Name - Specify a name to identify the switch. + Name of virtual switch to be used for guest traffic.
- Removing Nexus Virtual Switch + Removing VMware Virtual Switch - In the vCenter datacenter that is served by the VMware dvSwitch, ensure that you - delete all the hosts in the corresponding cluster. + In the vCenter datacenter that is served by the VDS, ensure that you delete all the + hosts in the corresponding cluster. Log in with Admin permissions to the &PRODUCT; administrator UI. From ed79b8bf140196a169a89021a4d02747d5312ad8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 10 Apr 2013 23:10:29 +0530 Subject: [PATCH 79/81] CLOUDSTACK-1867: Comment vmware-tools installation, use open-vm-tools Signed-off-by: Rohit Yadav --- .../systemvmtemplate/postinstall.sh | 27 ++++++++++--------- .../definitions/systemvmtemplate/preseed.cfg | 8 +++--- .../systemvmtemplate64/postinstall.sh | 27 ++++++++++--------- .../systemvmtemplate64/preseed.cfg | 8 +++--- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index 176c93f914d..ae8f1adfb9c 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -65,18 +65,19 @@ install_packages() { # vmware tools apt-get --no-install-recommends -q -y --force-yes install open-vm-tools - apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` - df -h - PREVDIR=$PWD - cd /opt - wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz - tar xzf VMwareTools-9.2.1-818201.tar.gz - rm VMwareTools-*.tar.gz - cd vmware-tools-distrib - ./vmware-install.pl -d - cd $PREV - rm -fr /opt/vmware-tools-distrib - apt-get -q -y --force-yes purge build-essential + # commented installaion of vmware-tools as we are using the opensource open-vm-tools: + # apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` + # df -h + # PREVDIR=$PWD + # cd /opt + # wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz + # tar xzf VMwareTools-9.2.1-818201.tar.gz + # rm VMwareTools-*.tar.gz + # cd vmware-tools-distrib + # ./vmware-install.pl -d + # cd $PREV + # rm -fr /opt/vmware-tools-distrib + # apt-get -q -y --force-yes purge build-essential } setup_accounts() { @@ -184,7 +185,7 @@ configure_services() { snapshot_url="https://git-wip-us.apache.org/repos/asf?p=cloudstack.git;a=snapshot;h=HEAD;sf=tgz" snapshot_dir="/opt/cloudstack*" cd /opt - wget $snapshot_url -O cloudstack.tar.gz + wget --no-check-certificate $snapshot_url -O cloudstack.tar.gz tar -zxvf cloudstack.tar.gz cp -rv $snapshot_dir/patches/systemvm/debian/config/* / cp -rv $snapshot_dir/patches/systemvm/debian/vpn/* / diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index d456256a37d..79349f6d62c 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 250 40 400 ext4 \ + 300 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,17 +146,17 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 900 20 1100 ext4 \ + 700 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ . \ - 300 40 500 ext4 \ + 400 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ . \ - 450 60 1000 ext4 \ + 500 60 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index 176c93f914d..ae8f1adfb9c 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -65,18 +65,19 @@ install_packages() { # vmware tools apt-get --no-install-recommends -q -y --force-yes install open-vm-tools - apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` - df -h - PREVDIR=$PWD - cd /opt - wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz - tar xzf VMwareTools-9.2.1-818201.tar.gz - rm VMwareTools-*.tar.gz - cd vmware-tools-distrib - ./vmware-install.pl -d - cd $PREV - rm -fr /opt/vmware-tools-distrib - apt-get -q -y --force-yes purge build-essential + # commented installaion of vmware-tools as we are using the opensource open-vm-tools: + # apt-get --no-install-recommends -q -y --force-yes install build-essential linux-headers-`uname -r` + # df -h + # PREVDIR=$PWD + # cd /opt + # wget http://people.apache.org/~bhaisaab/cloudstack/VMwareTools-9.2.1-818201.tar.gz + # tar xzf VMwareTools-9.2.1-818201.tar.gz + # rm VMwareTools-*.tar.gz + # cd vmware-tools-distrib + # ./vmware-install.pl -d + # cd $PREV + # rm -fr /opt/vmware-tools-distrib + # apt-get -q -y --force-yes purge build-essential } setup_accounts() { @@ -184,7 +185,7 @@ configure_services() { snapshot_url="https://git-wip-us.apache.org/repos/asf?p=cloudstack.git;a=snapshot;h=HEAD;sf=tgz" snapshot_dir="/opt/cloudstack*" cd /opt - wget $snapshot_url -O cloudstack.tar.gz + wget --no-check-certificate $snapshot_url -O cloudstack.tar.gz tar -zxvf cloudstack.tar.gz cp -rv $snapshot_dir/patches/systemvm/debian/config/* / cp -rv $snapshot_dir/patches/systemvm/debian/vpn/* / diff --git a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg index d456256a37d..79349f6d62c 100644 --- a/tools/appliance/definitions/systemvmtemplate64/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate64/preseed.cfg @@ -136,7 +136,7 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /boot } \ . \ - 250 40 400 ext4 \ + 300 40 400 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ / } \ @@ -146,17 +146,17 @@ d-i partman-auto/expert_recipe string \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /home } \ . \ - 900 20 1100 ext4 \ + 700 20 1100 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /usr } \ . \ - 300 40 500 ext4 \ + 400 40 500 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /opt } \ . \ - 450 60 1000 ext4 \ + 500 60 1000 ext4 \ method{ format } format{ } \ use_filesystem{ } filesystem{ ext4 } \ mountpoint{ /var } \ From ee0a91d111349b981d5f97fa69c97c34d9f15268 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 10 Apr 2013 10:46:53 -0700 Subject: [PATCH 80/81] Fixed the issue - VM deployment for local service offering for Root and with data disk(shared), was deploying data disk to local disk too. Currently there is no way to let the planner know multiple pool information, hence letting the planner search for a pool always during VM deployment. --- .../cloud/entity/api/VMEntityManagerImpl.java | 2 +- .../src/com/cloud/deploy/FirstFitPlanner.java | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index 8b9b100c243..0359db95a96 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -207,7 +207,7 @@ public class VMEntityManagerImpl implements VMEntityManager { } DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vmReservation.getPodId(), vmReservation.getClusterId(), - vmReservation.getHostId(), poolId , null); + vmReservation.getHostId(), null , null); VMInstanceVO vmDeployed = _itMgr.start(vm, params, _userDao.findById(new Long(caller)), _accountDao.findById(vm.getAccountId()), plan); diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index 012d160d3ef..2dffe70fb46 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -102,7 +102,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Inject protected StorageManager _storageMgr; @Inject DataStoreManager dataStoreMgr; @Inject protected ClusterDetailsDao _clusterDetailsDao; - + protected List _storagePoolAllocators; public List getStoragePoolAllocators() { return _storagePoolAllocators; @@ -157,7 +157,8 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if(plan.getHostId() != null && haVmTag == null){ Long hostIdSpecified = plan.getHostId(); if (s_logger.isDebugEnabled()){ - s_logger.debug("DeploymentPlan has host_id specified, making no checks on this host, looks like admin test: "+hostIdSpecified); + s_logger.debug("DeploymentPlan has host_id specified, choosing this host and making no checks on this host: " + + hostIdSpecified); } HostVO host = _hostDao.findById(hostIdSpecified); if (s_logger.isDebugEnabled()) { @@ -407,9 +408,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } /** - * This method should reorder the given list of Cluster Ids by applying any necessary heuristic + * This method should reorder the given list of Cluster Ids by applying any necessary heuristic * for this planner - * For FirstFitPlanner there is no specific heuristic to be applied + * For FirstFitPlanner there is no specific heuristic to be applied * other than the capacity based ordering which is done by default. * @return List ordered list of Cluster Ids */ @@ -419,9 +420,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } /** - * This method should reorder the given list of Pod Ids by applying any necessary heuristic + * This method should reorder the given list of Pod Ids by applying any necessary heuristic * for this planner - * For FirstFitPlanner there is no specific heuristic to be applied + * For FirstFitPlanner there is no specific heuristic to be applied * other than the capacity based ordering which is done by default. * @return List ordered list of Pod Ids */ @@ -443,7 +444,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { private List listDisabledPods(long zoneId){ List disabledPods = _podDao.listDisabledPods(zoneId); return disabledPods; - } + } private Map getCapacityThresholdMap(){ // Lets build this real time so that the admin wont have to restart MS if he changes these values @@ -461,9 +462,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } private List getCapacitiesForCheckingThreshold(){ - List capacityList = new ArrayList(); + List capacityList = new ArrayList(); capacityList.add(Capacity.CAPACITY_TYPE_CPU); - capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); + capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); return capacityList; } @@ -479,7 +480,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { // For each capacity get the cluster list crossing the threshold and remove it from the clusterList that will be used for vm allocation. for(short capacity : capacityList){ - + if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() == 0){ return; } @@ -492,17 +493,17 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { capacityThresholdMap.get(capacity), ram_requested ); } - + if (clustersCrossingThreshold != null && clustersCrossingThreshold.size() != 0){ // addToAvoid Set avoid.addClusterList(clustersCrossingThreshold); // Remove clusters crossing disabled threshold clusterListForVmAllocation.removeAll(clustersCrossingThreshold); - + s_logger.debug("Cannot allocate cluster list " + clustersCrossingThreshold.toString() + " for vm creation since their allocated percentage" + " crosses the disable capacity threshold: " + capacityThresholdMap.get(capacity) + " for capacity Type : " + capacity + ", skipping these clusters"); } - + } } @@ -652,7 +653,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { public int compare(Volume v1, Volume v2) { if(v1.getSize() < v2.getSize()) return 1; - else + else return -1; } }); @@ -749,7 +750,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { }else{ pool = (StoragePool)this.dataStoreMgr.getPrimaryDataStore(plan.getPoolId()); } - + if(!pool.isInMaintenance()){ if(!avoid.shouldAvoid(pool)){ long exstPoolDcId = pool.getDataCenterId(); @@ -781,13 +782,13 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if(!isRootAdmin(plan.getReservationContext())){ if(!isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())){ if(s_logger.isDebugEnabled()){ - s_logger.debug("Cannot allocate new storagepool for this volume in this cluster, allocation state is disabled"); + s_logger.debug("Cannot allocate new storagepool for this volume in this cluster, allocation state is disabled"); s_logger.debug("Cannot deploy to this specified plan, allocation state is disabled, returning."); } - //Cannot find suitable storage pools under this cluster for this volume since allocation_state is disabled. + //Cannot find suitable storage pools under this cluster for this volume since allocation_state is disabled. //- remove any suitable pools found for other volumes. //All volumes should get suitable pools under this cluster; else we cant use this cluster. - suitableVolumeStoragePools.clear(); + suitableVolumeStoragePools.clear(); break; } } @@ -877,7 +878,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { super.configure(name, params); _allocationAlgorithm = _configDao.getValue(Config.VmAllocationAlgorithm.key()); return true; - } + } private boolean isEnabledForAllocation(long zoneId, Long podId, Long clusterId){ // Check if the zone exists in the system From a4a059c0430eabf30bd96261c71aa700e62cca0c Mon Sep 17 00:00:00 2001 From: Jayapal Date: Wed, 10 Apr 2013 18:04:02 +0530 Subject: [PATCH 81/81] CLOUDSTACK-779 Egress firewall rules support for Juniper SRX --- .../cloud/agent/api/to/FirewallRuleTO.java | 6 + .../JuniperSRXExternalFirewallElement.java | 2 +- .../network/resource/JuniperSrxResource.java | 322 ++++++++++++++---- scripts/network/juniper/application-add.xml | 2 +- .../network/juniper/security-policy-add.xml | 4 +- .../ExternalFirewallDeviceManagerImpl.java | 12 +- .../cloud/upgrade/dao/Upgrade410to420.java | 60 ++++ 7 files changed, 328 insertions(+), 80 deletions(-) diff --git a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java index 7f779365c9e..f296aa4d1f9 100644 --- a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.State; +import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.utils.net.NetUtils; /** @@ -109,6 +110,11 @@ public class FirewallRuleTO implements InternalIdentity { this(rule.getId(),srcVlanTag, srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), revokeState, alreadyAdded, purpose,rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); } + public FirewallRuleTO(FirewallRule rule, String guestVlanTag, FirewallRule.TrafficType trafficType) { + this(rule.getId(), guestVlanTag, null, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(), rule.getSourceCidrList(), rule.getIcmpType(), rule.getIcmpCode()); + this.trafficType = trafficType; + } + public FirewallRule.TrafficType getTrafficType(){ return trafficType; } diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java index af0912ad9f5..64b0f5aa37d 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java @@ -274,7 +274,7 @@ PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, Junip firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); firewallCapabilities.put(Capability.MultipleIps, "true"); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); - firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress"); + firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress"); capabilities.put(Service.Firewall, firewallCapabilities); // Disabling VPN for Juniper in Acton as it 1) Was never tested 2) probably just doesn't work diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java index 84821680198..a0068c3784c 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java @@ -303,7 +303,7 @@ public class JuniperSrxResource implements ServerResource { } private enum Protocol { - tcp, udp, icmp, any; + tcp, udp, icmp, all, any; } private enum RuleMatchCondition { @@ -320,7 +320,8 @@ public class JuniperSrxResource implements ServerResource { private enum SecurityPolicyType { STATIC_NAT("staticnat"), DESTINATION_NAT("destnat"), - VPN("vpn"); + VPN("vpn"), + SECURITYPOLICY_EGRESS("egress"); private String identifier; @@ -776,6 +777,43 @@ public class JuniperSrxResource implements ServerResource { s_logger.debug(msg); } + private Map> getActiveFirewallEgressRules(FirewallRuleTO[] allRules) { + Map> activeRules = new HashMap>(); + + for (FirewallRuleTO rule : allRules) { + String guestVlan; + guestVlan = rule.getSrcVlanTag(); + + ArrayList activeRulesForNetwork = activeRules.get(guestVlan); + + if (activeRulesForNetwork == null) { + activeRulesForNetwork = new ArrayList(); + } + + if (!rule.revoked() || rule.isAlreadyAdded()) { + activeRulesForNetwork.add(rule); + } + + activeRules.put(guestVlan, activeRulesForNetwork); + } + + return activeRules; + } + + private List extractCidrs(List rules) throws ExecutionException { + List allCidrs = new ArrayList(); + List cidrs = new ArrayList(); + + for (FirewallRuleTO rule : rules) { + cidrs = (rule.getSourceCidrList()); + for (String cidr: cidrs) { + if (!allCidrs.contains(cidr)) { + allCidrs.add(cidr); + } + } + } + return allCidrs; + } /* security policies */ private synchronized Answer execute(SetFirewallRulesCommand cmd) { @@ -787,24 +825,39 @@ public class JuniperSrxResource implements ServerResource { FirewallRuleTO[] rules = cmd.getRules(); try { openConfiguration(); + if (rules[0].getTrafficType() == FirewallRule.TrafficType.Egress) { + Map> activeRules = getActiveFirewallEgressRules(rules); + Set guestVlans = activeRules.keySet(); + List cidrs = new ArrayList(); - for (FirewallRuleTO rule : rules) { - int startPort = 0, endPort = 0; - if (rule.getSrcPortRange() != null) { - startPort = rule.getSrcPortRange()[0]; - endPort = rule.getSrcPortRange()[1]; + for (String guestVlan : guestVlans) { + List activeRulesForGuestNw = activeRules.get(guestVlan); + + removeEgressSecurityPolicyAndApplications(SecurityPolicyType.SECURITYPOLICY_EGRESS, guestVlan, extractCidrs(activeRulesForGuestNw)); + if (activeRulesForGuestNw.size() > 0) { + addEgressSecurityPolicyAndApplications(SecurityPolicyType.SECURITYPOLICY_EGRESS, guestVlan, extractApplications(activeRulesForGuestNw), extractCidrs(activeRulesForGuestNw)); + } } - FirewallFilterTerm term = new FirewallFilterTerm(genIpIdentifier(rule.getSrcIp()) + "-" + String.valueOf(rule.getId()), rule.getSourceCidrList(), - rule.getSrcIp(), rule.getProtocol(), startPort, endPort, - rule.getIcmpType(), rule.getIcmpCode(), genIpIdentifier(rule.getSrcIp()) + _usageFilterIPInput.getCounterIdentifier()); - if (!rule.revoked()) { - manageFirewallFilter(SrxCommand.ADD, term, _publicZoneInputFilterName); - } else { - manageFirewallFilter(SrxCommand.DELETE, term, _publicZoneInputFilterName); + commitConfiguration(); + } else { + for (FirewallRuleTO rule : rules) { + int startPort = 0, endPort = 0; + if (rule.getSrcPortRange() != null) { + startPort = rule.getSrcPortRange()[0]; + endPort = rule.getSrcPortRange()[1]; + FirewallFilterTerm term = new FirewallFilterTerm(genIpIdentifier(rule.getSrcIp()) + "-" + String.valueOf(rule.getId()), rule.getSourceCidrList(), + rule.getSrcIp(), rule.getProtocol(), startPort, endPort, + rule.getIcmpType(), rule.getIcmpCode(), genIpIdentifier(rule.getSrcIp()) + _usageFilterIPInput.getCounterIdentifier()); + if (!rule.revoked()) { + manageFirewallFilter(SrxCommand.ADD, term, _publicZoneInputFilterName); + } else { + manageFirewallFilter(SrxCommand.DELETE, term, _publicZoneInputFilterName); + } + } + commitConfiguration(); } } - commitConfiguration(); return new Answer(cmd); } catch (ExecutionException e) { s_logger.error(e); @@ -992,7 +1045,7 @@ public class JuniperSrxResource implements ServerResource { // Delete all security policies for (String securityPolicyName : getVpnObjectNames(SrxXml.SECURITY_POLICY_GETALL, accountId)) { - manageSecurityPolicy(SecurityPolicyType.VPN, SrxCommand.DELETE, accountId, null, null, null, securityPolicyName); + manageSecurityPolicy(SecurityPolicyType.VPN, SrxCommand.DELETE, accountId, null, null, null, null, securityPolicyName); } // Delete all address book entries @@ -1064,7 +1117,7 @@ public class JuniperSrxResource implements ServerResource { manageAddressBookEntry(srxCmd, _privateZone , guestNetworkCidr, ipsecVpnName); // Security policy - manageSecurityPolicy(SecurityPolicyType.VPN, srxCmd, null, null, guestNetworkCidr, null, ipsecVpnName); + manageSecurityPolicy(SecurityPolicyType.VPN, srxCmd, null, null, guestNetworkCidr, null, null, ipsecVpnName); } commitConfiguration(); @@ -2455,38 +2508,44 @@ public class JuniperSrxResource implements ServerResource { * Applications */ - private String genApplicationName(Protocol protocol, int startPort, int endPort) { + private String genApplicationName(SecurityPolicyType type, Protocol protocol, int startPort, int endPort) { if (protocol.equals(Protocol.any)) { return Protocol.any.toString(); } else { - return genObjectName(protocol.toString(), String.valueOf(startPort), String.valueOf(endPort)); + if (type.equals(SecurityPolicyType.SECURITYPOLICY_EGRESS)) { + return genObjectName(type.getIdentifier(), protocol.toString(), String.valueOf(startPort), String.valueOf(endPort)); + } else { + return genObjectName(protocol.toString(), String.valueOf(startPort), String.valueOf(endPort)); + } } } - private Object[] parseApplicationName(String applicationName) throws ExecutionException { + private Object[] parseApplicationName(SecurityPolicyType type, String applicationName) throws ExecutionException { String errorMsg = "Invalid application: " + applicationName; String[] applicationComponents = applicationName.split("-"); Protocol protocol; Integer startPort; Integer endPort; + int offset = 0; try { - protocol = getProtocol(applicationComponents[0]); - startPort = Integer.parseInt(applicationComponents[1]); - endPort = Integer.parseInt(applicationComponents[2]); - } catch (Exception e) { + offset = type.equals(SecurityPolicyType.SECURITYPOLICY_EGRESS) ? 1 : 0; + protocol = getProtocol(applicationComponents[offset + 0]); + startPort = Integer.parseInt(applicationComponents[offset + 1]); + endPort = Integer.parseInt(applicationComponents[offset + 2]); + } catch (Exception e) { throw new ExecutionException(errorMsg); } return new Object[]{protocol, startPort, endPort}; } - private boolean manageApplication(SrxCommand command, Protocol protocol, int startPort, int endPort) throws ExecutionException { + private boolean manageApplication(SecurityPolicyType type, SrxCommand command, Protocol protocol, int startPort, int endPort) throws ExecutionException { if (protocol.equals(Protocol.any)) { return true; } - String applicationName = genApplicationName(protocol, startPort, endPort); + String applicationName = genApplicationName(type, protocol, startPort, endPort); String xml; switch (command) { @@ -2498,23 +2557,28 @@ public class JuniperSrxResource implements ServerResource { return sendRequestAndCheckResponse(command, xml, "name", applicationName); case ADD: - if (manageApplication(SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { + if (manageApplication(type, SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { return true; } - + String icmpOrDestPort; xml = SrxXml.APPLICATION_ADD.getXml(); xml = replaceXmlValue(xml, "name", applicationName); xml = replaceXmlValue(xml, "protocol", protocol.toString()); - - String destPort; - if (startPort == endPort) { - destPort = String.valueOf(startPort); + if (protocol.toString() == Protocol.icmp.toString()) { + icmpOrDestPort = "" + startPort + ""; + icmpOrDestPort += "" + endPort + ""; } else { - destPort = startPort + "-" + endPort; + String destPort; + + if (startPort == endPort) { + destPort = String.valueOf(startPort); + } else { + destPort = startPort + "-" + endPort; + } + icmpOrDestPort = "" + destPort + ""; } - xml = replaceXmlValue(xml, "dest-port", destPort); - + xml = replaceXmlValue(xml, "dest-port-icmp", icmpOrDestPort); if (!sendRequestAndCheckResponse(command, xml)) { throw new ExecutionException("Failed to add application " + applicationName); } else { @@ -2522,7 +2586,7 @@ public class JuniperSrxResource implements ServerResource { } case DELETE: - if (!manageApplication(SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { + if (!manageApplication(type, SrxCommand.CHECK_IF_EXISTS, protocol, startPort, endPort)) { return true; } @@ -2543,13 +2607,13 @@ public class JuniperSrxResource implements ServerResource { } - private List getUnusedApplications(List applications) throws ExecutionException { + private List getUnusedApplications(List applications, String fromZone, String toZone) throws ExecutionException { List unusedApplications = new ArrayList(); // Check if any of the applications are unused by existing security policies String xml = SrxXml.SECURITY_POLICY_GETALL.getXml(); - xml = replaceXmlValue(xml, "from-zone", _publicZone); - xml = replaceXmlValue(xml, "to-zone", _privateZone); + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); String allPolicies = sendRequest(xml); for (String application : applications) { @@ -2560,10 +2624,7 @@ public class JuniperSrxResource implements ServerResource { return unusedApplications; } - - private List getApplicationsForSecurityPolicy(SecurityPolicyType type, String privateIp) throws ExecutionException { - String fromZone = _publicZone; - String toZone = _privateZone; + private List getApplicationsForSecurityPolicy(SecurityPolicyType type, String privateIp, String fromZone, String toZone) throws ExecutionException { String policyName = genSecurityPolicyName(type, null, null, fromZone, toZone, privateIp); String xml = SrxXml.SECURITY_POLICY_GETONE.getXml(); xml = setDelete(xml, false); @@ -2591,8 +2652,31 @@ public class JuniperSrxResource implements ServerResource { for (FirewallRuleTO rule : rules) { Object[] application = new Object[3]; application[0] = getProtocol(rule.getProtocol()); - application[1] = rule.getSrcPortRange()[0]; - application[2] = rule.getSrcPortRange()[1]; + if (application[0] == Protocol.icmp) { + if (rule.getIcmpType() == -1) { + application[1] = 255; + } else { + application[1] = rule.getIcmpType(); + } + + if (rule.getIcmpCode() == -1) { + application[2] = 255; + } else { + application[2] = rule.getIcmpCode(); + } + } else if (application[0] == Protocol.tcp || application[0] == Protocol.udp) { + if (rule.getSrcPortRange() != null) { + application[1] = rule.getSrcPortRange()[0]; + application[2] = rule.getSrcPortRange()[1]; + } else { + application[1] = 0; + application[2] = 65535; + } + } else if (application[0] == Protocol.all) { + application[1] = 0; + application[2] = 65535; + } + applications.add(application); } @@ -2611,16 +2695,20 @@ public class JuniperSrxResource implements ServerResource { } } - private boolean manageSecurityPolicy(SecurityPolicyType type, SrxCommand command, Long accountId, String username, String privateIp, List applicationNames, String ipsecVpnName) throws ExecutionException { + private boolean manageSecurityPolicy(SecurityPolicyType type, SrxCommand command, Long accountId, String username, String privateIp, List applicationNames, List cidrs, String ipsecVpnName) throws ExecutionException { String fromZone = _publicZone; String toZone = _privateZone; String securityPolicyName; - String addressBookEntryName; - + String addressBookEntryName = null; + if (type.equals(SecurityPolicyType.VPN) && ipsecVpnName != null) { - securityPolicyName = ipsecVpnName; - addressBookEntryName = ipsecVpnName; + securityPolicyName = ipsecVpnName; + addressBookEntryName = ipsecVpnName; + } else if (type.equals(SecurityPolicyType.SECURITYPOLICY_EGRESS)) { + fromZone = _privateZone; + toZone = _publicZone; + securityPolicyName = genSecurityPolicyName(type, accountId, username, fromZone, toZone, privateIp); } else { securityPolicyName = genSecurityPolicyName(type, accountId, username, fromZone, toZone, privateIp); addressBookEntryName = genAddressBookEntryName(privateIp); @@ -2661,17 +2749,38 @@ public class JuniperSrxResource implements ServerResource { return false; case ADD: - if (!manageAddressBookEntry(SrxCommand.CHECK_IF_EXISTS, toZone, privateIp, ipsecVpnName)) { - throw new ExecutionException("No address book entry for policy: " + securityPolicyName); + if (!type.equals(SecurityPolicyType.SECURITYPOLICY_EGRESS)) { + if (!manageAddressBookEntry(SrxCommand.CHECK_IF_EXISTS, toZone, privateIp, addressBookEntryName)) { + throw new ExecutionException("No address book entry for policy: " + securityPolicyName); + } + } + + String srcAddrs = ""; + String dstAddrs = ""; + xml = SrxXml.SECURITY_POLICY_ADD.getXml(); + xml = replaceXmlValue(xml, "policy-name", securityPolicyName); + if (type.equals(SecurityPolicyType.SECURITYPOLICY_EGRESS)) { + xml = replaceXmlValue(xml, "from-zone", _privateZone); + xml = replaceXmlValue(xml, "to-zone", _publicZone); + if (cidrs == null) { + srcAddrs = "any"; + } else { + for (String cidr : cidrs) { + srcAddrs += "" + genAddressBookEntryName(cidr) + ""; + } + } + xml = replaceXmlValue(xml, "src-address", srcAddrs); + dstAddrs = "any"; + xml = replaceXmlValue(xml, "dst-address", dstAddrs); + } else { + xml = replaceXmlValue(xml, "from-zone", fromZone); + xml = replaceXmlValue(xml, "to-zone", toZone); + srcAddrs = "any"; + xml = replaceXmlValue(xml, "src-address", srcAddrs); + dstAddrs = "" + addressBookEntryName + ""; + xml = replaceXmlValue(xml, "dst-address", dstAddrs); } - xml = SrxXml.SECURITY_POLICY_ADD.getXml(); - xml = replaceXmlValue(xml, "from-zone", fromZone); - xml = replaceXmlValue(xml, "to-zone", toZone); - xml = replaceXmlValue(xml, "policy-name", securityPolicyName); - xml = replaceXmlValue(xml, "src-address", "any"); - xml = replaceXmlValue(xml, "dest-address", addressBookEntryName); - if (type.equals(SecurityPolicyType.VPN) && ipsecVpnName != null) { xml = replaceXmlValue(xml, "tunnel", "" + ipsecVpnName + ""); } else { @@ -2679,7 +2788,7 @@ public class JuniperSrxResource implements ServerResource { } String applications; - if (applicationNames == null) { + if (applicationNames == null || applicationNames.size() == 0) { applications = "any"; } else { applications = ""; @@ -2697,11 +2806,11 @@ public class JuniperSrxResource implements ServerResource { } case DELETE: - if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, applicationNames, ipsecVpnName)) { + if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, applicationNames, cidrs, ipsecVpnName)) { return true; } - if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, applicationNames, ipsecVpnName)) { + if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, applicationNames, cidrs, ipsecVpnName)) { return true; } @@ -2757,42 +2866,42 @@ public class JuniperSrxResource implements ServerResource { int startPort = application[1] != null ? ((Integer) application[1]) : -1; int endPort = application[2] != null ? ((Integer) application[2]) : -1; - String applicationName = genApplicationName(protocol, startPort, endPort); + String applicationName = genApplicationName(type, protocol, startPort, endPort); if (!applicationNames.contains(applicationName)) { applicationNames.add(applicationName); } - manageApplication(SrxCommand.ADD, protocol, startPort, endPort); + manageApplication(type, SrxCommand.ADD, protocol, startPort, endPort); } // Add a new security policy - manageSecurityPolicy(type, SrxCommand.ADD, null, null, privateIp, applicationNames, null); + manageSecurityPolicy(type, SrxCommand.ADD, null, null, privateIp, applicationNames, null, null); return true; } private boolean removeSecurityPolicyAndApplications(SecurityPolicyType type, String privateIp) throws ExecutionException { - if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, null, null)) { + if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, privateIp, null,null, null)) { return true; } - if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, null, null)) { + if (manageSecurityPolicy(type, SrxCommand.CHECK_IF_IN_USE, null, null, privateIp, null, null, null)) { return true; } // Get a list of applications for this security policy - List applications = getApplicationsForSecurityPolicy(type, privateIp); + List applications = getApplicationsForSecurityPolicy(type, privateIp, _publicZone, _privateZone); - // Remove the security policy - manageSecurityPolicy(type, SrxCommand.DELETE, null, null, privateIp, null, null); + // Remove the security policy + manageSecurityPolicy(type, SrxCommand.DELETE, null, null, privateIp, null, null, null); // Remove any applications for the removed security policy that are no longer in use - List unusedApplications = getUnusedApplications(applications); + List unusedApplications = getUnusedApplications(applications, _publicZone, _privateZone); for (String application : unusedApplications) { Object[] applicationComponents; try { - applicationComponents = parseApplicationName(application); + applicationComponents = parseApplicationName(type, application); } catch (ExecutionException e) { s_logger.error("Found an invalid application: " + application + ". Not attempting to clean up."); continue; @@ -2800,13 +2909,78 @@ public class JuniperSrxResource implements ServerResource { Protocol protocol = (Protocol) applicationComponents[0]; Integer startPort = (Integer) applicationComponents[1]; - Integer endPort = (Integer) applicationComponents[2]; - manageApplication(SrxCommand.DELETE, protocol, startPort, endPort); + Integer endPort = (Integer) applicationComponents[2]; + manageApplication(type, SrxCommand.DELETE, protocol, startPort, endPort); } return true; } + + private boolean removeEgressSecurityPolicyAndApplications(SecurityPolicyType type, String guestVlan, List cidrs) throws ExecutionException { + if (!manageSecurityPolicy(type, SrxCommand.CHECK_IF_EXISTS, null, null, guestVlan, null, cidrs, null)) { + return true; + } + // Get a list of applications for this security policy + List applications; + applications = getApplicationsForSecurityPolicy(type, guestVlan, _privateZone, _publicZone); + + // Remove the security policy even if it is in use + manageSecurityPolicy(type, SrxCommand.DELETE, null, null, guestVlan, null, cidrs, null); + + // Remove any applications for the removed security policy that are no longer in use + List unusedApplications; + unusedApplications = getUnusedApplications(applications, _privateZone, _publicZone); + + for (String application : unusedApplications) { + Object[] applicationComponents; + + try { + applicationComponents = parseApplicationName(type, application); + } catch (ExecutionException e) { + s_logger.error("Found an invalid application: " + application + ". Not attempting to clean up."); + continue; + } + + Protocol protocol = (Protocol) applicationComponents[0]; + Integer startPort = (Integer) applicationComponents[1]; + Integer endPort = (Integer) applicationComponents[2]; + manageApplication(type, SrxCommand.DELETE, protocol, startPort, endPort); + } + for (String cidr: cidrs) { + manageAddressBookEntry(SrxCommand.DELETE, _publicZone, cidr, null); + } + + return true; + } + + private boolean addEgressSecurityPolicyAndApplications(SecurityPolicyType type, String guestVlan, List applications, List cidrs) throws ExecutionException { + // Add all necessary applications + List applicationNames = new ArrayList(); + for (Object[] application : applications) { + Protocol protocol = (Protocol) application[0]; + if (!protocol.equals(Protocol.all)) { + int startPort = application[1] != null ? ((Integer) application[1]) : 0; + int endPort = application[2] != null ? ((Integer) application[2]) : 65535; + + String applicationName = genApplicationName(type, protocol, startPort, endPort); + if (!applicationNames.contains(applicationName)) { + applicationNames.add(applicationName); + } + manageApplication(type, SrxCommand.ADD, protocol, startPort, endPort); + } + } + + for (String cidr: cidrs) { + manageAddressBookEntry(SrxCommand.ADD, _privateZone, cidr, null); + } + + // Add a new security policy + manageSecurityPolicy(type, SrxCommand.ADD, null, null, guestVlan, applicationNames, cidrs, null); + s_logger.debug("Added Egress firewall rule for guest network " + guestVlan); + return true; + } + /* * Filter terms */ diff --git a/scripts/network/juniper/application-add.xml b/scripts/network/juniper/application-add.xml index 66038507c44..177329a0359 100644 --- a/scripts/network/juniper/application-add.xml +++ b/scripts/network/juniper/application-add.xml @@ -23,7 +23,7 @@ under the License. %name% %protocol% -%dest-port% +%dest-port-icmp% diff --git a/scripts/network/juniper/security-policy-add.xml b/scripts/network/juniper/security-policy-add.xml index 632a17d6651..595e02680b8 100644 --- a/scripts/network/juniper/security-policy-add.xml +++ b/scripts/network/juniper/security-policy-add.xml @@ -27,8 +27,8 @@ under the License. %policy-name% -%src-address% -%dest-address% +%src-address% +%dst-address% %applications% diff --git a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java index 1fc32d06679..c2038e5a85c 100644 --- a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java @@ -1,3 +1,4 @@ + // 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 @@ -541,8 +542,15 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl if (rule.getSourceCidrList() == null && (rule.getPurpose() == Purpose.Firewall || rule.getPurpose() == Purpose.NetworkACL)) { _fwRulesDao.loadSourceCidrs((FirewallRuleVO)rule); } - IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); - FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr()); + FirewallRuleTO ruleTO; + if (rule.getPurpose() == Purpose.Firewall && rule.getTrafficType() == FirewallRule.TrafficType.Egress) { + String guestVlanTag = network.getBroadcastUri().getHost(); + String guestCidr = network.getCidr(); + ruleTO = new FirewallRuleTO(rule, guestVlanTag, rule.getTrafficType()); + } else { + IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); + ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr()); + } rulesTO.add(ruleTO); } diff --git a/server/src/com/cloud/upgrade/dao/Upgrade410to420.java b/server/src/com/cloud/upgrade/dao/Upgrade410to420.java index f39038fea8a..b43e494893b 100644 --- a/server/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -65,6 +65,7 @@ public class Upgrade410to420 implements DbUpgrade { updateSystemVmTemplates(conn); updateCluster_details(conn); updatePrimaryStore(conn); + addEgressFwRulesForSRXGuestNw(conn); } private void updateSystemVmTemplates(Connection conn) { @@ -305,4 +306,63 @@ public class Upgrade410to420 implements DbUpgrade { } } } + private void addEgressFwRulesForSRXGuestNw(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + ResultSet rsId = null; + ResultSet rsNw = null; + try { + pstmt = conn.prepareStatement("select network_id FROM `cloud`.`ntwk_service_map` where service='Firewall' and provider='JuniperSRX' "); + rs = pstmt.executeQuery(); + while (rs.next()) { + long netId = rs.getLong(1); + //checking for Isolated OR Virtual + pstmt = conn.prepareStatement("select account_id, domain_id FROM `cloud`.`networks` where (guest_type='Isolated' OR guest_type='Virtual') and traffic_type='Guest' and vpc_id is NULL and (state='implemented' OR state='Shutdown') and id=? "); + pstmt.setLong(1, netId); + s_logger.debug("Getting account_id, domain_id from networks table: " + pstmt); + rsNw = pstmt.executeQuery(); + + if(rsNw.next()) { + long accountId = rsNw.getLong(1); + long domainId = rsNw.getLong(2); + + //Add new rule for the existing networks + s_logger.debug("Adding default egress firewall rule for network " + netId); + pstmt = conn.prepareStatement("INSERT INTO firewall_rules (uuid, state, protocol, purpose, account_id, domain_id, network_id, xid, created, traffic_type) VALUES (?, 'Active', 'all', 'Firewall', ?, ?, ?, ?, now(), 'Egress')"); + pstmt.setString(1, UUID.randomUUID().toString()); + pstmt.setLong(2, accountId); + pstmt.setLong(3, domainId); + pstmt.setLong(4, netId); + pstmt.setString(5, UUID.randomUUID().toString()); + s_logger.debug("Inserting default egress firewall rule " + pstmt); + pstmt.executeUpdate(); + + pstmt = conn.prepareStatement("select id from firewall_rules where protocol='all' and network_id=?"); + pstmt.setLong(1, netId); + rsId = pstmt.executeQuery(); + + long firewallRuleId; + if(rsId.next()) { + firewallRuleId = rsId.getLong(1); + pstmt = conn.prepareStatement("insert into firewall_rules_cidrs (firewall_rule_id,source_cidr) values (?, '0.0.0.0/0')"); + pstmt.setLong(1, firewallRuleId); + s_logger.debug("Inserting rule for cidr 0.0.0.0/0 for the new Firewall rule id=" + firewallRuleId + " with statement " + pstmt); + pstmt.executeUpdate(); + } + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to set egress firewall rules ", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } }