From 5a11fe77bae1966c986e541e484ae3d56485d78f Mon Sep 17 00:00:00 2001 From: Min Chen Date: Mon, 26 Aug 2013 15:52:24 -0700 Subject: [PATCH 001/251] CLOUDSTACK-3405: execute.in.sequence.hypervisor.commands and execute.in.sequence.network.element.commands should be set to "true" by default. --- server/src/com/cloud/configuration/Config.java | 8 ++++---- setup/db/db/schema-410to420.sql | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 8955087892f..1cc31b6023e 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -435,10 +435,10 @@ public enum Config { "routes", ConfigurationParameterScope.zone.toString()), InternalLbVmServiceOfferingId("Advanced", ManagementServer.class, String.class, "internallbvm.service.offering", null, "Uuid of the service offering used by internal lb vm; if NULL - default system internal lb offering will be used", null), - ExecuteInSequence("Advanced", ManagementServer.class, Boolean.class, "execute.in.sequence.hypervisor.commands", "false", "If set to true, StartCommand, StopCommand, CopyCommand will be synchronized on the agent side." + - " If set to false, these commands become asynchronous. Default value is false.", null), - ExecuteInSequenceNetworkElementCommands("Advanced", NetworkManager.class, Boolean.class, "execute.in.sequence.network.element.commands", "false", "If set to true, DhcpEntryCommand, SavePasswordCommand, UserDataCommand, VmDataCommand will be synchronized on the agent side." + - " If set to false, these commands become asynchronous. Default value is false.", null), + ExecuteInSequence("Advanced", ManagementServer.class, Boolean.class, "execute.in.sequence.hypervisor.commands", "true", "If set to true, StartCommand, StopCommand, CopyCommand will be synchronized on the agent side." + + " If set to false, these commands become asynchronous. Default value is true.", null), + ExecuteInSequenceNetworkElementCommands("Advanced", NetworkManager.class, Boolean.class, "execute.in.sequence.network.element.commands", "true", "If set to true, DhcpEntryCommand, SavePasswordCommand, UserDataCommand, VmDataCommand will be synchronized on the agent side." + + " If set to false, these commands become asynchronous. Default value is true.", null), UCSSyncBladeInterval("Advanced", ManagementServer.class, Integer.class, "ucs.sync.blade.interval", "3600", "the interval cloudstack sync with UCS manager for available blades in case user remove blades from chassis without notifying CloudStack", null); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 57051ffcc3b..c29fd86e71e 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -2170,8 +2170,8 @@ UPDATE `cloud`.`networks` set name='Shared SG enabled network', display_text='Sh INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'use.system.public.ips', 'true', 'If true, when account has dedicated public ip range(s), once the ips dedicated to the account have been consumed ips will be acquired from the system pool'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'use.system.guest.vlans', 'true', 'If true, when account has dedicated guest vlan range(s), once the vlans dedicated to the account have been consumed vlans will be allocated from the system pool'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'execute.in.sequence.hypervisor.commands', 'false', 'If set to true, StartCommand, StopCommand, CopyCommand will be synchronized on the agent side. If set to false, these commands become asynchronous. Default value is false.'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'execute.in.sequence.network.element.commands', 'false', 'If set to true, DhcpEntryCommand, SavePasswordCommand, UserDataCommand, VmDataCommand will be synchronized on the agent side. If set to false, these commands become asynchronous. Default value is false.'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'execute.in.sequence.hypervisor.commands', 'true', 'If set to true, StartCommand, StopCommand, CopyCommand will be synchronized on the agent side. If set to false, these commands become asynchronous. Default value is true.'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'execute.in.sequence.network.element.commands', 'true', 'If set to true, DhcpEntryCommand, SavePasswordCommand, UserDataCommand, VmDataCommand will be synchronized on the agent side. If set to false, these commands become asynchronous. Default value is true.'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'direct.agent.scan.interval', 90, 'Time interval (in seconds) to run the direct agent scan task.'); From ef7dbf230a3036cc60b3f0f3d8fe24e03342ae16 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 26 Aug 2013 11:53:04 -0700 Subject: [PATCH 002/251] CLOUDSTACK-4508 (WIP): Conditionally make VSM fields required If VSM is active, and either traffic overrides are checked, make VSM fields required. ** This creates new VSM fields, with '_req' appended to end of ID. API calls need to be changes to account for this. --- ui/scripts/system.js | 69 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7fe8882b450..e6bf03a2246 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10284,6 +10284,47 @@ }, createForm: { title: 'label.add.cluster', + preFilter: function(args) { + var $form = args.$form; + + $form.click(function() { + // VSM fields need to be required if a traffic override is selected + // + // ** This is done by switching out optional fields for required fields; + // need to check for *either* vsm[...]_req or vsm[...] + var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); + var $vsmFields = $form.find('.form-item').filter(function() { + var vsmFields = [ + 'vsmipaddress', + 'vsmusername', + 'vsmpassword' + ]; + + return $.inArray($(this).attr('rel'), vsmFields) > -1; + }); + var $vsmReqFields = $form.find('.form-item').filter(function() { + var vsmFields = [ + 'vsmipaddress_req', + 'vsmusername_req', + 'vsmpassword_req' + ]; + + return $.inArray($(this).attr('rel'), vsmFields) > -1; + }); + + + if ($overridePublicTraffic.is(':checked') || + $overrideGuestTraffic.is(':checked')) { + $vsmReqFields.css('display', 'inline-block'); + $vsmFields.hide(); + } else { + $vsmFields.css('display', 'inline-block'); + $vsmReqFields.hide(); + } + + }); + }, fields: { zoneid: { label: 'Zone Name', @@ -10722,14 +10763,28 @@ validation: { required: false }, - isHidden: false + isHidden: true + }, + vsmipaddress_req: { + label: 'Nexus 1000v IP Address', + validation: { + required: true + }, + isHidden: true }, vsmusername: { label: 'Nexus 1000v Username', validation: { required: false }, - isHidden: false + isHidden: true + }, + vsmusername_req: { + label: 'Nexus 1000v Username', + validation: { + required: true + }, + isHidden: true }, vsmpassword: { label: 'Nexus 1000v Password', @@ -10737,7 +10792,15 @@ required: false }, isPassword: true, - isHidden: false + isHidden: true + }, + vsmpassword_req: { + label: 'Nexus 1000v Password', + validation: { + required: true + }, + isPassword: true, + isHidden: true } //hypervisor==VMWare ends here } From 202e57a772017488293e0fcf183ae762439dbaaa Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 26 Aug 2013 15:59:18 -0700 Subject: [PATCH 003/251] CLOUDSTACK-4508: UI > Infrastructure > clusters > add cluster dialog > make NexusVSM fields required only when Override Traffic is checked AND vSwitch Type is "Cisco Nexus 1000v Distributed Virtual Switch". --- ui/scripts/system.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index e6bf03a2246..07b0bb8fcd3 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10292,8 +10292,13 @@ // // ** This is done by switching out optional fields for required fields; // need to check for *either* vsm[...]_req or vsm[...] - var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); + var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); + + var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); + var $vsmFields = $form.find('.form-item').filter(function() { var vsmFields = [ 'vsmipaddress', @@ -10314,8 +10319,8 @@ }); - if ($overridePublicTraffic.is(':checked') || - $overrideGuestTraffic.is(':checked')) { + if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || + ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { $vsmReqFields.css('display', 'inline-block'); $vsmFields.hide(); } else { From 9fc905383d132d0bb3abfd22109123df7d4355ec Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 26 Aug 2013 16:35:04 -0700 Subject: [PATCH 004/251] CLOUDSTACK-4508: UI > Infrastructure > clusters > add cluster dialog > VSM fields > pass only value of visible VSM fields to API call. --- ui/scripts/system.js | 74 ++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 07b0bb8fcd3..2d8bc055096 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10429,31 +10429,16 @@ return $.inArray($(this).attr('rel'), vsmFields) > -1; }); - if ($(this).val() == "VMware") { - //$('li[input_sub_group="external"]', $dialogAddCluster).show(); - - if (dvSwitchEnabled) { - // $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'inline-block'); - // $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'inline-block'); - // $form.find('.form-item[rel=vSwitchPublicName]').css('display','inline-block'); - //$form.find('.form-item[rel=vSwitchGuestName]').css('display','inline-block'); + if ($(this).val() == "VMware") { + if (dvSwitchEnabled) { $form.find('.form-item[rel=overridepublictraffic]').css('display', 'inline-block'); $form.find('.form-item[rel=overridepublictraffic]').find('input[type=checkbox]').removeAttr('checked'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); $form.find('.form-item[rel=overrideguesttraffic]').find('input[type=checkbox]').removeAttr('checked'); - - - - } else { - // $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); - // $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); - // $form.find('.form-item[rel=vSwitchPublicName]').css('display','none'); - // $form.find('.form-item[rel=vSwitchGuestName]').css('display','none'); + } else { $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); - - } $form.find('.form-item[rel=vCenterHost]').css('display', 'inline-block'); $form.find('.form-item[rel=vCenterUsername]').css('display', 'inline-block'); @@ -10465,9 +10450,8 @@ } else { $vsmFields.css('display', 'none'); } + } else { - - $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); @@ -10475,7 +10459,6 @@ $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); - $form.find('.form-item[rel=vCenterHost]').css('display', 'none'); $form.find('.form-item[rel=vCenterUsername]').css('display', 'none'); $form.find('.form-item[rel=vCenterPassword]').css('display', 'none'); @@ -10561,8 +10544,6 @@ data: items }); } - - }); } }, @@ -10575,11 +10556,9 @@ validation: { required: false } - }, //hypervisor==VMWare begins here - vCenterHost: { label: 'label.vcenter.host', docID: 'helpClustervCenterHost', @@ -10610,7 +10589,6 @@ isHidden: true, isChecked: false, docID: 'helpOverridePublicNetwork' - }, @@ -10633,7 +10611,6 @@ }); if (vSwitchEnabled) { - items.push({ id: "nexusdvs", description: "Cisco Nexus 1000v Distributed Virtual Switch" @@ -10646,13 +10623,7 @@ id: "vmwaredvs", description: "VMware vNetwork Distributed Virtual Switch" }); - - - - } - - // items.push({id: "" , description:" " }); else { items.push({ id: "vmwaredvs", @@ -10680,8 +10651,6 @@ label: 'Public Traffic vSwitch Name', dependsOn: 'overridepublictraffic', isHidden: true - - }, overrideguesttraffic: { @@ -10690,7 +10659,6 @@ isHidden: true, isChecked: false, docID: 'helpOverrideGuestNetwork' - }, @@ -10698,8 +10666,7 @@ label: 'Guest Traffic vSwitch Type', select: function(args) { var items = [] - // items.push({id: "" , description:" " }); - + var vSwitchEnabled = false; $.ajax({ url: createURL('listConfigurations'), @@ -10742,24 +10709,20 @@ id: "nexusdvs", description: "Cisco Nexus 1000v Distributed Virtual Switch" }); - - } + args.response.success({ data: items }); }, isHidden: true, dependsOn: 'overrideguesttraffic' - }, vSwitchGuestName: { label: ' Guest Traffic vSwitch Name', dependsOn: 'overrideguesttraffic', isHidden: true - - }, @@ -10846,18 +10809,29 @@ if (args.data.vSwitchGuestName != "") array1.push("&guestvswitchname=" + args.data.vSwitchGuestName); - if (args.data.vsmipaddress != null && args.data.vsmipaddress.length > 0) { + //Nexus VSM fields + if (args.$form.find('.form-item[rel=vsmipaddress]').css('display') != 'none' && args.data.vsmipaddress != null && args.data.vsmipaddress.length > 0) { array1.push('&vsmipaddress=' + args.data.vsmipaddress); } - - if(args.data.vsmusername != null && args.data.vsmusername.length > 0) { - array1.push('&vsmusername=' + args.data.vsmusername); + if (args.$form.find('.form-item[rel=vsmipaddress_req]').css('display') != 'none' && args.data.vsmipaddress_req != null && args.data.vsmipaddress_req.length > 0) { + array1.push('&vsmipaddress=' + args.data.vsmipaddress_req); } - if(args.data.vsmpassword != null && args.data.vsmpassword.length > 0) { + if(args.$form.find('.form-item[rel=vsmusername]').css('display') != 'none' && args.data.vsmusername != null && args.data.vsmusername.length > 0) { + array1.push('&vsmusername=' + args.data.vsmusername); + } + if(args.$form.find('.form-item[rel=vsmusername_req]').css('display') != 'none' && args.data.vsmusername_req != null && args.data.vsmusername_req.length > 0) { + array1.push('&vsmusername=' + args.data.vsmusername_req); + } + + if(args.$form.find('.form-item[rel=vsmpassword]').css('display') != 'none' && args.data.vsmpassword != null && args.data.vsmpassword.length > 0) { array1.push('&vsmpassword=' + args.data.vsmpassword); - } - + } + if(args.$form.find('.form-item[rel=vsmpassword_req]').css('display') != 'none' && args.data.vsmpassword_req != null && args.data.vsmpassword_req.length > 0) { + array1.push('&vsmpassword=' + args.data.vsmpassword_req); + } + + var hostname = args.data.vCenterHost; var dcName = args.data.vCenterDatacenter; From 5b93398c120edcb8591117cd460020ad3963971e Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 26 Aug 2013 17:02:24 -0700 Subject: [PATCH 005/251] CLOUDSTACK-4508: UI > Infrastructure > clusters > add cluster dialog > hide both required NexusVSM fields and optional NexusVSM fields when hypervisor is not VMware. --- ui/scripts/system.js | 60 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 2d8bc055096..63e316305f9 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10287,25 +10287,13 @@ preFilter: function(args) { var $form = args.$form; - $form.click(function() { - // VSM fields need to be required if a traffic override is selected - // - // ** This is done by switching out optional fields for required fields; - // need to check for *either* vsm[...]_req or vsm[...] - - var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); - var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); - - var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); - var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); - + $form.click(function() { var $vsmFields = $form.find('.form-item').filter(function() { var vsmFields = [ 'vsmipaddress', 'vsmusername', 'vsmpassword' - ]; - + ]; return $.inArray($(this).attr('rel'), vsmFields) > -1; }); var $vsmReqFields = $form.find('.form-item').filter(function() { @@ -10313,21 +10301,33 @@ 'vsmipaddress_req', 'vsmusername_req', 'vsmpassword_req' - ]; - + ]; return $.inArray($(this).attr('rel'), vsmFields) > -1; - }); - - - if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || - ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { - $vsmReqFields.css('display', 'inline-block'); - $vsmFields.hide(); + }); + + if ($form.find('.form-item[rel=hypervisor] select').val() == 'VMware' ) { + // VSM fields need to be required if a traffic override is selected and vSwitchType is 'nexusdvs'. + // This is done by switching out optional fields for required fields; + var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); + var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); + + var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); + + + if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || + ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { + $vsmReqFields.css('display', 'inline-block'); + $vsmFields.hide(); + } else { + $vsmFields.css('display', 'inline-block'); + $vsmReqFields.hide(); + } } else { - $vsmFields.css('display', 'inline-block'); - $vsmReqFields.hide(); + $vsmFields.hide(); + $vsmReqFields.hide(); } - + }); }, fields: { @@ -10419,15 +10419,17 @@ args.$select.bind("change", function(event) { var $form = $(this).closest('form'); + + /* var $vsmFields = $form.find('.form-item').filter(function() { var vsmFields = [ 'vsmipaddress', 'vsmusername', 'vsmpassword' ]; - return $.inArray($(this).attr('rel'), vsmFields) > -1; }); + */ if ($(this).val() == "VMware") { if (dvSwitchEnabled) { @@ -10445,11 +10447,13 @@ $form.find('.form-item[rel=vCenterPassword]').css('display', 'inline-block'); $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'inline-block'); + /* if (vSwitchEnabled) { $vsmFields.css('display', 'inline-block'); } else { $vsmFields.css('display', 'none'); } + */ } else { $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); @@ -10464,7 +10468,7 @@ $form.find('.form-item[rel=vCenterPassword]').css('display', 'none'); $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'none'); $form.find('.form-item[rel=enableNexusVswitch]').css('display', 'none'); - $vsmFields.css('display', 'none'); + //$vsmFields.css('display', 'none'); } }); } From 697cc2e397a8d6ff0d293587877dfb3033d87c26 Mon Sep 17 00:00:00 2001 From: frank Date: Mon, 26 Aug 2013 17:53:06 -0700 Subject: [PATCH 006/251] CloudStack CLOUDSTACK-4484 Vmware - Not able to fetch userdata from guest Vms using http:///latest/user-data --- server/src/com/cloud/api/ApiServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index ca6f4b46806..98c460af7c8 100755 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -79,7 +79,7 @@ public class ApiServlet extends HttpServlet { String[] paramsInQueryString = req.getQueryString().split("&"); if (paramsInQueryString != null) { for (String param : paramsInQueryString) { - String[] paramTokens = param.split("="); + String[] paramTokens = param.split("=", 2); if (paramTokens != null && paramTokens.length == 2) { String name = paramTokens[0]; String value = paramTokens[1]; From 816772a001f48f0b8f79a89242de2bc38aef8ca4 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 26 Aug 2013 18:07:18 -0700 Subject: [PATCH 007/251] CLOUDSTACK-4459: Libvirt reports: org.libvirt.LibvirtException: Storage volume not found: no storage vol with matching name in some cases, if the volume is created on one kvm host, while accessed from other host. It's possible due to concurrent access(read/write) storage. The current fix is to try serveral times, and wait for 30 seconds for each retry. If the issue still there, then need to sync the storage pool access --- .../kvm/storage/KVMStoragePoolManager.java | 8 ++++++-- .../kvm/storage/LibvirtStorageAdaptor.java | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 7fef6fffb51..e09c9ba44da 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -97,6 +97,7 @@ public class KVMStoragePoolManager { } public KVMStoragePool getStoragePool(StoragePoolType type, String uuid) { + StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = null; try { @@ -149,12 +150,15 @@ public class KVMStoragePoolManager { if (vol != null) { break; } - - Thread.sleep(10000); } catch (Exception e) { s_logger.debug("Failed to find volume:" + volName + " due to" + e.toString() + ", retry:" + cnt); errMsg = e.toString(); } + + try { + Thread.sleep(30000); + } catch (InterruptedException e) { + } cnt++; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 4a61f70fca0..123a9f10ffb 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -101,10 +101,15 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { try { vol = pool.storageVolLookupByName(volName); } catch (LibvirtException e) { - + s_logger.debug("Can't find volume: " + e.toString()); } if (vol == null) { - storagePoolRefresh(pool); + try { + refreshPool(pool); + } catch (LibvirtException e) { + s_logger.debug("failed to refresh pool: " + e.toString()); + } + try { vol = pool.storageVolLookupByName(volName); } catch (LibvirtException e) { @@ -119,6 +124,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { LibvirtStorageVolumeDef volDef = new LibvirtStorageVolumeDef(UUID .randomUUID().toString(), size, format, null, null); s_logger.debug(volDef.toString()); + return pool.storageVolCreateXML(volDef.toString(), 0); } @@ -128,7 +134,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { refreshPool(pool); } } catch (LibvirtException e) { - + s_logger.debug("refresh storage pool failed: " + e.toString()); } } @@ -438,6 +444,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } return disk; } catch (LibvirtException e) { + s_logger.debug("Failed to get physical disk:", e); throw new CloudRuntimeException(e.toString()); } From 046a8a889a3338ff86914b42eded316596ab5e6a Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Mon, 26 Aug 2013 17:45:54 -0700 Subject: [PATCH 008/251] CLOUDSTACK-3237: add disk chain sync logic to handle out-of-band chain changes that could happen in storage live migration and VM snapshot operations --- api/src/com/cloud/agent/api/to/VolumeTO.java | 4 + .../vmware/resource/VmwareResource.java | 701 +++++++++++------- .../src/com/cloud/storage/VolumeManager.java | 2 + .../com/cloud/storage/VolumeManagerImpl.java | 18 + .../cloud/vm/VirtualMachineManagerImpl.java | 17 + .../vmware/mo/VirtualMachineDiskInfo.java | 42 ++ .../mo/VirtualMachineDiskInfoBuilder.java | 97 +++ .../vmware/mo/VirtualMachineMO.java | 27 +- 8 files changed, 657 insertions(+), 251 deletions(-) create mode 100644 vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java create mode 100644 vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java index a5681a003dd..fb08384187e 100644 --- a/api/src/com/cloud/agent/api/to/VolumeTO.java +++ b/api/src/com/cloud/agent/api/to/VolumeTO.java @@ -126,6 +126,10 @@ public class VolumeTO implements InternalIdentity { public String getChainInfo() { return chainInfo; } + + public void setChainInfo(String chainInfo) { + this.chainInfo = chainInfo; + } public String getOsType() { return guestOsType; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 87e2e931384..b612ac73ac2 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -213,6 +213,7 @@ import com.cloud.hypervisor.vmware.mo.ClusterMO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO; import com.cloud.hypervisor.vmware.mo.DatacenterMO; +import com.cloud.hypervisor.vmware.mo.DatastoreFile; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.FeatureKeyConstants; @@ -223,6 +224,8 @@ import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; import com.cloud.hypervisor.vmware.mo.NetworkDetails; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; +import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfo; +import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VirtualSwitchType; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; @@ -282,8 +285,6 @@ import com.vmware.vim25.DistributedVirtualSwitchPortCriteria; import com.vmware.vim25.DynamicProperty; import com.vmware.vim25.GuestInfo; import com.vmware.vim25.HostCapability; -import com.vmware.vim25.HostFirewallInfo; -import com.vmware.vim25.HostFirewallRuleset; import com.vmware.vim25.HostHostBusAdapter; import com.vmware.vim25.HostInternetScsiHba; import com.vmware.vim25.HostInternetScsiHbaAuthenticationProperties; @@ -1131,11 +1132,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.error("Unexpected exception: " + e.toString(), e); return new Answer(cmd, false, "VPCLoadBalancerConfigCommand failed due to " + VmwareHelper.getExceptionMessage(e)); } - - } - protected Answer execute(final LoadBalancerConfigCommand cmd) { if ( cmd.getVpcId() != null ) { @@ -1834,7 +1832,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (!result_gateway.first()) { throw new InternalErrorException("Unable to configure source NAT for public IP address."); } - } } @@ -2334,6 +2331,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } return new CheckS2SVpnConnectionsAnswer(cmd, true, result.second()); } + protected Answer execute(CheckRouterCommand cmd) { if (s_logger.isDebugEnabled()) { s_logger.debug("Executing resource CheckRouterCommand: " + _gson.toJson(cmd)); @@ -2562,6 +2560,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return validatedDisks.toArray(new DiskTO[0]); } + + private static DiskTO getIsoDiskTO(DiskTO[] disks) { + for (DiskTO vol : disks) { + if (vol.getType() == Volume.Type.ISO) { + return vol; + } + } + return null; + } protected ScaleVmAnswer execute(ScaleVmCommand cmd) { @@ -2611,23 +2618,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } protected StartAnswer execute(StartCommand cmd) { - if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource StartCommand: " + _gson.toJson(cmd)); } - + VirtualMachineTO vmSpec = cmd.getVirtualMachine(); - String vmInternalCSName = null; - String vmNameOnVcenter = null; - if (vmSpec.getHostName() != null) { - vmInternalCSName = vmSpec.getName(); - if (_instanceNameFlag == true) - vmNameOnVcenter = vmSpec.getHostName(); - else - vmNameOnVcenter = vmSpec.getName(); - } else { - vmNameOnVcenter = vmInternalCSName = vmSpec.getName(); - } + + Pair names = composeVmNames(vmSpec); + String vmInternalCSName = names.first(); + String vmNameOnVcenter = names.second(); + // Thus, vmInternalCSName always holds i-x-y, the cloudstack generated internal VM name. State state = State.Stopped; VmwareContext context = getServiceContext(); @@ -2639,10 +2639,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa _vms.put(vmInternalCSName, State.Starting); } - VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.valueOf(vmSpec.getDetails().get(VmDetailConstants.NIC_ADAPTER)); - if(s_logger.isDebugEnabled()) - s_logger.debug("VM " + vmInternalCSName + " will be started with NIC device type: " + nicDeviceType); - VmwareHypervisorHost hyperHost = getHyperHost(context); DiskTO[] disks = validateDisks(vmSpec.getDisks()); assert (disks.length > 0); @@ -2663,12 +2659,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } DatacenterMO dcMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter()); - + VirtualMachineDiskInfoBuilder diskInfoBuilder = null; VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName); if (vmMo != null) { s_logger.info("VM " + vmInternalCSName + " already exists, tear down devices for reconfiguration"); if (getVmState(vmMo) != State.Stopped) vmMo.safePowerOff(_shutdown_waitMs); + + // retrieve disk information before we tear down + diskInfoBuilder = vmMo.getDiskInfoBuilder(); vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); vmMo.ensureScsiDeviceController(); } else { @@ -2685,6 +2684,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (getVmState(vmMo) != State.Stopped) vmMo.safePowerOff(_shutdown_waitMs); + + diskInfoBuilder = vmMo.getDiskInfoBuilder(); vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); vmMo.ensureScsiDeviceController(); } else { @@ -2698,6 +2699,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } assert (vmSpec.getMinSpeed() != null) && (rootDiskDataStoreDetails != null); + + if(rootDiskDataStoreDetails.second().folderExists(String.format("[%s]", rootDiskDataStoreDetails.second().getName()), vmNameOnVcenter)) { + s_logger.warn("WARN!!! Folder already exists on datastore for new VM " + vmNameOnVcenter + ", erase it"); + rootDiskDataStoreDetails.second().deleteFile(String.format("[%s] %s/", rootDiskDataStoreDetails.second().getName(), + vmNameOnVcenter), dcMo.getMor(), false); + } + if (!hyperHost.createBlankVm(vmNameOnVcenter, vmInternalCSName, vmSpec.getCpus(), vmSpec.getMaxSpeed().intValue(), vmSpec.getMinSpeed(), vmSpec.getLimitCpuUse(),(int)(vmSpec.getMaxRam()/(1024*1024)), ramMb, translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(), rootDiskDataStoreDetails.first(), false)) { @@ -2712,63 +2720,43 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } int totalChangeDevices = disks.length + nics.length; + DiskTO volIso = null; if (vmSpec.getType() != VirtualMachine.Type.User) { // system VM needs a patch ISO totalChangeDevices++; } else { - for (DiskTO vol : disks) { - if (vol.getType() == Volume.Type.ISO) { - volIso = vol; - break; - } - } - + volIso = getIsoDiskTO(disks); if (volIso == null) totalChangeDevices++; } VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); int ramMb = (int) (vmSpec.getMinRam() / (1024 * 1024)); + String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(); + VmwareHelper.setBasicVmConfig(vmConfigSpec, vmSpec.getCpus(), vmSpec.getMaxSpeed(), vmSpec.getMinSpeed(),(int) (vmSpec.getMaxRam()/(1024*1024)), ramMb, - translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(), vmSpec.getLimitCpuUse()); - String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(); + guestOsId, vmSpec.getLimitCpuUse()); + // Check for hotadd settings vmConfigSpec.setMemoryHotAddEnabled(vmMo.isMemoryHotAddSupported(guestOsId)); vmConfigSpec.setCpuHotAddEnabled(vmMo.isCpuHotAddSupported(guestOsId)); - - if ("true".equals(vmSpec.getDetails().get(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG))) { - s_logger.debug("Nested Virtualization enabled in configuration, checking hypervisor capability"); - ManagedObjectReference hostMor = vmMo.getRunningHost().getMor(); - ManagedObjectReference computeMor = context.getVimClient().getMoRefProp(hostMor, "parent"); - ManagedObjectReference environmentBrowser = - context.getVimClient().getMoRefProp(computeMor, "environmentBrowser"); - HostCapability hostCapability = context.getService().queryTargetCapabilities(environmentBrowser, hostMor); - Boolean nestedHvSupported = hostCapability.isNestedHVSupported(); - if (nestedHvSupported == null) { - // nestedHvEnabled property is supported only since VMware 5.1. It's not defined for earlier versions. - s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName()); - } else if (nestedHvSupported.booleanValue()) { - s_logger.debug("Hypervisor supports nested virtualization, enabling for VM " + vmSpec.getName()); - vmConfigSpec.setNestedHVEnabled(true); - } - else { - s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName()); - vmConfigSpec.setNestedHVEnabled(false); - } - } + configNestedHVSupport(vmMo, vmSpec, vmConfigSpec); VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[totalChangeDevices]; int i = 0; int ideUnitNumber = 0; - int scsiUnitNumber =0; + int scsiUnitNumber = 0; int nicUnitNumber = 0; int ideControllerKey = vmMo.getIDEDeviceControllerKey(); int scsiControllerKey = vmMo.getScsiDeviceControllerKey(); int controllerKey; - String datastoreDiskPath; + // + // Setup ISO device + // + // prepare systemvm patch ISO if (vmSpec.getType() != VirtualMachine.Type.User) { // attach ISO (for patching of system VM) @@ -2800,8 +2788,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); } } else { - // we will always plugin a CDROM device - + // Note: we will always plug a CDROM device if (volIso != null) { TemplateObjectTO iso = (TemplateObjectTO)volIso.getData(); @@ -2847,55 +2834,58 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } } + i++; - for (DiskTO vol : sortVolumesByDeviceId(disks)) { + + // + // Setup ROOT/DATA disk devices + // + DiskTO[] sortedDisks = sortVolumesByDeviceId(disks); + for (DiskTO vol : sortedDisks) { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - if (vol.getType() == Volume.Type.ISO) { - controllerKey = ideControllerKey; - } else { - if(vol.getType() == Volume.Type.ROOT) { - if(vmSpec.getDetails() != null && vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null) - { - if(vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi")) - controllerKey = scsiControllerKey; - else - controllerKey = ideControllerKey; - } else { - controllerKey = scsiControllerKey; - } - } else { - // DATA volume always use SCSI device - controllerKey = scsiControllerKey; - } - } + if (vol.getType() == Volume.Type.ISO) + continue; + + controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); - if (vol.getType() != Volume.Type.ISO) { - VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); - Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); - assert (volumeDsDetails != null); - VirtualDevice device; - - datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dcMo, vmNameOnVcenter, volumeDsDetails.second(), - volumeTO.getPath()); - device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(), - (controllerKey==ideControllerKey)?ideUnitNumber++:scsiUnitNumber++, i + 1); - - deviceConfigSpecArray[i].setDevice(device); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); + assert (volumeDsDetails != null); + VirtualDevice device; + + String[] diskChain = syncDiskChain(dcMo, vmMo, vmSpec, + vol, diskInfoBuilder, + dataStoresDetails, + (controllerKey == ideControllerKey) ? true : false, + 0, // currently only support bus 0 + (controllerKey == ideControllerKey) ? ideUnitNumber : scsiUnitNumber); + + device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, + diskChain, + volumeDsDetails.first(), + (controllerKey == ideControllerKey) ? ideUnitNumber++ : scsiUnitNumber++, i + 1); + + deviceConfigSpecArray[i].setDevice(device); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + if(s_logger.isDebugEnabled()) + s_logger.debug("Prepare volume at new device " + _gson.toJson(device)); - if(s_logger.isDebugEnabled()) - s_logger.debug("Prepare volume at new device " + _gson.toJson(device)); - - i++; - } + i++; } + // + // Setup NIC devices + // VirtualDevice nic; int nicMask = 0; int nicCount = 0; + VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.valueOf(vmSpec.getDetails().get(VmDetailConstants.NIC_ADAPTER)); + if(s_logger.isDebugEnabled()) + s_logger.debug("VM " + vmInternalCSName + " will be started with NIC device type: " + nicDeviceType); + for (NicTO nicTo : sortNicsByDeviceId(nics)) { s_logger.info("Prepare NIC device based on NicTO: " + _gson.toJson(nicTo)); @@ -2932,170 +2922,47 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa vmConfigSpec.getDeviceChange().addAll(Arrays.asList(deviceConfigSpecArray)); + // + // Setup VM options + // + // pass boot arguments through machine.id & perform customized options to VMX - ArrayList extraOptions = new ArrayList(); - OptionValue newVal = new OptionValue(); - newVal.setKey("machine.id"); - newVal.setValue(vmSpec.getBootArgs()); - extraOptions.add(newVal); - - newVal = new OptionValue(); - newVal.setKey("devices.hotplug"); - newVal.setValue("true"); - extraOptions.add(newVal); - - /** - * Extra Config : nvp.vm-uuid = uuid - * - Required for Nicira NVP integration - */ - newVal = new OptionValue(); - newVal.setKey("nvp.vm-uuid"); - newVal.setValue(vmSpec.getUuid()); - extraOptions.add(newVal); - - /** - * Extra Config : nvp.iface-id. = uuid - * - Required for Nicira NVP integration - */ - int nicNum = 0; - for (NicTO nicTo : sortNicsByDeviceId(nics)) { - if (nicTo.getUuid() != null) { - newVal = new OptionValue(); - newVal.setKey("nvp.iface-id." + nicNum); - newVal.setValue(nicTo.getUuid()); - extraOptions.add(newVal); - } - nicNum++; - } - - for(Map.Entry entry : validateVmDetails(vmSpec.getDetails()).entrySet()) { - newVal = new OptionValue(); - newVal.setKey(entry.getKey()); - newVal.setValue(entry.getValue()); - extraOptions.add(newVal); - } + configBasicExtraOption(extraOptions, vmSpec); + configNvpExtraOption(extraOptions, vmSpec); + configCustomExtraOption(extraOptions, vmSpec); + // config VNC String keyboardLayout = null; if(vmSpec.getDetails() != null) keyboardLayout = vmSpec.getDetails().get(VmDetailConstants.KEYBOARD); - vmConfigSpec.getExtraConfig().addAll(Arrays.asList(configureVnc(extraOptions.toArray(new OptionValue[0]), hyperHost, vmInternalCSName, vmSpec.getVncPassword(), keyboardLayout))); + vmConfigSpec.getExtraConfig().addAll( + Arrays.asList( + configureVnc( + extraOptions.toArray(new OptionValue[0]), + hyperHost, vmInternalCSName, vmSpec.getVncPassword(), keyboardLayout + ) + ) + ); + // + // Configure VM + // if (!vmMo.configureVm(vmConfigSpec)) { throw new Exception("Failed to configure VM before start. vmName: " + vmInternalCSName); } + // + // Post Configuration + // + vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMask)); - - /** - * We need to configure the port on the DV switch after the host is - * connected. So make this happen between the configure and start of - * the VM - */ - int nicIndex = 0; - for (NicTO nicTo : sortNicsByDeviceId(nics)) { - if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) { - // We need to create a port with a unique vlan and pass the key to the nic device - s_logger.trace("Nic " + nicTo.toString() + " is connected to an NVP logicalswitch"); - VirtualDevice nicVirtualDevice = vmMo.getNicDeviceByIndex(nicIndex); - if (nicVirtualDevice == null) { - throw new Exception("Failed to find a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad - } - VirtualDeviceBackingInfo backing = nicVirtualDevice.getBacking(); - if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) { - // This NIC is connected to a Distributed Virtual Switch - VirtualEthernetCardDistributedVirtualPortBackingInfo portInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing; - DistributedVirtualSwitchPortConnection port = portInfo.getPort(); - String portKey = port.getPortKey(); - String portGroupKey = port.getPortgroupKey(); - String dvSwitchUuid = port.getSwitchUuid(); - - s_logger.debug("NIC " + nicTo.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey); - - ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager(); - ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid); - - // Get all ports - DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria(); - criteria.setInside(true); - criteria.getPortgroupKey().add(portGroupKey); - List dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria); - - DistributedVirtualPort vmDvPort = null; - List usedVlans = new ArrayList(); - for (DistributedVirtualPort dvPort : dvPorts) { - // Find the port for this NIC by portkey - if (portKey.equals(dvPort.getKey())) { - vmDvPort = dvPort; - } - VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort - .getConfig().getSetting(); - VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings - .getVlan(); - s_logger.trace("Found port " + dvPort.getKey() - + " with vlan " + vlanId.getVlanId()); - if (vlanId.getVlanId() > 0 - && vlanId.getVlanId() < 4095) { - usedVlans.add(vlanId.getVlanId()); - } - } - - if (vmDvPort == null) { - throw new Exception("Empty port list from dvSwitch for nic " + nicTo.toString()); - } - - DVPortConfigInfo dvPortConfigInfo = vmDvPort - .getConfig(); - VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPortConfigInfo.getSetting(); - - VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan(); - BoolPolicy blocked = settings.getBlocked(); - if (blocked.isValue() == Boolean.TRUE) { - s_logger.trace("Port is blocked, set a vlanid and unblock"); - DVPortConfigSpec dvPortConfigSpec = new DVPortConfigSpec(); - VMwareDVSPortSetting edittedSettings = new VMwareDVSPortSetting(); - // Unblock - blocked.setValue(Boolean.FALSE); - blocked.setInherited(Boolean.FALSE); - edittedSettings.setBlocked(blocked); - // Set vlan - for (i = 1; i < 4095; i++) { - if (!usedVlans.contains(i)) - break; - } - vlanId.setVlanId(i); // FIXME should be a determined - // based on usage - vlanId.setInherited(false); - edittedSettings.setVlan(vlanId); - - dvPortConfigSpec.setSetting(edittedSettings); - dvPortConfigSpec.setOperation("edit"); - dvPortConfigSpec.setKey(portKey); - List dvPortConfigSpecs = new ArrayList(); - dvPortConfigSpecs.add(dvPortConfigSpec); - ManagedObjectReference task = vmMo.getContext().getVimClient().getService().reconfigureDVPortTask(dvSwitch, dvPortConfigSpecs); - if (!vmMo.getContext().getVimClient().waitForTask(task)) { - throw new Exception( - "Failed to configure the dvSwitch port for nic " - + nicTo.toString()); - } - s_logger.debug("NIC " + nicTo.toString() - + " connected to vlan " + i); - } else { - s_logger.trace("Port already configured and set to vlan " + vlanId.getVlanId()); - } - } else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) { - // This NIC is connected to a Virtual Switch - // Nothing to do - } - else { - s_logger.error("nic device backing is of type " + backing.getClass().getName()); - throw new Exception("Incompatible backing for a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad - } - } - nicIndex++; - } - + postNvpConfigBeforeStart(vmMo, vmSpec); + postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, ideControllerKey, scsiControllerKey); + + // + // Power-on VM + // if (!vmMo.powerOn()) { throw new Exception("Failed to start VM. vmName: " + vmInternalCSName + " with hostname " + vmNameOnVcenter); } @@ -3121,7 +2988,340 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } } + + // return the finalized disk chain for startup, from top to bottom + private String[] syncDiskChain(DatacenterMO dcMo, VirtualMachineMO vmMo, VirtualMachineTO vmSpec, + DiskTO vol, VirtualMachineDiskInfoBuilder diskInfoBuilder, + HashMap> dataStoresDetails, + boolean ideController, int deviceBusNumber, int deviceUnitNumber) throws Exception { + + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + + String deviceBusName; + if(ideController) + deviceBusName = String.format("ide%d:%d", deviceBusNumber, deviceUnitNumber); + else + deviceBusName = String.format("scsi%d:%d", deviceBusNumber, deviceUnitNumber); + + Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); + if(volumeDsDetails == null) + throw new Exception("Primary datastore " + primaryStore.getUuid() + " is not mounted on host."); + DatastoreMO dsMo = volumeDsDetails.second(); + String datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( + dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); + + if(!dsMo.fileExists(datastoreDiskPath)) { + if(s_logger.isInfoEnabled()) + s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath); + + if(diskInfoBuilder != null && diskInfoBuilder.getDiskCount() > 0) { + // we will always on-disk info from vCenter in this case + VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); + if(diskInfo != null) { + if(s_logger.isInfoEnabled()) + s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore. use on-disk chain: " + + _gson.toJson(diskInfo)); + + return diskInfo.getDiskChain(); + } else { + s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore. on-disk may be out of sync as well. disk device info: " + deviceBusName); + } + } + + // last resort, try chain info stored in DB + if(volumeTO.getChainInfo() != null) { + VirtualMachineDiskInfo diskInfo = _gson.fromJson(volumeTO.getChainInfo(), VirtualMachineDiskInfo.class); + if(diskInfo != null) { + s_logger.info("Use chain info from DB: " + volumeTO.getChainInfo()); + return diskInfo.getDiskChain(); + } + + throw new Exception("Volume " + volumeTO.getId() + " does not seem to exist on datastore. Broken disk chain"); + } + } + + return new String[] { datastoreDiskPath }; + } + + // Pair + private Pair composeVmNames(VirtualMachineTO vmSpec) { + String vmInternalCSName = null; + String vmNameOnVcenter = null; + if (vmSpec.getHostName() != null) { + vmInternalCSName = vmSpec.getName(); + if (_instanceNameFlag == true) + vmNameOnVcenter = vmSpec.getHostName(); + else + vmNameOnVcenter = vmSpec.getName(); + } else { + vmNameOnVcenter = vmInternalCSName = vmSpec.getName(); + } + + return new Pair(vmInternalCSName, vmNameOnVcenter); + } + + private static void configNestedHVSupport(VirtualMachineMO vmMo, + VirtualMachineTO vmSpec, VirtualMachineConfigSpec vmConfigSpec) throws Exception { + + VmwareContext context = vmMo.getContext(); + if ("true".equals(vmSpec.getDetails().get(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG))) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Nested Virtualization enabled in configuration, checking hypervisor capability"); + + ManagedObjectReference hostMor = vmMo.getRunningHost().getMor(); + ManagedObjectReference computeMor = context.getVimClient().getMoRefProp(hostMor, "parent"); + ManagedObjectReference environmentBrowser = context.getVimClient().getMoRefProp(computeMor, "environmentBrowser"); + HostCapability hostCapability = context.getService().queryTargetCapabilities(environmentBrowser, hostMor); + Boolean nestedHvSupported = hostCapability.isNestedHVSupported(); + if (nestedHvSupported == null) { + // nestedHvEnabled property is supported only since VMware 5.1. It's not defined for earlier versions. + s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName()); + } else if (nestedHvSupported.booleanValue()) { + s_logger.debug("Hypervisor supports nested virtualization, enabling for VM " + vmSpec.getName()); + vmConfigSpec.setNestedHVEnabled(true); + } + else { + s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName()); + vmConfigSpec.setNestedHVEnabled(false); + } + } + } + + private static void configBasicExtraOption(List extraOptions, VirtualMachineTO vmSpec) { + OptionValue newVal = new OptionValue(); + newVal.setKey("machine.id"); + newVal.setValue(vmSpec.getBootArgs()); + extraOptions.add(newVal); + + newVal = new OptionValue(); + newVal.setKey("devices.hotplug"); + newVal.setValue("true"); + extraOptions.add(newVal); + } + + private static void configNvpExtraOption(List extraOptions, VirtualMachineTO vmSpec) { + /** + * Extra Config : nvp.vm-uuid = uuid + * - Required for Nicira NVP integration + */ + OptionValue newVal = new OptionValue(); + newVal.setKey("nvp.vm-uuid"); + newVal.setValue(vmSpec.getUuid()); + extraOptions.add(newVal); + + /** + * Extra Config : nvp.iface-id. = uuid + * - Required for Nicira NVP integration + */ + int nicNum = 0; + for (NicTO nicTo : sortNicsByDeviceId(vmSpec.getNics())) { + if (nicTo.getUuid() != null) { + newVal = new OptionValue(); + newVal.setKey("nvp.iface-id." + nicNum); + newVal.setValue(nicTo.getUuid()); + extraOptions.add(newVal); + } + nicNum++; + } + } + + private static void configCustomExtraOption(List extraOptions, VirtualMachineTO vmSpec) { + // we no longer to validation anymore + for(Map.Entry entry : vmSpec.getDetails().entrySet()) { + OptionValue newVal = new OptionValue(); + newVal.setKey(entry.getKey()); + newVal.setValue(entry.getValue()); + extraOptions.add(newVal); + } + } + + private static void postNvpConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec) throws Exception { + /** + * We need to configure the port on the DV switch after the host is + * connected. So make this happen between the configure and start of + * the VM + */ + int nicIndex = 0; + for (NicTO nicTo : sortNicsByDeviceId(vmSpec.getNics())) { + if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) { + // We need to create a port with a unique vlan and pass the key to the nic device + s_logger.trace("Nic " + nicTo.toString() + " is connected to an NVP logicalswitch"); + VirtualDevice nicVirtualDevice = vmMo.getNicDeviceByIndex(nicIndex); + if (nicVirtualDevice == null) { + throw new Exception("Failed to find a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad + } + VirtualDeviceBackingInfo backing = nicVirtualDevice.getBacking(); + if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) { + // This NIC is connected to a Distributed Virtual Switch + VirtualEthernetCardDistributedVirtualPortBackingInfo portInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing; + DistributedVirtualSwitchPortConnection port = portInfo.getPort(); + String portKey = port.getPortKey(); + String portGroupKey = port.getPortgroupKey(); + String dvSwitchUuid = port.getSwitchUuid(); + + s_logger.debug("NIC " + nicTo.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey); + + ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager(); + ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid); + + // Get all ports + DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria(); + criteria.setInside(true); + criteria.getPortgroupKey().add(portGroupKey); + List dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria); + + DistributedVirtualPort vmDvPort = null; + List usedVlans = new ArrayList(); + for (DistributedVirtualPort dvPort : dvPorts) { + // Find the port for this NIC by portkey + if (portKey.equals(dvPort.getKey())) { + vmDvPort = dvPort; + } + VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort + .getConfig().getSetting(); + VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings + .getVlan(); + s_logger.trace("Found port " + dvPort.getKey() + + " with vlan " + vlanId.getVlanId()); + if (vlanId.getVlanId() > 0 + && vlanId.getVlanId() < 4095) { + usedVlans.add(vlanId.getVlanId()); + } + } + + if (vmDvPort == null) { + throw new Exception("Empty port list from dvSwitch for nic " + nicTo.toString()); + } + + DVPortConfigInfo dvPortConfigInfo = vmDvPort + .getConfig(); + VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPortConfigInfo.getSetting(); + + VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan(); + BoolPolicy blocked = settings.getBlocked(); + if (blocked.isValue() == Boolean.TRUE) { + s_logger.trace("Port is blocked, set a vlanid and unblock"); + DVPortConfigSpec dvPortConfigSpec = new DVPortConfigSpec(); + VMwareDVSPortSetting edittedSettings = new VMwareDVSPortSetting(); + // Unblock + blocked.setValue(Boolean.FALSE); + blocked.setInherited(Boolean.FALSE); + edittedSettings.setBlocked(blocked); + // Set vlan + int i; + for (i = 1; i < 4095; i++) { + if (!usedVlans.contains(i)) + break; + } + vlanId.setVlanId(i); // FIXME should be a determined + // based on usage + vlanId.setInherited(false); + edittedSettings.setVlan(vlanId); + + dvPortConfigSpec.setSetting(edittedSettings); + dvPortConfigSpec.setOperation("edit"); + dvPortConfigSpec.setKey(portKey); + List dvPortConfigSpecs = new ArrayList(); + dvPortConfigSpecs.add(dvPortConfigSpec); + ManagedObjectReference task = vmMo.getContext().getVimClient().getService().reconfigureDVPortTask(dvSwitch, dvPortConfigSpecs); + if (!vmMo.getContext().getVimClient().waitForTask(task)) { + throw new Exception( + "Failed to configure the dvSwitch port for nic " + + nicTo.toString()); + } + s_logger.debug("NIC " + nicTo.toString() + + " connected to vlan " + i); + } else { + s_logger.trace("Port already configured and set to vlan " + vlanId.getVlanId()); + } + } else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) { + // This NIC is connected to a Virtual Switch + // Nothing to do + } + else { + s_logger.error("nic device backing is of type " + backing.getClass().getName()); + throw new Exception("Incompatible backing for a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad + } + } + nicIndex++; + } + } + + private static int getDiskController(DiskTO vol, VirtualMachineTO vmSpec, int ideControllerKey, int scsiControllerKey) { + int controllerKey; + + if(vol.getType() == Volume.Type.ROOT) { + if(vmSpec.getDetails() != null && vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null) + { + if(vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi")) + controllerKey = scsiControllerKey; + else + controllerKey = ideControllerKey; + } else { + controllerKey = scsiControllerKey; + } + } else { + // DATA volume always use SCSI device + controllerKey = scsiControllerKey; + } + + return controllerKey; + } + + private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks, + int ideControllerKey, int scsiControllerKey) throws Exception { + + VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); + int controllerKey; + int ideUnitNumber = 1; // we always count in IDE device first + int scsiUnitNumber = 0; + + for(DiskTO vol: sortedDisks) { + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + + if (vol.getType() == Volume.Type.ISO) + continue; + + controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); + + String deviceBusName; + if(controllerKey == ideControllerKey) + deviceBusName = String.format("ide%d:%d", 0, ideUnitNumber++); + else + deviceBusName = String.format("scsi%d:%d", 0, scsiUnitNumber++); + + VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); + assert(diskInfo != null); + + String[] diskChain = diskInfo.getDiskChain(); + assert(diskChain.length > 0); + + DatastoreFile file = new DatastoreFile(diskChain[0]); + if(!file.getFileBaseName().equalsIgnoreCase(volumeTO.getPath())) { + if(s_logger.isInfoEnabled()) + s_logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " " + + volumeTO.getPath() + " -> " + file.getFileBaseName()); + } + + VolumeObjectTO volInSpec = getVolumeInSpec(vmSpec, volumeTO); + volInSpec.setPath(file.getFileBaseName()); + volInSpec.setChainInfo(_gson.toJson(diskInfo)); + } + } + + private static VolumeObjectTO getVolumeInSpec(VirtualMachineTO vmSpec, VolumeObjectTO srcVol) { + for(DiskTO disk : vmSpec.getDisks()) { + VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); + if(vol.getId() == srcVol.getId()) + return vol; + } + + return null; + } + + @Deprecated private Map validateVmDetails(Map vmDetails) { Map validatedDetails = new HashMap(); @@ -3147,7 +3347,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return validatedDetails; } - private NicTO[] sortNicsByDeviceId(NicTO[] nics) { + private static NicTO[] sortNicsByDeviceId(NicTO[] nics) { List listForSort = new ArrayList(); for (NicTO nic : nics) { @@ -3170,7 +3370,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return listForSort.toArray(new NicTO[0]); } - private DiskTO[] sortVolumesByDeviceId(DiskTO[] volumes) { + private static DiskTO[] sortVolumesByDeviceId(DiskTO[] volumes) { List listForSort = new ArrayList(); for (DiskTO vol : volumes) { @@ -6399,6 +6599,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa cfmMo.ensureCustomFieldDef("Network", CustomFieldConstants.CLOUD_GC); cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_UUID); cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_NIC_MASK); + cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); VmwareHypervisorHost hostMo = this.getHyperHost(context); _hostName = hostMo.getHyperHostName(); diff --git a/server/src/com/cloud/storage/VolumeManager.java b/server/src/com/cloud/storage/VolumeManager.java index d022c4a5f7b..25929a437dc 100644 --- a/server/src/com/cloud/storage/VolumeManager.java +++ b/server/src/com/cloud/storage/VolumeManager.java @@ -119,4 +119,6 @@ public interface VolumeManager extends VolumeApiService { String getStoragePoolOfVolume(long volumeId); boolean validateVolumeSizeRange(long size); + + void updateVolumeDiskChain(long volumeId, String path, String chainInfo); } diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index f7c0f86e847..6b7788b31c2 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -2870,4 +2870,22 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { VolumeVO vol = _volsDao.findById(volumeId); return dataStoreMgr.getPrimaryDataStore(vol.getPoolId()).getUuid(); } + + public void updateVolumeDiskChain(long volumeId, String path, String chainInfo) { + VolumeVO vol = _volsDao.findById(volumeId); + boolean needUpdate = false; + if(!vol.getPath().equalsIgnoreCase(path)) + needUpdate = true; + + if(chainInfo != null && (vol.getChainInfo() == null || !chainInfo.equalsIgnoreCase(vol.getChainInfo()))) + needUpdate = true; + + if(needUpdate) { + s_logger.info("Update volume disk chain info. vol: " + vol.getId() + ", " + vol.getPath() + " -> " + path + + ", " + vol.getChainInfo() + " -> " + chainInfo); + vol.setPath(path); + vol.setChainInfo(chainInfo); + _volsDao.update(volumeId, vol); + } + } } diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 499cf56b972..2f5d51ba2c2 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -42,6 +42,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -70,8 +71,10 @@ import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.StartupRoutingCommand.VmState; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.api.to.VolumeTO; import com.cloud.agent.manager.Commands; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.AlertManager; @@ -925,6 +928,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac destHostId = finalHost.getId(); } if (vmGuru.finalizeStart(vmProfile, destHostId, cmds, ctx)) { + syncDiskChainChange(startAnswer); + if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) { throw new ConcurrentOperationException("Unable to transition to a new state."); } @@ -1015,6 +1020,18 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return startedVm; } + + private void syncDiskChainChange(StartAnswer answer) { + VirtualMachineTO vmSpec = answer.getVirtualMachine(); + + for(DiskTO disk : vmSpec.getDisks()) { + if(disk.getType() != Volume.Type.ISO) { + VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); + + volumeMgr.updateVolumeDiskChain(vol.getId(), vol.getPath(), vol.getChainInfo()); + } + } + } @Override public boolean stop(T vm, User user, Account account) throws ResourceUnavailableException { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java new file mode 100644 index 00000000000..5a5060da315 --- /dev/null +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java @@ -0,0 +1,42 @@ +// 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.hypervisor.vmware.mo; + +public class VirtualMachineDiskInfo { + String diskDeviceBusName; + String[] diskChain; + + public VirtualMachineDiskInfo() { + } + + public String getDiskDeviceBusName() { + return diskDeviceBusName; + } + + public void setDiskDeviceBusName(String diskDeviceBusName) { + this.diskDeviceBusName = diskDeviceBusName; + } + + public String[] getDiskChain() { + return diskChain; + } + + public void setDiskChain(String[] diskChain) { + this.diskChain = diskChain; + } +} diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java new file mode 100644 index 00000000000..41c6db686b7 --- /dev/null +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java @@ -0,0 +1,97 @@ +// 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.hypervisor.vmware.mo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VirtualMachineDiskInfoBuilder { + Map> disks; + + public VirtualMachineDiskInfoBuilder() { + disks = new HashMap>(); + } + + public void addDisk(String diskDeviceBusName, String diskBackingFilePath) { + List chain = getDiskChainContainer(diskDeviceBusName); + chain.add(diskBackingFilePath); + } + + public int getDiskCount() { + return disks.keySet().size(); + } + + public List getAllDiskInfo() { + List infoList = new ArrayList(); + for(Map.Entry> entry : disks.entrySet()) { + VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo(); + diskInfo.setDiskDeviceBusName(entry.getKey()); + diskInfo.setDiskChain(entry.getValue().toArray(new String[1])); + infoList.add(diskInfo); + } + return infoList; + } + + public VirtualMachineDiskInfo getDiskInfoByDeviceBusName(String diskDeviceBusName) { + List chain = disks.get(diskDeviceBusName); + if(chain != null && chain.size() > 0) { + VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo(); + diskInfo.setDiskDeviceBusName(diskDeviceBusName); + diskInfo.setDiskChain(chain.toArray(new String[1])); + return diskInfo; + } + + return null; + } + + public VirtualMachineDiskInfo getDiskInfoByBackingFileBaseName(String diskBackingFileBaseName) { + for(Map.Entry> entry : disks.entrySet()) { + if(chainContains(entry.getValue(), diskBackingFileBaseName)) { + VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo(); + diskInfo.setDiskDeviceBusName(entry.getKey()); + diskInfo.setDiskChain(entry.getValue().toArray(new String[1])); + return diskInfo; + } + } + + return null; + } + + private List getDiskChainContainer(String diskDeviceBusName) { + assert(diskDeviceBusName != null); + List chain = disks.get(diskDeviceBusName); + if(chain == null) { + chain = new ArrayList(); + disks.put(diskDeviceBusName, chain); + } + return chain; + } + + private static boolean chainContains(List chain, String diskBackingFileBaseName) { + for(String backing : chain) { + DatastoreFile file = new DatastoreFile(backing); + + if(file.getFileBaseName().contains(diskBackingFileBaseName)) + return true; + } + + return false; + } +} diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 6a16ffa016f..71ab609b54a 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1847,6 +1847,31 @@ public class VirtualMachineMO extends BaseMO { return null; } + + public VirtualMachineDiskInfoBuilder getDiskInfoBuilder() throws Exception { + VirtualMachineDiskInfoBuilder builder = new VirtualMachineDiskInfoBuilder(); + + List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualDisk) { + VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking(); + if(backingInfo instanceof VirtualDiskFlatVer2BackingInfo) { + VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo; + + while(diskBackingInfo != null) { + String deviceBusName = getDeviceBusName(devices, device); + builder.addDisk(deviceBusName, diskBackingInfo.getFileName()); + diskBackingInfo = diskBackingInfo.getParent(); + } + } + } + } + } + + return builder; + } @Deprecated public List> getDiskDatastorePathChain(VirtualDisk disk, boolean followChain) throws Exception { @@ -1938,7 +1963,7 @@ public class VirtualMachineMO extends BaseMO { } throw new Exception("Unable to find device controller"); } - + public VirtualDisk[] getAllDiskDevice() throws Exception { List deviceList = new ArrayList(); List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); From f7df3ef9f1d6127d5438e58af7dccfbd1f58461c Mon Sep 17 00:00:00 2001 From: Sowmya Krishnan Date: Mon, 26 Aug 2013 22:56:11 +0530 Subject: [PATCH 009/251] CLOUDSTACK-4510 Move NS scripts to appropriate suites and related common.py fixes Signed-off-by: Prasanna Santhanam --- .../component/test_netscaler_lb.py | 20 +- .../component/test_netscaler_lb_algo.py | 18 +- .../component/test_netscaler_lb_sticky.py | 2 +- .../component/test_netscaler_nw_off.py | 659 +++++++++++++++++- .../component/test_network_offering.py | 628 ----------------- tools/marvin/marvin/integration/lib/common.py | 4 +- 6 files changed, 680 insertions(+), 651 deletions(-) diff --git a/test/integration/component/test_netscaler_lb.py b/test/integration/component/test_netscaler_lb.py index 8375f25a7ed..4b380e904e1 100644 --- a/test/integration/component/test_netscaler_lb.py +++ b/test/integration/component/test_netscaler_lb.py @@ -163,7 +163,7 @@ class TestLbSourceNat(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -373,7 +373,7 @@ class TestLbOnIpWithPf(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -587,7 +587,7 @@ class TestPfOnIpWithLb(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -802,7 +802,7 @@ class TestLbOnNonSourceNat(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -1020,7 +1020,7 @@ class TestAddMultipleVmsLb(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -1300,7 +1300,7 @@ class TestMultipleLbRules(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -1620,7 +1620,7 @@ class TestMultipleLbRulesSameIp(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -1946,7 +1946,7 @@ class TestLoadBalancingRule(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -2160,7 +2160,7 @@ class TestDeleteCreateLBRule(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -2289,7 +2289,7 @@ class TestVmWithLb(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, diff --git a/test/integration/component/test_netscaler_lb_algo.py b/test/integration/component/test_netscaler_lb_algo.py index 24c1837f437..4df7b897a5b 100644 --- a/test/integration/component/test_netscaler_lb_algo.py +++ b/test/integration/component/test_netscaler_lb_algo.py @@ -130,7 +130,7 @@ class TestLbWithRoundRobin(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -344,7 +344,7 @@ class TestLbWithLeastConn(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -568,7 +568,7 @@ class TestLbWithSourceIp(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup = [ cls.netscaler ] @@ -784,7 +784,7 @@ class TestLbAlgoRrLc(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -989,7 +989,7 @@ class TestLbAlgoLcRr(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -1191,7 +1191,7 @@ class TestLbAlgoRrSb(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -1396,7 +1396,7 @@ class TestLbAlgoSbRr(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -1604,7 +1604,7 @@ class TestLbAlgoSbLc(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, @@ -1811,7 +1811,7 @@ class TestLbAlgoLcSb(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, diff --git a/test/integration/component/test_netscaler_lb_sticky.py b/test/integration/component/test_netscaler_lb_sticky.py index 7ec85a13f29..56964a9fd9e 100644 --- a/test/integration/component/test_netscaler_lb_sticky.py +++ b/test/integration/component/test_netscaler_lb_sticky.py @@ -131,7 +131,7 @@ class TestLbStickyPolicy(cloudstackTestCase): cls.services["ostype"] ) try: - cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler"]) cls._cleanup.append(cls.netscaler) cls.network_offering = NetworkOffering.create( cls.api_client, diff --git a/test/integration/component/test_netscaler_nw_off.py b/test/integration/component/test_netscaler_nw_off.py index 5742f09d933..cb49dbe258d 100644 --- a/test/integration/component/test_netscaler_nw_off.py +++ b/test/integration/component/test_netscaler_nw_off.py @@ -157,13 +157,36 @@ class Services: "publicport": 22, "openfirewall": False, }, + "lbrule_port_2221": { + "name": "SSH", + "alg": "leastconn", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2221, + "openfirewall": False, + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "natrule_port_66": { + "privateport": 22, + "publicport": 66, + "protocol": "TCP" + }, + "fw_rule": { + "startport": 1, + "endport": 6000, + "cidr": '55.55.0.0/11', + # Any network (For creating FW rule) + }, "ostype": 'CentOS 5.3 (64-bit)', # Cent OS 5.3 (64 bit) "sleep": 60, "timeout": 10, } - class TestAddMultipleNetScaler(cloudstackTestCase): @classmethod @@ -2348,3 +2371,637 @@ class TestNwOffDToSUpgrade(cloudstackTestCase): except Exception as e: self.fail("Failed to create load balancing rule - %s" % e) return + +class TestNOWithNetscaler(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNOWithNetscaler, + cls + ).getClsTestClient().getApiClient() + + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + try: + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services["netscaler_1"]) + cls._cleanup = [ + cls.netscaler + ] + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + except Exception as e: + cls.tearDownClass() + raise Exception ("Warning: Exception in setUpClass: %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + return + + def tearDown(self): + try: + self.account.delete(self.apiclient) + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags = ["advancedns"]) + def test_01_network_off_without_conserve_mode(self): + """Test Nw off with Conserve mode off, VR-All services, LB-netscaler + """ + + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule + # 3. On source NAT ipaddress, we should NOT be allowed to add PF rule + # 4. On an ipaddress that has PF rules, we should NOT be allowed to + # add a LB rules. + # 5. On an ipaddress that has Lb rules , we should NOT allow firewall + # rules to be programmed. + # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules + # to be programmed. + # 7. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 8. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 9. On source NAT ipaddress, we should NOT be allowed to Enable VPN. + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR, LB in NS & conserve mode:ON" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering"], + conservemode=False + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.name, + domainid=self.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.name + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_nat_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + ip_with_nat_rule.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + ip_with_nat_rule.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + ip_with_nat_rule.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id, + accountid=self.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_lb_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id + ) + self.debug("Associated %s with network %s" % ( + ip_with_lb_rule.ipaddress, + self.network.id + )) + self.debug("Creating LB rule for IP address: %s" % + ip_with_lb_rule.ipaddress) + + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.name, + networkid=self.network.id + ) + + self.debug("Trying to create PF rule on IP with LB rule: %s" % + ip_with_nat_rule.ipaddress) + + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id + ) + + self.debug("Trying to create FW rule on IP with LB rule") + with self.assertRaises(Exception): + FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.name, + networkid=self.network.id + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + # User should be able to enable VPN on source NAT + self.debug("Enabling VPN on source NAT IP: %s" % src_nat.ipaddress) + # Assign VPN to source NAT + with self.assertRaises(Exception): + Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.name, + domainid=self.account.domainid + ) + return + + @attr(tags = ["advancedns"]) + def test_02_network_off_with_conserve_mode_netscaler(self): + """Test NW off with Conserve mode ON, LB-Netscaler and VR-All services + """ + + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule + # 3. On source NAT ipaddress, we should be allowed to add PF rule and + # Fierwall rules. + # 4. On an ipaddress that has PF rules, we should NOT be allowed to + # add a LB rules. + # 5. On an ipaddress that has Lb rules , we should NOT allow firewall + # rules to be programmed. + # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules + # to be programmed. + # 7. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 8. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 9. On source NAT ipaddress, we should be allowed to Enable VPN. + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:ON" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering"], + conservemode=True + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.name, + domainid=self.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.name + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) + + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT should return a valid port forwarding rules" + ) + self.assertNotEqual( + len(nat_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_nat_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + ip_with_nat_rule.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + ip_with_nat_rule.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + ip_with_nat_rule.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id, + accountid=self.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_lb_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id + ) + self.debug("Associated %s with network %s" % ( + ip_with_lb_rule.ipaddress, + self.network.id + )) + self.debug("Creating LB rule for IP address: %s" % + ip_with_lb_rule.ipaddress) + + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.name, + networkid=self.network.id + ) + + self.debug("Trying to create PF rule on IP with LB rule: %s" % + ip_with_nat_rule.ipaddress) + + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id + ) + + self.debug("Trying to create FW rule on IP with LB rule") + with self.assertRaises(Exception): + FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.name, + networkid=self.network.id + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + # User should be able to enable VPN on source NAT + self.debug("Created VPN with source NAT IP: %s" % src_nat.ipaddress) + # Assign VPN to source NAT + vpn = Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.name, + domainid=self.account.domainid + ) + + vpns = Vpn.list( + self.apiclient, + publicipid=src_nat.id, + listall=True, + ) + + self.assertEqual( + isinstance(vpns, list), + True, + "List VPNs should return a valid VPN list" + ) + + self.assertNotEqual( + len(vpns), + 0, + "Length of list VNP response should not be zero" + ) + return diff --git a/test/integration/component/test_network_offering.py b/test/integration/component/test_network_offering.py index e8a7b971017..335f8592ff0 100644 --- a/test/integration/component/test_network_offering.py +++ b/test/integration/component/test_network_offering.py @@ -729,634 +729,6 @@ class TestNOVirtualRouter(cloudstackTestCase): return -class TestNOWithNetscaler(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - cls.api_client = super( - TestNOWithNetscaler, - cls - ).getClsTestClient().getApiClient() - cls.services = Services().services - # Get Zone, Domain and templates - cls.domain = get_domain(cls.api_client, cls.services) - cls.zone = get_zone(cls.api_client, cls.services) - cls.services['mode'] = cls.zone.networktype - cls.template = get_template( - cls.api_client, - cls.zone.id, - cls.services["ostype"] - ) - cls.services["virtual_machine"]["zoneid"] = cls.zone.id - cls.services["virtual_machine"]["template"] = cls.template.id - - cls.service_offering = ServiceOffering.create( - cls.api_client, - cls.services["service_offering"] - ) - cls._cleanup = [ - cls.service_offering, - ] - return - - @classmethod - def tearDownClass(cls): - try: - #Cleanup resources used - cleanup_resources(cls.api_client, cls._cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - def setUp(self): - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.account = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.domain.id - ) - self.cleanup = [] - return - - def tearDown(self): - try: - self.account.delete(self.apiclient) - cleanup_resources(self.apiclient, self.cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return - - @attr(tags = ["advancedns"]) - def test_01_network_off_without_conserve_mode(self): - """Test Nw off with Conserve mode off, VR-All services, LB-netscaler - """ - - - # Validate the following - # 1. Create a Network from the above network offering and deploy a VM. - # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule - # 3. On source NAT ipaddress, we should NOT be allowed to add PF rule - # 4. On an ipaddress that has PF rules, we should NOT be allowed to - # add a LB rules. - # 5. On an ipaddress that has Lb rules , we should NOT allow firewall - # rules to be programmed. - # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules - # to be programmed. - # 7. We should be allowed to program multiple PF rules on the same Ip - # address on different public ports. - # 8. We should be allowed to program multiple LB rules on the same Ip - # address for different public port ranges. - # 9. On source NAT ipaddress, we should NOT be allowed to Enable VPN. - - # Create a network offering with all virtual router services enabled - self.debug( - "Creating n/w offering with all services in VR & conserve mode:ON" - ) - self.network_offering = NetworkOffering.create( - self.api_client, - self.services["network_offering_netscaler"], - conservemode=False - ) - self.cleanup.append(self.network_offering) - - self.debug("Created n/w offering with ID: %s" % - self.network_offering.id) - # Enable Network offering - self.network_offering.update(self.apiclient, state='Enabled') - - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - self.network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % self.network.id) - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(self.network.id)] - ) - self.debug("Deployed VM in network: %s" % self.network.id) - - src_nat_list = PublicIPAddress.list( - self.apiclient, - associatednetworkid=self.network.id, - account=self.account.name, - domainid=self.account.domainid, - listall=True, - issourcenat=True, - ) - self.assertEqual( - isinstance(src_nat_list, list), - True, - "List Public IP should return a valid source NAT" - ) - self.assertNotEqual( - len(src_nat_list), - 0, - "Length of response from listPublicIp should not be 0" - ) - - src_nat = src_nat_list[0] - - self.debug("Trying to create LB rule on source NAT IP: %s" % - src_nat.ipaddress) - # Create Load Balancer rule with source NAT - with self.assertRaises(Exception): - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=src_nat.id, - accountid=self.account.name - ) - - self.debug( - "Trying to create a port forwarding rule in source NAT: %s" % - src_nat.ipaddress) - #Create NAT rule - with self.assertRaises(Exception): - NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=src_nat.id - ) - self.debug("Creating firewall rule on source NAT: %s" % - src_nat.ipaddress) - #Create Firewall rule on source NAT - fw_rule = FireWallRule.create( - self.apiclient, - ipaddressid=src_nat.id, - protocol='TCP', - cidrlist=[self.services["fw_rule"]["cidr"]], - startport=self.services["fw_rule"]["startport"], - endport=self.services["fw_rule"]["endport"] - ) - - self.debug("Created firewall rule: %s" % fw_rule.id) - - fw_rules = FireWallRule.list( - self.apiclient, - id=fw_rule.id - ) - self.assertEqual( - isinstance(fw_rules, list), - True, - "List fw rules should return a valid firewall rules" - ) - - self.assertNotEqual( - len(fw_rules), - 0, - "Length of fw rules response should not be zero" - ) - - self.debug("Associating public IP for network: %s" % self.network.id) - ip_with_nat_rule = PublicIPAddress.create( - self.apiclient, - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - networkid=self.network.id - ) - - self.debug("Associated %s with network %s" % ( - ip_with_nat_rule.ipaddress, - self.network.id - )) - self.debug("Creating PF rule for IP address: %s" % - ip_with_nat_rule.ipaddress) - NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=ip_with_nat_rule.ipaddress.id - ) - - self.debug("Trying to create LB rule on IP with NAT: %s" % - ip_with_nat_rule.ipaddress) - - # Create Load Balancer rule on IP already having NAT rule - with self.assertRaises(Exception): - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=ip_with_nat_rule.ipaddress.id, - accountid=self.account.name - ) - self.debug("Creating PF rule with public port: 66") - - nat_rule = NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule_port_66"], - ipaddressid=ip_with_nat_rule.ipaddress.id - ) - - # Check if NAT rule created successfully - nat_rules = NATRule.list( - self.apiclient, - id=nat_rule.id - ) - - self.assertEqual( - isinstance(nat_rules, list), - True, - "List NAT rules should return valid list" - ) - - self.debug("Associating public IP for network: %s" % self.network.id) - ip_with_lb_rule = PublicIPAddress.create( - self.apiclient, - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - networkid=self.network.id - ) - self.debug("Associated %s with network %s" % ( - ip_with_lb_rule.ipaddress, - self.network.id - )) - self.debug("Creating LB rule for IP address: %s" % - ip_with_lb_rule.ipaddress) - - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=ip_with_lb_rule.ipaddress.id, - accountid=self.account.name, - networkid=self.network.id - ) - - self.debug("Trying to create PF rule on IP with LB rule: %s" % - ip_with_nat_rule.ipaddress) - - with self.assertRaises(Exception): - NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=ip_with_lb_rule.ipaddress.id - ) - - self.debug("Trying to create FW rule on IP with LB rule") - with self.assertRaises(Exception): - FireWallRule.create( - self.apiclient, - ipaddressid=src_nat.id, - protocol='TCP', - cidrlist=[self.services["fw_rule"]["cidr"]], - startport=self.services["fw_rule"]["startport"], - endport=self.services["fw_rule"]["endport"] - ) - - self.debug("Creating LB rule with public port: 2221") - lb_rule = LoadBalancerRule.create( - self.apiclient, - self.services["lbrule_port_2221"], - ipaddressid=ip_with_lb_rule.ipaddress.id, - accountid=self.account.name, - networkid=self.network.id - ) - - # Check if NAT rule created successfully - lb_rules = LoadBalancerRule.list( - self.apiclient, - id=lb_rule.id - ) - - self.assertEqual( - isinstance(lb_rules, list), - True, - "List LB rules should return valid list" - ) - - # User should be able to enable VPN on source NAT - self.debug("Enabling VPN on source NAT IP: %s" % src_nat.ipaddress) - # Assign VPN to source NAT - with self.assertRaises(Exception): - Vpn.create( - self.apiclient, - src_nat.id, - account=self.account.name, - domainid=self.account.domainid - ) - return - - @attr(tags = ["advancedns"]) - def test_02_network_off_with_conserve_mode_netscaler(self): - """Test NW off with Conserve mode ON, LB-Netscaler and VR-All services - """ - - - # Validate the following - # 1. Create a Network from the above network offering and deploy a VM. - # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule - # 3. On source NAT ipaddress, we should be allowed to add PF rule and - # Fierwall rules. - # 4. On an ipaddress that has PF rules, we should NOT be allowed to - # add a LB rules. - # 5. On an ipaddress that has Lb rules , we should NOT allow firewall - # rules to be programmed. - # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules - # to be programmed. - # 7. We should be allowed to program multiple PF rules on the same Ip - # address on different public ports. - # 8. We should be allowed to program multiple LB rules on the same Ip - # address for different public port ranges. - # 9. On source NAT ipaddress, we should be allowed to Enable VPN. - - # Create a network offering with all virtual router services enabled - self.debug( - "Creating n/w offering with all services in VR & conserve mode:ON" - ) - self.network_offering = NetworkOffering.create( - self.api_client, - self.services["network_offering_netscaler"], - conservemode=True - ) - self.cleanup.append(self.network_offering) - - self.debug("Created n/w offering with ID: %s" % - self.network_offering.id) - # Enable Network offering - self.network_offering.update(self.apiclient, state='Enabled') - - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - self.network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % self.network.id) - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(self.network.id)] - ) - self.debug("Deployed VM in network: %s" % self.network.id) - - src_nat_list = PublicIPAddress.list( - self.apiclient, - associatednetworkid=self.network.id, - account=self.account.name, - domainid=self.account.domainid, - listall=True, - issourcenat=True, - ) - self.assertEqual( - isinstance(src_nat_list, list), - True, - "List Public IP should return a valid source NAT" - ) - self.assertNotEqual( - len(src_nat_list), - 0, - "Length of response from listPublicIp should not be 0" - ) - - src_nat = src_nat_list[0] - - self.debug("Trying to create LB rule on source NAT IP: %s" % - src_nat.ipaddress) - # Create Load Balancer rule with source NAT - with self.assertRaises(Exception): - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=src_nat.id, - accountid=self.account.name - ) - - self.debug( - "Trying to create a port forwarding rule in source NAT: %s" % - src_nat.ipaddress) - #Create NAT rule - nat_rule = NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=src_nat.id - ) - self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) - - nat_rules = NATRule.list( - self.apiclient, - id=nat_rule.id - ) - self.assertEqual( - isinstance(nat_rules, list), - True, - "List NAT should return a valid port forwarding rules" - ) - self.assertNotEqual( - len(nat_rules), - 0, - "Length of response from listLbRules should not be 0" - ) - self.debug("Creating firewall rule on source NAT: %s" % - src_nat.ipaddress) - #Create Firewall rule on source NAT - fw_rule = FireWallRule.create( - self.apiclient, - ipaddressid=src_nat.id, - protocol='TCP', - cidrlist=[self.services["fw_rule"]["cidr"]], - startport=self.services["fw_rule"]["startport"], - endport=self.services["fw_rule"]["endport"] - ) - self.debug("Created firewall rule: %s" % fw_rule.id) - - fw_rules = FireWallRule.list( - self.apiclient, - id=fw_rule.id - ) - self.assertEqual( - isinstance(fw_rules, list), - True, - "List fw rules should return a valid firewall rules" - ) - - self.assertNotEqual( - len(fw_rules), - 0, - "Length of fw rules response should not be zero" - ) - self.debug("Associating public IP for network: %s" % self.network.id) - ip_with_nat_rule = PublicIPAddress.create( - self.apiclient, - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - networkid=self.network.id - ) - - self.debug("Associated %s with network %s" % ( - ip_with_nat_rule.ipaddress, - self.network.id - )) - self.debug("Creating PF rule for IP address: %s" % - ip_with_nat_rule.ipaddress) - NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=ip_with_nat_rule.ipaddress.id - ) - - self.debug("Trying to create LB rule on IP with NAT: %s" % - ip_with_nat_rule.ipaddress) - - # Create Load Balancer rule on IP already having NAT rule - with self.assertRaises(Exception): - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=ip_with_nat_rule.ipaddress.id, - accountid=self.account.name - ) - self.debug("Creating PF rule with public port: 66") - - nat_rule = NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule_port_66"], - ipaddressid=ip_with_nat_rule.ipaddress.id - ) - - # Check if NAT rule created successfully - nat_rules = NATRule.list( - self.apiclient, - id=nat_rule.id - ) - - self.assertEqual( - isinstance(nat_rules, list), - True, - "List NAT rules should return valid list" - ) - - self.debug("Associating public IP for network: %s" % self.network.id) - ip_with_lb_rule = PublicIPAddress.create( - self.apiclient, - accountid=self.account.name, - zoneid=self.zone.id, - domainid=self.account.domainid, - networkid=self.network.id - ) - self.debug("Associated %s with network %s" % ( - ip_with_lb_rule.ipaddress, - self.network.id - )) - self.debug("Creating LB rule for IP address: %s" % - ip_with_lb_rule.ipaddress) - - LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - ipaddressid=ip_with_lb_rule.ipaddress.id, - accountid=self.account.name, - networkid=self.network.id - ) - - self.debug("Trying to create PF rule on IP with LB rule: %s" % - ip_with_nat_rule.ipaddress) - - with self.assertRaises(Exception): - NATRule.create( - self.apiclient, - virtual_machine, - self.services["natrule"], - ipaddressid=ip_with_lb_rule.ipaddress.id - ) - - self.debug("Trying to create FW rule on IP with LB rule") - with self.assertRaises(Exception): - FireWallRule.create( - self.apiclient, - ipaddressid=src_nat.id, - protocol='TCP', - cidrlist=[self.services["fw_rule"]["cidr"]], - startport=self.services["fw_rule"]["startport"], - endport=self.services["fw_rule"]["endport"] - ) - - self.debug("Creating LB rule with public port: 2221") - lb_rule = LoadBalancerRule.create( - self.apiclient, - self.services["lbrule_port_2221"], - ipaddressid=ip_with_lb_rule.ipaddress.id, - accountid=self.account.name, - networkid=self.network.id - ) - - # Check if NAT rule created successfully - lb_rules = LoadBalancerRule.list( - self.apiclient, - id=lb_rule.id - ) - - self.assertEqual( - isinstance(lb_rules, list), - True, - "List LB rules should return valid list" - ) - - # User should be able to enable VPN on source NAT - self.debug("Created VPN with source NAT IP: %s" % src_nat.ipaddress) - # Assign VPN to source NAT - vpn = Vpn.create( - self.apiclient, - src_nat.id, - account=self.account.name, - domainid=self.account.domainid - ) - - vpns = Vpn.list( - self.apiclient, - publicipid=src_nat.id, - listall=True, - ) - - self.assertEqual( - isinstance(vpns, list), - True, - "List VPNs should return a valid VPN list" - ) - - self.assertNotEqual( - len(vpns), - 0, - "Length of list VNP response should not be zero" - ) - return - class TestNetworkUpgrade(cloudstackTestCase): diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index 7e8d92d6864..f27e87df38f 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -59,7 +59,7 @@ def wait_for_cleanup(apiclient, configs=None): time.sleep(int(config_desc.value)) return -def add_netscaler(apiclient, zoneid, services=None): +def add_netscaler(apiclient, zoneid, NSservice): """ Adds Netscaler device and enables NS provider""" cmd = listPhysicalNetworks.listPhysicalNetworksCmd() @@ -70,7 +70,7 @@ def add_netscaler(apiclient, zoneid, services=None): netscaler = NetScaler.add( apiclient, - services["netscaler"], + NSservice, physicalnetworkid=physical_network.id ) From cd4f93c9e45dfe8c3483115087f8bbb1ab0d4eba Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 11:31:45 +0530 Subject: [PATCH 010/251] review comments --- docs/en-US/add-loadbalancer-rule-vpc.xml | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index 0f2a83dcbfd..618d7b9ed22 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -274,6 +274,19 @@ +
+ Guidelines + + Internal LB and Public LB are mutually exclusive on a tier. If the tier has LB on the public + side, then it can't have the Internal LB. + Internal LB is supported just on VPC networks in &PRODUCT; 4.2 release. + Only Internal LB VM can act as the Internal LB provider in &PRODUCT; 4.2 release. + Network upgrade is not supported from the network offering with Internal LB to the network + offering with Public LB. + Multiple tiers can have internal LB support in a VPC. + Only one tier can have Public LB support in a VPC. + +
Enabling Internal LB on a VPC Tier @@ -364,6 +377,17 @@
Creating an Internal LB Rule +<<<<<<< HEAD +======= + When you create the Internal LB rule and applies to a VM, an Internal LB VM, which is + responsible for load balancing, is created. + You can view the created Internal LB VM in the Instances page if you navigate to + Infrastructure > Zones > + <zone_ name> > <physical_network_name> > Network Service + Providers > Internal LB VM. You can manage the + Internal LB VMs as and when required from the location. +>>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Log in to the &PRODUCT; UI as an administrator or end user. @@ -397,9 +421,17 @@ that can be displayed to users. +<<<<<<< HEAD Source IP Address: The source IP from which traffic originates. Typically, this is the IP of an instance on another tier within your VPC. +======= + Source IP Address: (Optional) The source IP + from which traffic originates. The IP is acquired from the CIDR of that particular + tier on which you want to create the Internal LB rule. If not specified, the IP + address is automatically allocated from the network CIDR. + For every Source IP, a new Internal LB VM is created for load balancing. +>>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Source Port: The port associated with the From fb4c8b35e5d24861f76c0ef375cfd22305040a8c Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 11:31:45 +0530 Subject: [PATCH 011/251] vpc review comments --- docs/en-US/add-loadbalancer-rule-vpc.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index 618d7b9ed22..3c164c2140e 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -387,6 +387,9 @@ >physical_network_name> > Network Service Providers > Internal LB VM. You can manage the Internal LB VMs as and when required from the location. +<<<<<<< HEAD +>>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 +======= >>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 @@ -421,11 +424,14 @@ that can be displayed to users. +<<<<<<< HEAD <<<<<<< HEAD Source IP Address: The source IP from which traffic originates. Typically, this is the IP of an instance on another tier within your VPC. ======= +======= +>>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Source IP Address: (Optional) The source IP from which traffic originates. The IP is acquired from the CIDR of that particular tier on which you want to create the Internal LB rule. If not specified, the IP From 1f46bc3fb09aead2cf1744d358fea7adba7df6e1 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 11:49:52 +0530 Subject: [PATCH 012/251] review comments for CLOUDSTACK-2808, CLOUDSTACK-769 --- docs/en-US/add-loadbalancer-rule-vpc.xml | 33 +++++++++--------------- docs/en-US/add-tier.xml | 3 ++- docs/en-US/vlan-assign-isolated-nw.xml | 8 ++++-- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index 3c164c2140e..90247b0a6f9 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -60,7 +60,7 @@
Creating a Network Offering for External LB - To have internal LB support on VPC, create a network offering as follows: + To have external LB support on VPC, create a network offering as follows: Log in to the &PRODUCT; UI as a user or admin. @@ -111,12 +111,16 @@ Indicate whether a VLAN should be specified when this offering is used. - Supported Services: Select Load Balancer. - Select InternalLbVM from the provider list. + Supported Services: Select Load Balancer. Use + Netscaler or VpcVirtualRouter. - Load Balancer Type: Select external LB from the - drop-down. Use Netscaler + Load Balancer Type: Select Public LB from the + drop-down. + + + LB Isolation: Select Dedicated if Netscaler is + used as the external LB provider. System Offering: Choose the system service @@ -301,7 +305,9 @@
Creating a Network Offering for Internal LB - To have internal LB support on VPC, create a network offering as follows: + To have internal LB support on VPC, either use the default offering, + DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, or create a network offering as + follows: Log in to the &PRODUCT; UI as a user or admin. @@ -377,8 +383,6 @@
Creating an Internal LB Rule -<<<<<<< HEAD -======= When you create the Internal LB rule and applies to a VM, an Internal LB VM, which is responsible for load balancing, is created. You can view the created Internal LB VM in the Instances page if you navigate to @@ -387,10 +391,6 @@ >physical_network_name> > Network Service Providers > Internal LB VM. You can manage the Internal LB VMs as and when required from the location. -<<<<<<< HEAD ->>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 -======= ->>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Log in to the &PRODUCT; UI as an administrator or end user. @@ -424,20 +424,11 @@ that can be displayed to users. -<<<<<<< HEAD -<<<<<<< HEAD - Source IP Address: The source IP from which - traffic originates. Typically, this is the IP of an instance on another tier within - your VPC. -======= -======= ->>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Source IP Address: (Optional) The source IP from which traffic originates. The IP is acquired from the CIDR of that particular tier on which you want to create the Internal LB rule. If not specified, the IP address is automatically allocated from the network CIDR. For every Source IP, a new Internal LB VM is created for load balancing. ->>>>>>> 030fe5b... review comments for CLOUDSTACK-2808, CLOUDSTACK-769 Source Port: The port associated with the diff --git a/docs/en-US/add-tier.xml b/docs/en-US/add-tier.xml index 17e02be7b7b..94a8237c066 100644 --- a/docs/en-US/add-tier.xml +++ b/docs/en-US/add-tier.xml @@ -75,7 +75,8 @@ the VPC, and is not overlapped with the CIDR of any existing tier within the VPC. - VLAN: The VLAN ID for the tier you create. + VLAN: The VLAN ID for the tier that the root admin + creates. This option is only visible if the network offering you selected is VLAN-enabled. For more information, see the Assigning VLANs to Isolated diff --git a/docs/en-US/vlan-assign-isolated-nw.xml b/docs/en-US/vlan-assign-isolated-nw.xml index 2ed0129cfdf..424ecd2ac4a 100644 --- a/docs/en-US/vlan-assign-isolated-nw.xml +++ b/docs/en-US/vlan-assign-isolated-nw.xml @@ -21,14 +21,18 @@ -->
Assigning VLANs to Isolated Networks - &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. You can - assign a VLAN ID when a network is created, just the way it's done for Shared networks. + &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. As a + Root admin, you can assign a VLAN ID when a network is created, just the way it's done for + Shared networks. The former behaviour also is supported — VLAN is randomly allocated to a network from the VNET range of the physical network when the network turns to Implemented state. The VLAN is released back to the VNET pool when the network shuts down as a part of the Network Garbage Collection. The VLAN can be re-used either by the same network when it is implemented again, or by any other network. On each subsequent implementation of a network, a new VLAN can be assigned. + Only the Root admin can assign VLANs because the regular users or domain admin are not aware + of the physical network topology. They cannot even view what VLAN is assigned to a + network. To enable you to assign VLANs to Isolated networks, From 239281a401f09df9519bb8e8637c2060b9be019b Mon Sep 17 00:00:00 2001 From: Sowmya Krishnan Date: Mon, 26 Aug 2013 11:14:01 +0530 Subject: [PATCH 013/251] CLOUDSTACK-4491: Added missing NS setup Signed-off-by: venkataswamybabu budumuru --- .../component/test_netscaler_configs.py | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/test/integration/component/test_netscaler_configs.py b/test/integration/component/test_netscaler_configs.py index df4e707ef09..c10f6882334 100644 --- a/test/integration/component/test_netscaler_configs.py +++ b/test/integration/component/test_netscaler_configs.py @@ -62,7 +62,7 @@ class Services: "protocol": 'TCP', }, "netscaler": { - "ipaddress": '10.147.60.26', + "ipaddress": '10.147.60.26', "username": 'nsroot', "password": 'nsroot', "networkdevicetype": 'NetscalerVPXLoadBalancer', @@ -2095,6 +2095,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): @classmethod def setUpClass(cls): + cls._cleanup = [] cls.api_client = super( TestGuestNetworkShutDown, cls @@ -2108,30 +2109,32 @@ class TestGuestNetworkShutDown(cloudstackTestCase): cls.zone.id, cls.services["ostype"] ) - - cls.network_offering = NetworkOffering.create( + try: + cls.netscaler = add_netscaler(cls.api_client, cls.zone.id, cls.services) + cls._cleanup.append(cls.netscaler) + cls.network_offering = NetworkOffering.create( cls.api_client, cls.services["network_offering"], conservemode=True ) - # Enable Network offering - cls.network_offering.update(cls.api_client, state='Enabled') - cls.services["virtual_machine"]["zoneid"] = cls.zone.id - cls.services["virtual_machine"]["template"] = cls.template.id + # Enable Network offering + cls.network_offering.update(cls.api_client, state='Enabled') + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id - cls.service_offering = ServiceOffering.create( + cls.service_offering = ServiceOffering.create( cls.api_client, cls.services["service_offering"] ) - cls.account = Account.create( + cls.account = Account.create( cls.api_client, cls.services["account"], admin=True, domainid=cls.domain.id ) - - # Creating network using the network offering created - cls.network = Network.create( + cls._cleanup.insert(0,cls.account) + # Creating network using the network offering created + cls.network = Network.create( cls.api_client, cls.services["network"], accountid=cls.account.name, @@ -2140,8 +2143,8 @@ class TestGuestNetworkShutDown(cloudstackTestCase): zoneid=cls.zone.id ) - # Spawn few instances in that network - cls.vm_1 = VirtualMachine.create( + # Spawn few instances in that network + cls.vm_1 = VirtualMachine.create( cls.api_client, cls.services["virtual_machine"], accountid=cls.account.name, @@ -2149,7 +2152,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): serviceofferingid=cls.service_offering.id, networkids=[str(cls.network.id)] ) - cls.vm_2 = VirtualMachine.create( + cls.vm_2 = VirtualMachine.create( cls.api_client, cls.services["virtual_machine"], accountid=cls.account.name, @@ -2157,25 +2160,25 @@ class TestGuestNetworkShutDown(cloudstackTestCase): serviceofferingid=cls.service_offering.id, networkids=[str(cls.network.id)] ) - cls.public_ip = PublicIPAddress.create( + cls.public_ip = PublicIPAddress.create( cls.api_client, accountid=cls.account.name, zoneid=cls.zone.id, domainid=cls.account.domainid, networkid=cls.network.id ) - cls.lb_rule = LoadBalancerRule.create( + cls.lb_rule = LoadBalancerRule.create( cls.api_client, cls.services["lbrule"], ipaddressid=cls.public_ip.ipaddress.id, accountid=cls.account.name, networkid=cls.network.id ) - cls.lb_rule.assign(cls.api_client, [cls.vm_1, cls.vm_2]) - cls._cleanup = [ - cls.service_offering, - cls.account - ] + cls.lb_rule.assign(cls.api_client, [cls.vm_1, cls.vm_2]) + except Exception as e: + cls.tearDownClass() + raise Exception ("Warning: Exception in setUpClass: %s" % e) + return @classmethod From d0ad80530f9f7a62464497a7d39ccd63958463a1 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 12:26:28 +0530 Subject: [PATCH 014/251] review comments for CLOUDSTACK-1815 --- docs/en-US/password-storage-engine.xml | 30 +++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/en-US/password-storage-engine.xml b/docs/en-US/password-storage-engine.xml index 05661055e9b..8bbc96fcac2 100644 --- a/docs/en-US/password-storage-engine.xml +++ b/docs/en-US/password-storage-engine.xml @@ -22,11 +22,13 @@
Changing the Default Password Encryption Passwords are encoded when creating or updating users. &PRODUCT; allows you to determine the - default encoding and authentication mechanism for admin and user logins. A new configurable list - called UserPasswordEncoders to allow you to separately configure the order of - preference for encoding and authentication schemes. - Additionally, plain text user authenticator has been changed to use SHA256SALT as the - default encoding algorithm because it is more secure compared to MD5 hashing. It does a simple + default encoding and authentication mechanism for admin and user logins. Two new configurable + lists have been introduced—userPasswordEncoders and userAuthenticators. + userPasswordEncoders allows you to configure the order of preference for encoding passwords, + whereas userAuthenticators allows you to configure the order in which authentication schemes are + invoked to validate user passwords. + Additionally, the plain text user authenticator has been modified not to convert supplied + passwords to their md5 sums before checking them with the database entries. It performs a simple string comparison between retrieved and supplied login passwords instead of comparing the retrieved md5 hash of the stored password against the supplied md5 hash of the password because clients no longer hash the password. The following method determines what encoding scheme is @@ -35,11 +37,15 @@ loaded as per the sequence specified in the UserPasswordEncoders property in the ComponentContext.xml or nonossComponentContext.xml files. The order of authentication schemes is determined by the UserAuthenticators - property in the same files. When a new authenticator or encoder is added, you can add them to - this list. While doing so, ensure that the new authenticator or encoder is specified as a bean - in both these files. The administrator can change the ordering of both these properties as - preferred to change the order of schemes. Modify the following list properties available in - client/tomcatconf/nonossComponentContext.xml.in or + property in the same files. If Non-OSS components, such as VMware environments, are to be + deployed, modify the UserPasswordEncoders and UserAuthenticators lists + in the nonossComponentContext.xml file, for OSS environments, such as + XenServer or KVM, modify the ComponentContext.xml file. It is recommended + to make uniform changes across both the files. When a new authenticator or encoder is added, you + can add them to this list. While doing so, ensure that the new authenticator or encoder is + specified as a bean in both these files. The administrator can change the ordering of both these + properties as preferred to change the order of schemes. Modify the following list properties + available in client/tomcatconf/nonossComponentContext.xml.in or client/tomcatconf/componentContext.xml.in as applicable, to the desired order: <property name="UserAuthenticators"> @@ -62,7 +68,7 @@ the encoded password is stored in the user table's password column. If it fails for any reason, the MD5UserAuthenticator will be tried next, and the order continues. For UserAuthenticators, SHA256Salt authentication is tried first. If it succeeds, the - user is logged into the Management server. If it fails, MD5 is tried next, and attempts - continues until any of them succeeds and the user logs in . If none of them works, the user is + user is logged into the Management server. If it fails, md5 is tried next, and attempts + continues until any of them succeeds and the user logs in . If none of them works, the user is returned an invalid credential message.
From c059dacfc49b0757e4cd78498fef0d424a462c54 Mon Sep 17 00:00:00 2001 From: Anshul Gangwar Date: Mon, 26 Aug 2013 13:27:56 +0530 Subject: [PATCH 015/251] CLOUDSTACK-3806: There was error in host_view sql, it was using host_details.id for join instead host_details.host_id Signed-off-by: Sateesh Chodapuneedi --- setup/db/db/schema-307to410.sql | 2 +- setup/db/db/schema-40to410.sql | 2 +- setup/db/db/schema-410to420.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/db/db/schema-307to410.sql b/setup/db/db/schema-307to410.sql index 70cddfdfbc8..7feb53eb16e 100644 --- a/setup/db/db/schema-307to410.sql +++ b/setup/db/db/schema-307to410.sql @@ -977,7 +977,7 @@ CREATE VIEW `cloud`.`host_view` AS left join `cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id left join - `cloud`.`host_details` ON host.id = host_details.id + `cloud`.`host_details` ON host.id = host_details.host_id and host_details.name = 'guest.os.category.id' left join `cloud`.`guest_os_category` ON guest_os_category.id = CONVERT( host_details.value , UNSIGNED) diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index f1482f84085..24f6d4c5e19 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -1073,7 +1073,7 @@ CREATE VIEW `cloud`.`host_view` AS left join `cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id left join - `cloud`.`host_details` ON host.id = host_details.id + `cloud`.`host_details` ON host.id = host_details.host_id and host_details.name = 'guest.os.category.id' left join `cloud`.`guest_os_category` ON guest_os_category.id = CONVERT( host_details.value , UNSIGNED) diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index c29fd86e71e..4d688384922 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -992,7 +992,7 @@ CREATE VIEW `cloud`.`host_view` AS left join `cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id left join - `cloud`.`host_details` ON host.id = host_details.id + `cloud`.`host_details` ON host.id = host_details.host_id and host_details.name = 'guest.os.category.id' left join `cloud`.`guest_os_category` ON guest_os_category.id = CONVERT( host_details.value , UNSIGNED) From e01457134a2dcffa75f30ad7d11851c04f01ce6f Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 14:17:15 +0530 Subject: [PATCH 016/251] CLOUDSTACK-772 review comments on DVS fixed --- docs/en-US/vmware-cluster-config-dvswitch.xml | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml index 98bee0e066a..173055f40ca 100644 --- a/docs/en-US/vmware-cluster-config-dvswitch.xml +++ b/docs/en-US/vmware-cluster-config-dvswitch.xml @@ -78,6 +78,84 @@ Use this VDS name when you specify the switch name in the traffic label while creating the zone. Traffic label format is [["Name of vSwitch/dvSwitch/EthernetPortProfile"][,"VLAN ID"[,"vSwitch Type"]]] +<<<<<<< HEAD +======= + The possible values for traffic labels are: + + empty string + dvSwitch0 + dvSwitch0,200 + dvSwitch1,300,vmwaredvs + myEthernetPortProfile,,nexusdvs + dvSwitch0,,vmwaredvs + + + + + + + + traffic-label.png: Traffic label specified while zone creation. + + + + + + + + + + Fields + Name + Description + + + + + 1 + Represents the name of the virtual / distributed virtual switch at + vCenter. + The default value depends on the type of virtual switch: + vSwitch0: If type of virtual switch is VMware + vNetwork Standard virtual switch + dvSwitch0: If type of virtual switch is VMware + vNetwork Distributed virtual switch + epp0: If type of virtual switch is Cisco Nexus + 1000v Distributed virtual switch + + + 2 + VLAN ID to be used for this traffic wherever applicable. + This field would be used for only public traffic as of now. In case of guest traffic this + field would be ignored and could be left empty for guest traffic. By default empty + string would be assumed which translates to untagged VLAN for that specific traffic + type. + + + 3 + Type of virtual switch. Specified as string. + Possible valid values are vmwaredvs, vmwaresvs, nexusdvs. + vmwaresvs: Represents VMware vNetwork Standard + virtual switch + vmwaredvs: Represents VMware vNetwork + distributed virtual switch + nexusdvs: Represents Cisco Nexus 1000v + distributed virtual switch. + If nothing specified (left empty), zone-level default virtual switch would be + defaulted, based on the value of global parameter you specify. + Following are the global configuration parameters: + vmware.use.dvswitch: Set to true to enable any + kind (VMware DVS and Cisco Nexus 1000v) of distributed virtual switch in a &PRODUCT; + deployment. If set to false, the virtual switch that can be used in that &PRODUCT; + deployment is Standard virtual switch. + vmware.use.nexus.vswitch: This parameter is + ignored if vmware.use.dvswitch is set to false. Set to true to enable Cisco Nexus + 1000v distributed virtual switch in a &PRODUCT; deployment. + + + + +>>>>>>> 6fc98a8... CLOUDSTACK-772 review comments on DVS fixed
Enabling Virtual Distributed Switch in &PRODUCT; From 6da0eb4b8bbaa7348b779674654ec4ce9c09c441 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 27 Aug 2013 14:23:30 +0530 Subject: [PATCH 017/251] CLOUDSTACK-772 review comments on DVS fixed --- docs/en-US/vmware-cluster-config-dvswitch.xml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml index 173055f40ca..311673a0e09 100644 --- a/docs/en-US/vmware-cluster-config-dvswitch.xml +++ b/docs/en-US/vmware-cluster-config-dvswitch.xml @@ -78,8 +78,6 @@ Use this VDS name when you specify the switch name in the traffic label while creating the zone. Traffic label format is [["Name of vSwitch/dvSwitch/EthernetPortProfile"][,"VLAN ID"[,"vSwitch Type"]]] -<<<<<<< HEAD -======= The possible values for traffic labels are: empty string @@ -155,7 +153,6 @@ ->>>>>>> 6fc98a8... CLOUDSTACK-772 review comments on DVS fixed
Enabling Virtual Distributed Switch in &PRODUCT; @@ -166,8 +163,8 @@ &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. Another global parameter that defines VDS configuration is vmware.ports.per.dvportgroup. This is the - default number of ports per VMware dvPortGroup in a VMware environment. This number directly - associated with the number of guest network you can create. + default number of ports per VMware dvPortGroup in a VMware environment. Default value is 256. + This number directly associated with the number of guest network you can create. &PRODUCT; supports orchestration of virtual networks in a deployment with a mix of Virtual Distributed Switch, Standard Virtual Switch and Nexus 1000v Virtual Switch.
@@ -257,5 +254,4 @@
- From 89c3297e56691ea99b8957bd9cb9f189ada560a3 Mon Sep 17 00:00:00 2001 From: venkataswamybabu budumuru Date: Tue, 27 Aug 2013 15:24:34 +0530 Subject: [PATCH 018/251] CLOUDSTACK-4378 : Fixing the typo in Apache License Text Signed-off-by: venkataswamybabu budumuru --- test/integration/component/test_affinity_groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/component/test_affinity_groups.py b/test/integration/component/test_affinity_groups.py index f8677ec9291..ae53e399df9 100644 --- a/test/integration/component/test_affinity_groups.py +++ b/test/integration/component/test_affinity_groups.py @@ -9,7 +9,7 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required byswa applicable law or agreed to in writing, +# 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 From d7da3ceaefe30a2833fa2c04ac2d8b0ab56d1c32 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 27 Aug 2013 15:27:35 +0530 Subject: [PATCH 019/251] CLOUDSTACK-4452: Fix the mount format to host:dir Signed-off-by: Prasanna Santhanam --- tools/marvin/marvin/integration/lib/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index 1512d52f868..c2403d4fb0a 100644 --- a/tools/marvin/marvin/integration/lib/utils.py +++ b/tools/marvin/marvin/integration/lib/utils.py @@ -283,7 +283,7 @@ def is_snapshot_on_nfs(apiclient, dbconn, config, zoneid, snapshotid): ) cmds = [ "mkdir -p %s /mnt/tmp", - "mount -t %s %s%s /mnt/tmp" % ( + "mount -t %s %s:%s /mnt/tmp" % ( 'nfs', host, path, From 7cc8ccead541f6089ed48d9a1c335daa458b74fc Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Tue, 27 Aug 2013 15:23:31 +0530 Subject: [PATCH 020/251] CLOUDSTACK-4471. If an instance fails to start then mark the volumes allocated as part of VM creation as removed and set the volume state as destroyed. --- server/src/com/cloud/storage/VolumeManagerImpl.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 6b7788b31c2..35fe72a43a3 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -2676,8 +2676,14 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { @Override public void destroyVolume(VolumeVO volume) { try { - volService.destroyVolume(volume.getId()); - } catch (ConcurrentOperationException e) { + // Mark volume as removed if volume has not been created on primary + if (volume.getState() == Volume.State.Allocated) { + _volsDao.remove(volume.getId()); + stateTransitTo(volume, Volume.Event.DestroyRequested); + } else { + volService.destroyVolume(volume.getId()); + } + } catch (Exception e) { s_logger.debug("Failed to destroy volume" + volume.getId(), e); throw new CloudRuntimeException("Failed to destroy volume" + volume.getId(), e); } From 6e64fa53c62d927e6dc4c0053abc5d3f9f8329dc Mon Sep 17 00:00:00 2001 From: Ashutosh Kelkar Date: Tue, 27 Aug 2013 15:49:20 +0530 Subject: [PATCH 021/251] CLOUDSTACK-4303: Add missing base.py changes egressdefaultpolicy parameter was not set in base.py. Therefore egress test failed. Added this missing change. Signed-off-by: Jayapal --- tools/marvin/marvin/integration/lib/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 3016ee4ca32..c528cea24fc 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -1493,6 +1493,10 @@ class NetworkOffering: cmd.specifyVlan = services["specifyVlan"] if "specifyIpRanges" in services: cmd.specifyIpRanges = services["specifyIpRanges"] + + if "egress_policy" in services: + cmd.egressdefaultpolicy = services["egress_policy"] + cmd.availability = 'Optional' [setattr(cmd, k, v) for k, v in kwargs.items()] From 37d500d2a6b71c267af20a83208eb6049157f95f Mon Sep 17 00:00:00 2001 From: Harikrishna Patnala Date: Tue, 27 Aug 2013 11:46:26 +0530 Subject: [PATCH 022/251] CLOUDSTACK-4482: getVMPassword() API call does not return password for Vms that are deployed with password enabled templates. Improving the error message saying no ssh key pair is assinged to VM to get the encrypted password and included a check for password enabled template Signed-off-by: Koushik Das --- server/src/com/cloud/server/ManagementServerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index d1e4e5f91a8..b20bf48cded 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -3673,7 +3673,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe _userVmDao.loadDetails(vm); String password = vm.getDetail("Encrypted.Password"); if (password == null || password.equals("")) { - InvalidParameterValueException ex = new InvalidParameterValueException("No password for VM with specified id found."); + InvalidParameterValueException ex = new InvalidParameterValueException("No password for VM with specified id found. " + + "If VM is created from password enabled template and SSH keypair is assigned to VM then only password can be retrieved."); ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } From f7018317131f42f77e9bba18e5e1c78457679afc Mon Sep 17 00:00:00 2001 From: Bharat Kumar Date: Tue, 27 Aug 2013 12:27:22 +0530 Subject: [PATCH 023/251] CLOUDSTACK-4498 we should not reserve memory and cpu for vmware VMs if the vmware.reserve.cpu and vmware.reserve.mem are set to false. Conflicts: plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java Signed-off-by: Jayapal --- .../com/cloud/hypervisor/guru/VMwareGuru.java | 12 +++++- .../vmware/manager/VmwareManagerImpl.java | 2 - .../vmware/resource/VmwareResource.java | 43 +++++++++++-------- .../src/com/cloud/configuration/Config.java | 4 +- 4 files changed, 39 insertions(+), 22 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 8aae8c87f93..7c70c6a6160 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -29,8 +29,11 @@ import javax.ejb.Local; import javax.inject.Inject; import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand; +import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; +import com.cloud.server.ConfigurationServer; import com.cloud.storage.Storage; +import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; @@ -108,12 +111,17 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { @Inject SecondaryStorageVmManager _secStorageMgr; @Inject NetworkModel _networkMgr; @Inject ConfigurationDao _configDao; + @Inject ConfigurationServer _configServer; @Inject NicDao _nicDao; @Inject PhysicalNetworkDao _physicalNetworkDao; @Inject PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao; + @Inject + VMInstanceDao _vmDao; + @Inject + ClusterDao _clusterDao; protected VMwareGuru() { super(); @@ -180,7 +188,9 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { break; } } - + long clusterId = _hostDao.findById( _vmDao.findById(vm.getId()).getHostId()).getClusterId(); + details.put(Config.VmwareReserveCpu.key(), _configServer.getConfigValue(Config.VmwareReserveCpu.key(), Config.ConfigurationParameterScope.cluster.toString(), clusterId)); + details.put(Config.VmwareReserveMem.key(), _configServer.getConfigValue(Config.VmwareReserveMem.key(), Config.ConfigurationParameterScope.cluster.toString(), clusterId)); to.setDetails(details); if(vm.getVirtualMachine() instanceof DomainRouterVO) { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 4d24562e833..b5e75354554 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -518,8 +518,6 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw params.put("vm.instancename.flag", _instanceNameFlag); params.put("service.console.name", _serviceConsoleName); params.put("management.portgroup.name", _managemetPortGroupName); - params.put("vmware.reserve.cpu", _reserveCpu); - params.put("vmware.reserve.mem", _reserveMem); params.put("vmware.root.disk.controller", _rootDiskController); params.put("vmware.recycle.hung.wokervm", _recycleHungWorker); params.put("ports.per.dvportgroup", _portsPerDvPortGroup); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index b612ac73ac2..6e4d230acbb 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -46,6 +46,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.configuration.Config; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; @@ -364,9 +365,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected boolean _fullCloneFlag = false; protected boolean _instanceNameFlag = false; - protected boolean _reserveCpu = false; - protected boolean _reserveMem = false; protected boolean _recycleHungWorker = false; protected DiskControllerType _rootDiskController = DiskControllerType.ide; @@ -2689,7 +2688,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); vmMo.ensureScsiDeviceController(); } else { - int ramMb = (int) (vmSpec.getMinRam() / (1024 * 1024)); Pair rootDiskDataStoreDetails = null; for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ROOT) { @@ -2707,7 +2705,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } if (!hyperHost.createBlankVm(vmNameOnVcenter, vmInternalCSName, vmSpec.getCpus(), vmSpec.getMaxSpeed().intValue(), - vmSpec.getMinSpeed(), vmSpec.getLimitCpuUse(),(int)(vmSpec.getMaxRam()/(1024*1024)), ramMb, + getReservedCpuMHZ(vmSpec), vmSpec.getLimitCpuUse(),(int)(vmSpec.getMaxRam()/(1024*1024)), getReservedMemoryMb(vmSpec), translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(), rootDiskDataStoreDetails.first(), false)) { throw new Exception("Failed to create VM. vmName: " + vmInternalCSName); } @@ -2732,11 +2730,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); - int ramMb = (int) (vmSpec.getMinRam() / (1024 * 1024)); String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(); VmwareHelper.setBasicVmConfig(vmConfigSpec, vmSpec.getCpus(), vmSpec.getMaxSpeed(), - vmSpec.getMinSpeed(),(int) (vmSpec.getMaxRam()/(1024*1024)), ramMb, + getReservedCpuMHZ(vmSpec),(int) (vmSpec.getMaxRam()/(1024*1024)), getReservedMemoryMb(vmSpec), guestOsId, vmSpec.getLimitCpuUse()); // Check for hotadd settings @@ -2988,7 +2985,27 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } } - + + int getReservedMemoryMb(VirtualMachineTO vmSpec) { + if (vmSpec.getDetails().get(Config.VmwareReserveMem.key()).equalsIgnoreCase("true")) { + return (int) (vmSpec.getMinRam() / (1024 * 1024)); + } else if (vmSpec.getMinRam() != vmSpec.getMaxRam()) { + s_logger.warn("memory overprovisioning factor is set to "+ (vmSpec.getMaxRam()/vmSpec.getMinRam())+" ignoring the flag vmware.reserve.mem"); + return (int) (vmSpec.getMinRam() / (1024 * 1024)); + } + return 0; + } + + int getReservedCpuMHZ(VirtualMachineTO vmSpec) { + if (vmSpec.getDetails().get(Config.VmwareReserveCpu.key()).equalsIgnoreCase("true")) { + return vmSpec.getMinSpeed(); + }else if (vmSpec.getMinSpeed().intValue() != vmSpec.getMaxSpeed().intValue()) { + s_logger.warn("cpu overprovisioning factor is set to "+ (vmSpec.getMaxSpeed().intValue()/vmSpec.getMinSpeed().intValue())+" ignoring the flag vmware.reserve.cpu"); + return vmSpec.getMinSpeed(); + } + return 0; + } + // return the finalized disk chain for startup, from top to bottom private String[] syncDiskChain(DatacenterMO dcMo, VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO vol, VirtualMachineDiskInfoBuilder diskInfoBuilder, @@ -6623,18 +6640,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa _privateNetworkVSwitchName = (String) params.get("private.network.vswitch.name"); } - String value = (String) params.get("vmware.reserve.cpu"); - if(value != null && value.equalsIgnoreCase("true")) - _reserveCpu = true; - - value = (String) params.get("vmware.recycle.hung.wokervm"); + String value = (String) params.get("vmware.recycle.hung.wokervm"); if(value != null && value.equalsIgnoreCase("true")) _recycleHungWorker = true; - - value = (String) params.get("vmware.reserve.mem"); - if(value != null && value.equalsIgnoreCase("true")) - _reserveMem = true; - + value = (String)params.get("vmware.root.disk.controller"); if(value != null && value.equalsIgnoreCase("scsi")) _rootDiskController = DiskControllerType.scsi; diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 1cc31b6023e..8dd5c8b07eb 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -293,8 +293,8 @@ public enum Config { VmwareAdditionalVncPortRangeStart("Advanced", ManagementServer.class, Integer.class, "vmware.additional.vnc.portrange.start", "50000", "Start port number of additional VNC port range", null), VmwareAdditionalVncPortRangeSize("Advanced", ManagementServer.class, Integer.class, "vmware.additional.vnc.portrange.size", "1000", "Start port number of additional VNC port range", null), //VmwareGuestNicDeviceType("Advanced", ManagementServer.class, String.class, "vmware.guest.nic.device.type", "E1000", "Ethernet card type used in guest VM, valid values are E1000, PCNet32, Vmxnet2, Vmxnet3", null), - VmwareReserveCpu("Advanced", ManagementServer.class, Boolean.class, "vmware.reserve.cpu", "false", "Specify whether or not to reserve CPU based on CPU overprovisioning factor", null), - VmwareReserveMem("Advanced", ManagementServer.class, Boolean.class, "vmware.reserve.mem", "false", "Specify whether or not to reserve memory based on memory overprovisioning factor", null), + VmwareReserveCpu("Advanced", ManagementServer.class, Boolean.class, "vmware.reserve.cpu", "false", "Specify whether or not to reserve CPU when not overprovisioning, In case of cpu overprovisioning we will always reserve cpu", null, ConfigurationParameterScope.cluster.toString()), + VmwareReserveMem("Advanced", ManagementServer.class, Boolean.class, "vmware.reserve.mem", "false", "Specify whether or not to reserve memory when not overprovisioning, In case of memory overprovisioning we will always reserve memory", null, ConfigurationParameterScope.cluster.toString()), VmwareRootDiskControllerType("Advanced", ManagementServer.class, String.class, "vmware.root.disk.controller", "ide", "Specify the default disk controller for root volumes, valid values are scsi, ide", null), VmwareSystemVmNicDeviceType("Advanced", ManagementServer.class, String.class, "vmware.systemvm.nic.device.type", "E1000", "Specify the default network device type for system VMs, valid values are E1000, PCNet32, Vmxnet2, Vmxnet3", null), VmwareRecycleHungWorker("Advanced", ManagementServer.class, Boolean.class, "vmware.recycle.hung.wokervm", "false", "Specify whether or not to recycle hung worker VMs", null), From 3315159de62620166c119dbf72dad9cf661cc0cb Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Tue, 27 Aug 2013 17:03:56 +0200 Subject: [PATCH 024/251] CLOUDSTACK-1191: Re-enable deployment of SystemVMs on RBD This was reverted by 0a5228922b822b57af72249912ed854281e1d85a unintentionnaly but broke this feature for RBD. Enable SystemVM deployment on RBD again --- .../storage/allocator/AbstractStoragePoolAllocator.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 89e09748ea3..e5a1d8bf864 100755 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -183,14 +183,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return false; } - - DiskOfferingVO diskOffering = _diskOfferingDao.findById(dskCh.getDiskOfferingId()); - if (diskOffering.getSystemUse() && pool.getPoolType() == StoragePoolType.RBD) { - s_logger.debug("Skipping RBD pool " + pool.getName() - + " as a suitable pool. RBD is not supported for System VM's"); - return false; - } - Long clusterId = pool.getClusterId(); ClusterVO cluster = _clusterDao.findById(clusterId); if (!(cluster.getHypervisorType() == dskCh.getHypervisorType())) { From 93b40b0ca6a796ffbacd80c40897f6ada6284bc4 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Tue, 27 Aug 2013 18:33:46 +0200 Subject: [PATCH 025/251] CLOUDSTACK-4423: Do not always append .qcow2 to volume names when copying Disk images are not always in the QCOW2 format, so don't assume that the filename will always and in .qcow2 --- .../cloud/hypervisor/kvm/storage/KVMStorageProcessor.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index fd5c9d9ff32..8482a8d25dc 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -319,6 +319,8 @@ public class KVMStorageProcessor implements StorageProcessor { DataTO destData = cmd.getDestTO(); DataStoreTO srcStore = srcData.getDataStore(); DataStoreTO destStore = destData.getDataStore(); + VolumeObjectTO srcVol = (VolumeObjectTO) srcData; + ImageFormat srcFormat = srcVol.getFormat(); PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) destStore; if (!(srcStore instanceof NfsTO)) { return new CopyCmdAnswer("can only handle nfs storage"); @@ -352,14 +354,16 @@ public class KVMStorageProcessor implements StorageProcessor { secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( secondaryStorageUrl + File.separator + volumeDir ); - if (!srcVolumeName.endsWith(".qcow2")) { + if (!srcVolumeName.endsWith(".qcow2") && srcFormat == ImageFormat.QCOW2) { srcVolumeName = srcVolumeName + ".qcow2"; } KVMPhysicalDisk volume = secondaryStoragePool .getPhysicalDisk(srcVolumeName); - storagePoolMgr.copyPhysicalDisk(volume, volumeName, + volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); + KVMPhysicalDisk newDisk = storagePoolMgr.copyPhysicalDisk(volume, volumeName, primaryPool); VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setFormat(ImageFormat.valueOf(newDisk.getFormat().toString().toUpperCase())); newVol.setPath(volumeName); return new CopyCmdAnswer(newVol); } catch (CloudRuntimeException e) { From 808183fdcc24fd6417d7bb806ee58a84b84868f4 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Tue, 27 Aug 2013 16:54:29 +0530 Subject: [PATCH 026/251] CLOUDSTACK-4443 [VMWARE]Failed to deploy VM's on VMWARE Standard vSwitch Legacy Zone after upgrade While upgrading to 4.2 to support existing clusters working over standard vswitch or nexus dvswitch should be supported as is. To ensure this while upgrading to 4.2, existing cluster's vswitch configuration needs to be persisted to database. If zone level traffic label is empty then default vswitch name would be used. Signed-off-by: Sateesh Chodapuneedi --- .../cloud/upgrade/dao/Upgrade410to420.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 81f11f00bc4..5db64200a9e 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -39,6 +39,7 @@ import org.apache.log4j.Logger; import com.cloud.deploy.DeploymentPlanner; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Networks.TrafficType; import com.cloud.network.vpc.NetworkACL; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; @@ -147,6 +148,10 @@ public class Upgrade410to420 implements DbUpgrade { boolean nexusEnabled = false; String publicVswitchType = VMWARE_STANDARD_VSWITCH; String guestVswitchType = VMWARE_STANDARD_VSWITCH; + String defaultPublicVswitchName = "vSwitch0"; + String defaultGuestVswitchName = "vSwitch0"; + String publicVswitchName = null; + String guestVswitchName = null; Map>> detailsMap = new HashMap>>(); List> detailsList; @@ -163,13 +168,27 @@ public class Upgrade410to420 implements DbUpgrade { nexusEnabled = true; } } + // Set default values if cloud level setting is turned on for nexus 1000v. if (nexusEnabled) { publicVswitchType = NEXUS_1000V_DVSWITCH; guestVswitchType = NEXUS_1000V_DVSWITCH; + defaultPublicVswitchName = "publicEthernetPortProfile"; + defaultGuestVswitchName = "guestEthernetPortProfile"; + } + // Read zone level settings from zone wide traffic labels for guest traffic and public traffic + guestVswitchName = getDefaultTrafficLabel(conn, TrafficType.Guest.toString()); + publicVswitchName = getDefaultTrafficLabel(conn, TrafficType.Public.toString()); + if (guestVswitchName == null) { + guestVswitchName = defaultGuestVswitchName; + } + if (publicVswitchName == null) { + publicVswitchName = defaultPublicVswitchName; } detailsList = new ArrayList>(); detailsList.add(new Pair(ApiConstants.VSWITCH_TYPE_GUEST_TRAFFIC, guestVswitchType)); + detailsList.add(new Pair(ApiConstants.VSWITCH_NAME_GUEST_TRAFFIC, guestVswitchName)); detailsList.add(new Pair(ApiConstants.VSWITCH_TYPE_PUBLIC_TRAFFIC, publicVswitchType)); + detailsList.add(new Pair(ApiConstants.VSWITCH_NAME_PUBLIC_TRAFFIC, publicVswitchName)); detailsMap.put(clusterId, detailsList); updateClusterDetails(conn, detailsMap); @@ -237,6 +256,34 @@ public class Upgrade410to420 implements DbUpgrade { } } + private String getDefaultTrafficLabel(Connection conn, String trafficType) { + ResultSet rs = null; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("select vmware_network_label from physical_network_traffic_types where vmware_network_label is not NULL and traffic_type='" + trafficType + "';"); + rs = pstmt.executeQuery(); + + while (rs.next()) { + String label = rs.getString("vmware_network_label"); + // Handle case of label specified as [vswitch_name,vlan_id] + return label.split(",")[0]; + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable read default traffic label for " + trafficType + ". ", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + return null; + } + private String getConfigurationParameter(Connection conn, String category, String paramName) { ResultSet rs = null; PreparedStatement pstmt = null; From 8099a6e200846878c2061c909609f88b437d8810 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 27 Aug 2013 11:38:31 -0700 Subject: [PATCH 027/251] CLOUDSTACK-4522: check before we do dynamic casting --- .../com/cloud/hypervisor/vmware/resource/VmwareResource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 6e4d230acbb..63d048266f1 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -3296,10 +3296,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa int scsiUnitNumber = 0; for(DiskTO vol: sortedDisks) { - VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - if (vol.getType() == Volume.Type.ISO) continue; + + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); From 47fa6d95611c596d4705ffc0f422e3dab70931a5 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 28 Aug 2013 00:31:15 +0530 Subject: [PATCH 028/251] CLOUDSTACK-3010: [VMWare] [SharedNetworkWithServices] router VM deployment fails with error "Message: Invalid configuration for device '2'." ensuring that direct network guru assigns a mac address for the nic that it designs --- server/src/com/cloud/network/guru/DirectNetworkGuru.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/src/com/cloud/network/guru/DirectNetworkGuru.java b/server/src/com/cloud/network/guru/DirectNetworkGuru.java index b4577ac096a..ca4f068135c 100755 --- a/server/src/com/cloud/network/guru/DirectNetworkGuru.java +++ b/server/src/com/cloud/network/guru/DirectNetworkGuru.java @@ -204,6 +204,13 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru { allocateDirectIp(nic, network, vm, dc, nic.getRequestedIpv4(), nic.getRequestedIpv6()); nic.setStrategy(ReservationStrategy.Create); + if (nic.getMacAddress() == null) { + nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId())); + if (nic.getMacAddress() == null) { + throw new InsufficientAddressCapacityException("Unable to allocate more mac addresses", Network.class, network.getId()); + } + } + return nic; } From a55c75bbd201e53f0aca3e8f8f9dee08cd132a95 Mon Sep 17 00:00:00 2001 From: Dave Cahill Date: Tue, 27 Aug 2013 11:55:49 -0700 Subject: [PATCH 029/251] CLOUDSTACK-4466: Fix DHCP capability breaks in 4.2 for MidoNet A recent code change in NetworkManager causes NullPointerExceptions when DHCP capability list is null. The commit which made the NetworkManager change also changed the VirtualRouter to not use null for the capabilitylist, but didn't make this change for other network devices, causing DHCP to fail on MidoNet. This change also updates the MidoNet plugin to use the most recent MidoNet API. Signed-off-by: Sheng Yang --- plugins/network-elements/midonet/pom.xml | 4 +- .../cloud/network/element/MidoNetElement.java | 151 ++++++++++-------- .../network/element/SimpleFirewallRule.java | 20 ++- .../network/guru/MidoNetGuestNetworkGuru.java | 11 -- .../network/resource/MidoNetVifDriver.java | 18 +-- .../network/element/MidoNetElementTest.java | 4 +- 6 files changed, 106 insertions(+), 102 deletions(-) diff --git a/plugins/network-elements/midonet/pom.xml b/plugins/network-elements/midonet/pom.xml index 69eeb702eeb..c7dbdf29791 100644 --- a/plugins/network-elements/midonet/pom.xml +++ b/plugins/network-elements/midonet/pom.xml @@ -35,9 +35,9 @@ - com.midokura + org.midonet midonet-client - 12.12.2 + 1.1.0 org.mockito diff --git a/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java b/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java index ab6a6def405..42a6795f579 100644 --- a/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java +++ b/plugins/network-elements/midonet/src/com/cloud/network/element/MidoNetElement.java @@ -54,19 +54,21 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; -import com.midokura.midonet.client.MidonetApi; -import com.midokura.midonet.client.dto.DtoRule; -import com.midokura.midonet.client.resource.Bridge; -import com.midokura.midonet.client.resource.BridgePort; -import com.midokura.midonet.client.resource.DhcpHost; -import com.midokura.midonet.client.resource.DhcpSubnet; -import com.midokura.midonet.client.resource.Port; -import com.midokura.midonet.client.resource.ResourceCollection; -import com.midokura.midonet.client.resource.Route; -import com.midokura.midonet.client.resource.Router; -import com.midokura.midonet.client.resource.RouterPort; -import com.midokura.midonet.client.resource.Rule; -import com.midokura.midonet.client.resource.RuleChain; +import org.midonet.client.MidonetApi; +import org.midonet.client.exception.HttpInternalServerError; +import org.midonet.client.dto.DtoRule; +import org.midonet.client.dto.DtoRule.DtoRange; +import org.midonet.client.resource.Bridge; +import org.midonet.client.resource.BridgePort; +import org.midonet.client.resource.DhcpHost; +import org.midonet.client.resource.DhcpSubnet; +import org.midonet.client.resource.Port; +import org.midonet.client.resource.ResourceCollection; +import org.midonet.client.resource.Route; +import org.midonet.client.resource.Router; +import org.midonet.client.resource.RouterPort; +import org.midonet.client.resource.Rule; +import org.midonet.client.resource.RuleChain; import com.sun.jersey.core.util.MultivaluedMapImpl; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -420,7 +422,9 @@ public class MidoNetElement extends AdapterBase implements sub.subnetLength(cidrInfo.second()); sub.subnetPrefix(cidrInfo.first()); sub.defaultGateway(network.getGateway()); - sub.dnsServerAddr(dest.getDataCenter().getDns1()); + List dcs = new ArrayList(); + dcs.add(dest.getDataCenter().getDns1()); + sub.dnsServerAddrs(dcs); sub.create(); } @@ -529,10 +533,8 @@ public class MidoNetElement extends AdapterBase implements .nwDstAddress(floatingIp) .nwDstLength(32) .nwProto(SimpleFirewallRule.stringToProtocolNumber("icmp")) - .tpSrcStart(0) - .tpSrcEnd(0) - .tpDstStart(0) - .tpDstEnd(0) + .tpSrc(new DtoRange(0,0)) + .tpDst(new DtoRange(0,0)) .position(2) .create(); @@ -685,52 +687,56 @@ public class MidoNetElement extends AdapterBase implements } for (FirewallRule rule : rulesToApply) { - IpAddress dstIp = _networkModel.getIp(rule.getSourceIpAddressId()); - FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, dstIp.getAddress().addr()); + if (rule.getState() == FirewallRule.State.Revoke || rule.getState() == FirewallRule.State.Add) { + IpAddress dstIp = _networkModel.getIp(rule.getSourceIpAddressId()); + FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, dstIp.getAddress().addr()); - // Convert to string representation - SimpleFirewallRule fwRule = new SimpleFirewallRule(ruleTO); - String[] ruleStrings = fwRule.toStringArray(); + // Convert to string representation + SimpleFirewallRule fwRule = new SimpleFirewallRule(ruleTO); + String[] ruleStrings = fwRule.toStringArray(); - if (rule.getState() == FirewallRule.State.Revoke) { - // Lookup in existingRules, delete if present - for(String revokeRuleString : ruleStrings){ - Rule foundRule = existingRules.get(revokeRuleString); - if(foundRule != null){ - foundRule.delete(); - } - } - } else if (rule.getState() == FirewallRule.State.Add) { - // Lookup in existingRules, add if not present - for(int i = 0; i < ruleStrings.length; i++){ - String ruleString = ruleStrings[i]; - Rule foundRule = existingRules.get(ruleString); - if(foundRule == null){ - // Get the cidr for the related entry in the Source Cidrs list - String relatedCidr = fwRule.sourceCidrs.get(i); - Pair cidrParts = NetUtils.getCidr(relatedCidr); - - // Create rule with correct proto, cidr, ACCEPT, dst IP - Rule toApply = preFilter.addRule() - .type(DtoRule.Jump) - .jumpChainId(preNat.getId()) - .position(1) - .nwSrcAddress(cidrParts.first()) - .nwSrcLength(cidrParts.second()) - .nwDstAddress(ruleTO.getSrcIp()) - .nwDstLength(32) - .nwProto(SimpleFirewallRule.stringToProtocolNumber(rule.getProtocol())); - - if(rule.getProtocol().equals("icmp")){ - // ICMP rules - reuse port fields - toApply.tpSrcStart(fwRule.icmpType).tpSrcEnd(fwRule.icmpType) - .tpDstStart(fwRule.icmpCode).tpDstEnd(fwRule.icmpCode); - } else { - toApply.tpDstStart(fwRule.dstPortStart) - .tpDstEnd(fwRule.dstPortEnd); + if (rule.getState() == FirewallRule.State.Revoke) { + // Lookup in existingRules, delete if present + for(String revokeRuleString : ruleStrings){ + Rule foundRule = existingRules.get(revokeRuleString); + if(foundRule != null){ + foundRule.delete(); } + } + } else if (rule.getState() == FirewallRule.State.Add) { + // Lookup in existingRules, add if not present + for(int i = 0; i < ruleStrings.length; i++){ + String ruleString = ruleStrings[i]; + Rule foundRule = existingRules.get(ruleString); + if(foundRule == null){ + // Get the cidr for the related entry in the Source Cidrs list + String relatedCidr = fwRule.sourceCidrs.get(i); + Pair cidrParts = NetUtils.getCidr(relatedCidr); - toApply.create(); + // Create rule with correct proto, cidr, ACCEPT, dst IP + Rule toApply = preFilter.addRule() + .type(DtoRule.Jump) + .jumpChainId(preNat.getId()) + .position(1) + .nwSrcAddress(cidrParts.first()) + .nwSrcLength(cidrParts.second()) + .nwDstAddress(ruleTO.getSrcIp()) + .nwDstLength(32) + .nwProto(SimpleFirewallRule.stringToProtocolNumber(rule.getProtocol())); + + if(rule.getProtocol().equals("icmp")){ + // ICMP rules - reuse port fields + // (-1, -1) means "allow all ICMP", so we don't set tpSrc / tpDst + if(fwRule.icmpType != -1 | fwRule.icmpCode != -1){ + toApply.tpSrc(new DtoRange(fwRule.icmpType, fwRule.icmpType)) + .tpDst(new DtoRange(fwRule.icmpCode, fwRule.icmpCode)); + } + } else { + toApply.tpDst(new DtoRange(fwRule.dstPortStart, fwRule.dstPortEnd)); + } + + toApply.create(); + } } } } @@ -969,8 +975,11 @@ public class MidoNetElement extends AdapterBase implements // Rules in the preNat table Map existingPreNatRules = new HashMap(); for (Rule existingRule : preNat.getRules()) { - String ruleString = new SimpleFirewallRule(existingRule).toStringArray()[0]; - existingPreNatRules.put(ruleString, existingRule); + // The "port forwarding" rules we're interested in are dnat rules where src / dst ports are specified + if(existingRule.getType().equals(DtoRule.DNAT) && existingRule.getTpDst() != null){ + String ruleString = new SimpleFirewallRule(existingRule).toStringArray()[0]; + existingPreNatRules.put(ruleString, existingRule); + } } /* @@ -1054,8 +1063,7 @@ public class MidoNetElement extends AdapterBase implements .flowAction(DtoRule.Accept) .nwDstAddress(publicIp) .nwDstLength(32) - .tpDstStart(pubPortStart) - .tpDstEnd(pubPortEnd) + .tpDst(new DtoRange(pubPortStart, pubPortEnd)) .natTargets(preTargets) .nwProto(SimpleFirewallRule.stringToProtocolNumber(rule.getProtocol())) .position(1); @@ -1119,7 +1127,8 @@ public class MidoNetElement extends AdapterBase implements capabilities.put(Service.Gateway, null); // L3 Support : DHCP - capabilities.put(Service.Dhcp, null); + Map dhcpCapabilities = new HashMap(); + capabilities.put(Service.Dhcp, dhcpCapabilities); // L3 Support : SourceNat Map sourceNatCapabilities = new HashMap(); @@ -1360,7 +1369,7 @@ public class MidoNetElement extends AdapterBase implements int pos = 1; // If it is ARP, accept it egressChain.addRule().type(DtoRule.Accept) - .dlType((short)0x0806) + .dlType(0x0806) .position(pos++) .create(); @@ -1419,7 +1428,7 @@ public class MidoNetElement extends AdapterBase implements // If it is ARP, accept it inc.addRule().type(DtoRule.Accept) - .dlType((short)0x0806) + .dlType(0x0806) .position(pos++) .create(); @@ -1483,7 +1492,13 @@ public class MidoNetElement extends AdapterBase implements String networkUUIDStr = String.valueOf(networkID); - netBridge = api.addBridge().tenantId(accountUuid).name(networkUUIDStr).create(); + s_logger.debug("Attempting to create guest network bridge"); + try { + netBridge = api.addBridge().tenantId(accountUuid).name(networkUUIDStr).create(); + } catch (HttpInternalServerError ex) { + s_logger.warn("Bridge creation failed, retrying bridge get in case it now exists.", ex); + netBridge = getNetworkBridge(networkID, accountUuid); + } } return netBridge; } diff --git a/plugins/network-elements/midonet/src/com/cloud/network/element/SimpleFirewallRule.java b/plugins/network-elements/midonet/src/com/cloud/network/element/SimpleFirewallRule.java index a6b78d84129..e8d81ef1865 100644 --- a/plugins/network-elements/midonet/src/com/cloud/network/element/SimpleFirewallRule.java +++ b/plugins/network-elements/midonet/src/com/cloud/network/element/SimpleFirewallRule.java @@ -21,9 +21,10 @@ package com.cloud.network.element; import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.NetworkACLTO; import com.cloud.agent.api.to.PortForwardingRuleTO; -import com.midokura.midonet.client.dto.DtoRule; +import org.midonet.client.dto.DtoRule; +import org.midonet.client.resource.*; import com.google.common.collect.*; -import com.midokura.midonet.client.resource.*; + import java.util.*; // Used for translation between MidoNet firewall rules and @@ -147,8 +148,14 @@ public class SimpleFirewallRule { dstIp = rule.getNwDstAddress(); if("icmp".equals(protocol)){ - icmpType = rule.getTpSrcStart(); - icmpCode = rule.getTpDstStart(); + if(rule.getTpSrc() != null && rule.getTpDst() != null){ + icmpType = rule.getTpSrc().start; + icmpCode = rule.getTpDst().start; + } else { + icmpType = -1; + icmpCode = -1; + } + } else { /* * If this is port forwarding, we want to take the start @@ -158,9 +165,9 @@ public class SimpleFirewallRule { if (targets != null) { dstPortStart = targets[0].portFrom; } else { - dstPortStart = rule.getTpDstStart(); + dstPortStart = rule.getTpDst().start; } - dstPortEnd = rule.getTpDstEnd(); + dstPortEnd = rule.getTpDst().end; } // cidr, protocol, dstIp, dstPortStart, dstPortEnd, icmpType, icmpCode); @@ -177,6 +184,7 @@ public class SimpleFirewallRule { public int getFieldOne(){ if(protocol.equals("icmp")){ return icmpType; + } else { return dstPortStart; } diff --git a/plugins/network-elements/midonet/src/com/cloud/network/guru/MidoNetGuestNetworkGuru.java b/plugins/network-elements/midonet/src/com/cloud/network/guru/MidoNetGuestNetworkGuru.java index d57affc5827..acc574ec9c9 100644 --- a/plugins/network-elements/midonet/src/com/cloud/network/guru/MidoNetGuestNetworkGuru.java +++ b/plugins/network-elements/midonet/src/com/cloud/network/guru/MidoNetGuestNetworkGuru.java @@ -19,8 +19,6 @@ package com.cloud.network.guru; -import com.cloud.network.element.MidoNetElement; -import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; @@ -33,21 +31,12 @@ import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; import com.cloud.vm.*; -import com.midokura.midonet.client.resource.Bridge; -import com.cloud.utils.net.NetUtils; - -import com.cloud.network.Networks.AddressFormat; -import com.midokura.midonet.client.resource.BridgePort; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; - -import com.cloud.vm.Nic.ReservationStrategy; - import javax.ejb.Local; -import java.util.UUID; import javax.inject.Inject; @Component diff --git a/plugins/network-elements/midonet/src/com/cloud/network/resource/MidoNetVifDriver.java b/plugins/network-elements/midonet/src/com/cloud/network/resource/MidoNetVifDriver.java index 3c7c23d669f..9c21636fd15 100644 --- a/plugins/network-elements/midonet/src/com/cloud/network/resource/MidoNetVifDriver.java +++ b/plugins/network-elements/midonet/src/com/cloud/network/resource/MidoNetVifDriver.java @@ -21,32 +21,24 @@ package com.cloud.network.resource; import com.cloud.hypervisor.kvm.resource.*; import com.cloud.agent.api.to.NicTO; -import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource; import com.cloud.exception.InternalErrorException; import com.cloud.network.Networks; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -import com.midokura.midonet.client.resource.Bridge; -import com.midokura.midonet.client.resource.Router; -import com.midokura.midonet.client.resource.BridgePort; -import com.midokura.midonet.client.resource.RouterPort; -import com.midokura.midonet.client.resource.Host; import org.apache.log4j.Logger; import org.libvirt.LibvirtException; - import com.sun.jersey.core.util.MultivaluedMapImpl; -import com.midokura.midonet.client.MidonetApi; - import javax.ws.rs.core.MultivaluedMap; - import javax.naming.ConfigurationException; -import javax.ws.rs.core.MultivaluedMap; -import java.net.URI; import java.util.Map; import java.util.UUID; +import org.midonet.client.resource.Bridge; +import org.midonet.client.resource.BridgePort; +import org.midonet.client.resource.Host; +import org.midonet.client.MidonetApi; + public class MidoNetVifDriver extends VifDriverBase { private static final Logger s_logger = Logger diff --git a/plugins/network-elements/midonet/test/com/cloud/network/element/MidoNetElementTest.java b/plugins/network-elements/midonet/test/com/cloud/network/element/MidoNetElementTest.java index a7d96b0c310..47b16b81682 100644 --- a/plugins/network-elements/midonet/test/com/cloud/network/element/MidoNetElementTest.java +++ b/plugins/network-elements/midonet/test/com/cloud/network/element/MidoNetElementTest.java @@ -24,8 +24,8 @@ import com.cloud.user.dao.AccountDao; import junit.framework.TestCase; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; -import com.midokura.midonet.client.MidonetApi; -import com.midokura.midonet.client.resource.*; +import org.midonet.client.MidonetApi; +import org.midonet.client.resource.*; import com.sun.jersey.core.util.MultivaluedMapImpl; import com.cloud.network.*; import com.cloud.vm.*; From 2d08a322dd976e696783535a0f3d174483adf155 Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 27 Aug 2013 13:37:40 -0700 Subject: [PATCH 030/251] CLOUDSTACK-2121. DOC. New docs for affinity groups. --- docs/en-US/accounts-users-domains.xml | 33 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/en-US/accounts-users-domains.xml b/docs/en-US/accounts-users-domains.xml index a3f5837db8e..34372a7871c 100644 --- a/docs/en-US/accounts-users-domains.xml +++ b/docs/en-US/accounts-users-domains.xml @@ -46,8 +46,33 @@ Root Administrator Root administrators have complete access to the system, including managing templates, service offerings, customer care administrators, and domains - The resources belong to the account, not individual users in that account. For example, - billing, resource limits, and so on are maintained by the account, not the users. A user can - operate on any resource in the account provided the user has privileges for that operation. - The privileges are determined by the role. + + Resource Ownership + Resources belong to the account, not individual users in that account. For example, + billing, resource limits, and so on are maintained by the account, not the users. A user can + operate on any resource in the account provided the user has privileges for that operation. + The privileges are determined by the role. + +
+ Dedicating Resources to Accounts and Domains + You can dedicate infrastructure resources including zones, pods, clusters, or hosts to an account or domain. + + The root administrator can dedicate resources to a specific domain or account + that needs private infrastructure for additional security or performance guarantees. + A zone, pod, cluster, or host can be reserved by the root administrator for a specific domain or account. + Only users in that domain or its subdomain may use the infrastructure. + For example, only users in a given domain can create guests in a zone dedicated to that domain. + There are several types of dedication available: + + + To explicitly dedicate a resource, use the explicit-dedicated type of Affinity Group. + For example, when creating a new VM, an end user can choose to place it on dedicated infrastructure. + See . + You can also use strict implicit dedication. + Strict Implicit dedication, when requested, means, a host will not be shared across multiple accounts – as an example, here is a reason: + for deployment of certain types of applications, such as desktops, due to licensing reasons, no host can be shared between different accounts. + You can also implicitly dedicate a resource with "preferred" implicit dedication. This means that the resource will be deployed + in dedicated infrastructure if possible. Otherwise, the resource can be deployed in shared infrastructure. + +
From ee0b1d768af0543d8d023d118d30fbc340da4e83 Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 27 Aug 2013 13:48:59 -0700 Subject: [PATCH 031/251] CLOUDSTACK-2121. DOC. New docs for affinity groups. --- docs/en-US/host-allocation.xml | 91 ++++++++++++++++++- docs/en-US/images/change-affinity-button.png | Bin 0 -> 8570 bytes docs/en-US/stopping-and-starting-vms.xml | 2 +- docs/en-US/virtual-machines.xml | 1 + docs/en-US/working-with-hosts.xml | 1 - 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 docs/en-US/images/change-affinity-button.png diff --git a/docs/en-US/host-allocation.xml b/docs/en-US/host-allocation.xml index d287856ecf9..9e752afbd53 100644 --- a/docs/en-US/host-allocation.xml +++ b/docs/en-US/host-allocation.xml @@ -23,9 +23,90 @@ -->
- Host Allocation - The system automatically picks the most appropriate host to run each virtual machine. End users may specify the zone in which the virtual machine will be created. End users do not have control over which host will run the virtual machine instance. - &PRODUCT; administrators can specify that certain hosts should have a preference for particular types of guest instances. For example, an administrator could state that a host should have a preference to run Windows guests. The default host allocator will attempt to place guests of that OS type on such hosts first. If no such host is available, the allocator will place the instance wherever there is sufficient physical capacity. - Both vertical and horizontal allocation is allowed. Vertical allocation consumes all the resources of a given host before allocating any guests on a second host. This reduces power consumption in the cloud. Horizontal allocation places a guest on each host in a round-robin fashion. This may yield better performance to the guests in some cases. &PRODUCT; also allows an element of CPU over-provisioning as configured by the administrator. Over-provisioning allows the administrator to commit more CPU cycles to the allocated guests than are actually available from the hardware. - &PRODUCT; also provides a pluggable interface for adding new allocators. These custom allocators can provide any policy the administrator desires. + Assigning VMs to Hosts + At any point in time, each virtual machine instance is running on a single host. + How does &PRODUCT; determine which host to place a VM on? There are several ways: + + Automatic default host allocation. &PRODUCT; can automatically pick + the most appropriate host to run each virtual machine. + Instance type preferences. &PRODUCT; administrators can specify that certain hosts should have a preference for particular types of guest instances. + For example, an administrator could state that a host should have a preference to run Windows guests. + The default host allocator will attempt to place guests of that OS type on such hosts first. + If no such host is available, the allocator will place the instance wherever there is sufficient physical capacity. + Vertical and horizontal allocation. + Vertical allocation consumes all the resources of a given host before allocating any guests on a second host. + This reduces power consumption in the cloud. Horizontal allocation places a guest on each host in a round-robin fashion. + This may yield better performance to the guests in some cases. + End user preferences. + Users can not control exactly which host will run a given VM instance, + but they can specify a zone for the VM. + &PRODUCT; is then restricted to allocating the VM only to one of the hosts in that zone. + Host tags. The administrator can assign tags to hosts. These tags can be used to + specify which host a VM should use. + The &PRODUCT; administrator decides whether to define host tags, then create a service offering using those tags and offer it to the user. + + Affinity groups. + By defining affinity groups and assigning VMs to them, the user or administrator can + influence (but not dictate) which VMs should run on separate hosts. + This feature is to let users specify that certain VMs won't be on the same host. + &PRODUCT; also provides a pluggable interface for adding new allocators. + These custom allocators can provide any policy the administrator desires. + +
+ Affinity Groups + By defining affinity groups and assigning VMs to them, the user or administrator can + influence (but not dictate) which VMs should run on separate hosts. + This feature is to let users specify that VMs with the same “host anti-affinity” type won’t be on the same host. + This serves to increase fault tolerance. + If a host fails, another VM offering the same service (for example, hosting the user's website) is still up and running on another host. + The scope of an affinity group is per user account. + Creating a New Affinity Group + To add an affinity group: + + Log in to the &PRODUCT; UI as an administrator or user. + In the left navigation bar, click Affinity Groups. + Click Add affinity group. In the dialog box, fill in the following fields: + + Name. Give the group a name. + Description. Any desired text to tell more about the purpose of the group. + Type. The only supported type shipped with &PRODUCT; is Host Anti-Affinity. + This indicates that the VMs in this group should avoid being placed on the same VM with each other. + If you see other types in this list, it means that your installation of &PRODUCT; has been extended + with customized affinity group plugins. + + + + Assign a New VM to an Affinity Group + To assign a new VM to an affinity group: + + Create the VM as usual, as described in . + In the Add Instance wizard, there is a new Affinity tab where you can select the affinity group. + + Change Affinity Group for an Existing VM + To assign an existing VM to an affinity group: + + Log in to the &PRODUCT; UI as an administrator or user. + In the left navigation bar, click Instances. + Click the name of the VM you want to work with. + Stop the VM by clicking the Stop button. + Click the Change Affinity button. + + + + + change-affinity-button.png: button to assign an affinity group + to a virtual machine + + + + + View Members of an Affinity Group + To see which VMs are currently assigned to a particular affinity group: + + In the left navigation bar, click Affinity Groups. + Click the name of the group you are interested in. + Click View Instances. The members of the group are listed. + From here, you can click the name of any VM in the list to access all its details and controls. + +
diff --git a/docs/en-US/images/change-affinity-button.png b/docs/en-US/images/change-affinity-button.png new file mode 100644 index 0000000000000000000000000000000000000000..36b4ae33fd4174b771a92044cfb3d810f5b8db96 GIT binary patch literal 8570 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 AS^xk5 literal 0 HcmV?d00001 diff --git a/docs/en-US/stopping-and-starting-vms.xml b/docs/en-US/stopping-and-starting-vms.xml index 1c8bd808394..25c1f494b92 100644 --- a/docs/en-US/stopping-and-starting-vms.xml +++ b/docs/en-US/stopping-and-starting-vms.xml @@ -24,6 +24,6 @@
Stopping and Starting VMs - Once a VM instance is created, you can stop, restart, or delete it as needed. In the &PRODUCT; UI, click Instances, select the VM, and use the Stop, Start, Reboot, and Destroy links. + Once a VM instance is created, you can stop, restart, or delete it as needed. In the &PRODUCT; UI, click Instances, select the VM, and use the Stop, Start, Reboot, and Destroy buttons.
diff --git a/docs/en-US/virtual-machines.xml b/docs/en-US/virtual-machines.xml index 0effb97c41e..8d8847853db 100644 --- a/docs/en-US/virtual-machines.xml +++ b/docs/en-US/virtual-machines.xml @@ -26,6 +26,7 @@ + diff --git a/docs/en-US/working-with-hosts.xml b/docs/en-US/working-with-hosts.xml index 93adcfc2b4a..d1fc74fd207 100644 --- a/docs/en-US/working-with-hosts.xml +++ b/docs/en-US/working-with-hosts.xml @@ -34,7 +34,6 @@ - From 13d9070d5e11fc8b15156b4d3978446837904641 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 27 Aug 2013 13:57:04 -0700 Subject: [PATCH 032/251] CLOUDSTACK-4520: [upgrade][vmware]ExtractVolumeCmd fails with NPE while attempting to download a volume. --- engine/schema/src/com/cloud/storage/VolumeVO.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java index bbecf0346c3..ea3d6bffa67 100755 --- a/engine/schema/src/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/com/cloud/storage/VolumeVO.java @@ -238,6 +238,7 @@ public class VolumeVO implements Volume { this.chainInfo = that.getChainInfo(); this.templateId = that.getTemplateId(); this.deviceId = that.getDeviceId(); + this.format = that.getFormat(); this.uuid = UUID.randomUUID().toString(); } From 21cb33a02ce2ff1e1b22af275068451a3ab6add5 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 27 Aug 2013 14:11:55 -0700 Subject: [PATCH 033/251] CLOUDSTACK-4516:[Upgrade][VMWare] MySQLIntegrityConstraintViolationException while performing any task using local storage after upgrade from 3.0.7 to 4.2. --- .../storage/endpoint/DefaultEndPointSelector.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java index a1d7315c899..98c6a3fc001 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java +++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java @@ -96,13 +96,13 @@ public class DefaultEndPointSelector implements EndPointSelector { sbuilder.append(sqlBase); if (scope.getScopeType() == ScopeType.HOST) { - sbuilder.append(" and id = "); + sbuilder.append(" and h.id = "); sbuilder.append(scope.getScopeId()); } else if (scope.getScopeType() == ScopeType.CLUSTER) { - sbuilder.append(" and cluster_id = "); + sbuilder.append(" and h.cluster_id = "); sbuilder.append(scope.getScopeId()); } else if (scope.getScopeType() == ScopeType.ZONE) { - sbuilder.append(" and data_center_id = "); + sbuilder.append(" and h.data_center_id = "); sbuilder.append(scope.getScopeId()); } // TODO: order by rand() is slow if there are lot of hosts From cfe3a0fee39379c4ab1fdad9150924b9e46becd6 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 27 Aug 2013 14:15:50 -0700 Subject: [PATCH 034/251] CLOUDSTACK-4459: one more try, bypass libvirt to find volume if libvirt call failed --- .../kvm/storage/LibvirtStoragePool.java | 32 ++++++++++++- .../cloud/template/TemplateManagerImpl.java | 47 ++++++++++++------- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 2ce517504d6..dc7981c336b 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -16,14 +16,19 @@ // under the License. package com.cloud.hypervisor.kvm.storage; +import java.io.File; import java.util.List; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.log4j.Logger; import org.libvirt.StoragePool; import com.cloud.storage.Storage.StoragePoolType; public class LibvirtStoragePool implements KVMStoragePool { + private static final Logger s_logger = Logger + .getLogger(LibvirtStoragePool.class); protected String uuid; protected String uri; protected long capacity; @@ -120,7 +125,32 @@ public class LibvirtStoragePool implements KVMStoragePool { @Override public KVMPhysicalDisk getPhysicalDisk(String volumeUuid) { - return this._storageAdaptor.getPhysicalDisk(volumeUuid, this); + KVMPhysicalDisk disk = null; + try { + disk = this._storageAdaptor.getPhysicalDisk(volumeUuid, this); + } catch (CloudRuntimeException e) { + if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) || + (this.getStoragePoolType() != StoragePoolType.Filesystem)) { + throw e; + } + } + + if (disk != null) { + return disk; + } + s_logger.debug("find volume bypass libvirt"); + //For network file system or file system, try to use java file to find the volume, instead of through libvirt. BUG:CLOUDSTACK-4459 + String localPoolPath = this.getLocalPath(); + File f = new File(localPoolPath + File.separator + volumeUuid); + if (!f.exists()) { + s_logger.debug("volume: " + volumeUuid + " not exist on storage pool"); + throw new CloudRuntimeException("Can't find volume:" + volumeUuid); + } + disk = new KVMPhysicalDisk(f.getPath(), volumeUuid, this); + disk.setFormat(PhysicalDiskFormat.QCOW2); + disk.setSize(f.length()); + disk.setVirtualSize(f.length()); + return disk; } @Override diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index a263720e9f8..2a0e04dfbee 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -780,29 +780,42 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } @Override + @DB public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) { - StoragePool pool = (StoragePool) this._dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); - VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Evicting " + templatePoolVO); + //Need to hold the lock, otherwise, another thread may create a volume from the template at the same time. + //Assumption here is that, we will hold the same lock during create volume from template + VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolVO.getId()); + if (templatePoolRef == null) { + s_logger.debug("can't aquire the lock for template pool ref:" + templatePoolVO.getId()); + return; } - DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); try { - Answer answer = _storageMgr.sendToPool(pool, cmd); + StoragePool pool = (StoragePool) this._dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); + VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); - if (answer != null && answer.getResult()) { - // Remove the templatePoolVO - if (_tmpltPoolDao.remove(templatePoolVO.getId())) { - s_logger.debug("Successfully evicted template: " + template.getName() + " from storage pool: " + pool.getName()); - } - } else { - s_logger.info("Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Evicting " + templatePoolVO); } - } catch (StorageUnavailableException e) { - s_logger.info("Storage is unavailable currently. Will retry evicte template: " + template.getName() + " from storage pool: " - + pool.getName()); + DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); + + try { + Answer answer = _storageMgr.sendToPool(pool, cmd); + + if (answer != null && answer.getResult()) { + // Remove the templatePoolVO + if (_tmpltPoolDao.remove(templatePoolVO.getId())) { + s_logger.debug("Successfully evicted template: " + template.getName() + " from storage pool: " + pool.getName()); + } + } else { + s_logger.info("Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); + } + } catch (StorageUnavailableException e) { + s_logger.info("Storage is unavailable currently. Will retry evicte template: " + template.getName() + " from storage pool: " + + pool.getName()); + } + } finally { + _tmpltPoolDao.releaseFromLockTable(templatePoolRef.getId()); } } From 8e350cb296af6bb457057847941752712a6d022e Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 27 Aug 2013 15:04:24 -0700 Subject: [PATCH 035/251] turn off nfs cache --- python/lib/cloudutils/serviceConfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/lib/cloudutils/serviceConfig.py b/python/lib/cloudutils/serviceConfig.py index d129e00c45b..5c552c0b756 100755 --- a/python/lib/cloudutils/serviceConfig.py +++ b/python/lib/cloudutils/serviceConfig.py @@ -388,7 +388,8 @@ class nfsConfig(serviceCfgBase): return True cfo = configFileOps("/etc/nfsmount.conf") - cfo.addEntry("AC", "False") + cfo.addEntry("Ac", "False") + cfo.addEntry("actimeo", "0") cfo.save() self.syscfg.svo.enableService("rpcbind") From 687e87faa7a2d828fa428ac0b013e0c1de361bdc Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 27 Aug 2013 15:04:17 -0700 Subject: [PATCH 036/251] CLOUDSTACK-4529: use property collector to collect all information in one round --- .../com/cloud/hypervisor/vmware/mo/HostMO.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index c0ef2175af5..49a340c366e 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -35,6 +35,7 @@ import com.vmware.vim25.AboutInfo; import com.vmware.vim25.AlreadyExistsFaultMsg; import com.vmware.vim25.ClusterDasConfigInfo; import com.vmware.vim25.ComputeResourceSummary; +import com.vmware.vim25.CustomFieldStringValue; import com.vmware.vim25.DatastoreSummary; import com.vmware.vim25.DynamicProperty; import com.vmware.vim25.HostConfigManager; @@ -577,8 +578,14 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { } public HashMap getVmVncPortsOnHost() throws Exception { + + int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + ObjectContent[] ocs = getVmPropertiesOnHyperHost( - new String[] { "name", "config.extraConfig[\"RemoteDisplay.vnc.port\"]" } + new String[] { "name", "config.extraConfig[\"RemoteDisplay.vnc.port\"]", "value[" + key + "]" } ); HashMap portInfo = new HashMap(); @@ -592,14 +599,15 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { for(DynamicProperty objProp : objProps) { if(objProp.getName().equals("name")) { vmName = (String)objProp.getVal(); + } else if(objProp.getName().startsWith("value[")) { + if(objProp.getVal() != null) + vmInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue(); } else { OptionValue optValue = (OptionValue)objProp.getVal(); value = (String)optValue.getValue(); } } - VirtualMachineMO vmMo = new VirtualMachineMO(_context, oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - vmInternalCSName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if (vmInternalCSName != null && isUserVMInternalCSName(vmInternalCSName)) vmName = vmInternalCSName; From 8326dfb76f3072dab63243ef2cd62fa3e7b7c688 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 27 Aug 2013 16:28:16 -0700 Subject: [PATCH 037/251] CLOUDSTACK-4518: make folder sync resilience with possible runtime situation --- .../storage/resource/VmwareStorageLayoutHelper.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java index c61f15e2b4c..d60abad3519 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java @@ -129,17 +129,20 @@ public class VmwareStorageLayoutHelper { vmdkLinkedCloneModePair[1], dcMo.getMor(), true); } - s_logger.info("sync " + vmdkLinkedCloneModeLegacyPair[0] + "->" + vmdkLinkedCloneModePair[0]); - ds.moveDatastoreFile(vmdkLinkedCloneModeLegacyPair[0], dcMo.getMor(), ds.getMor(), - vmdkLinkedCloneModePair[0], dcMo.getMor(), true); + if(ds.fileExists(vmdkLinkedCloneModeLegacyPair[0])) { + s_logger.info("sync " + vmdkLinkedCloneModeLegacyPair[0] + "->" + vmdkLinkedCloneModePair[0]); + ds.moveDatastoreFile(vmdkLinkedCloneModeLegacyPair[0], dcMo.getMor(), ds.getMor(), + vmdkLinkedCloneModePair[0], dcMo.getMor(), true); + } + // Note: we will always return a path return vmdkLinkedCloneModePair[0]; } public static void syncVolumeToRootFolder(DatacenterMO dcMo, DatastoreMO ds, String vmdkName) throws Exception { String fileDsFullPath = ds.searchFileInSubFolders(vmdkName + ".vmdk", false); if(fileDsFullPath == null) - throw new Exception("Unable to find " + vmdkName + ".vmdk in datastore: " + ds.getName()); + return; DatastoreFile srcDsFile = new DatastoreFile(fileDsFullPath); String companionFilePath = srcDsFile.getCompanionPath(vmdkName + "-flat.vmdk"); From 4ba68e3b3fd58bd1e7333bb68e8970b3af7a954f Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 27 Aug 2013 16:56:21 -0700 Subject: [PATCH 038/251] CLOUDSTACK-4530:Create Template from a Snapshot fails with unable to find any snapshot ova/ovf when we have multiple secondary storage for a zone. --- server/src/com/cloud/template/TemplateManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 2a0e04dfbee..fa92a02dd98 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1374,6 +1374,10 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, AsyncCallFuture future = null; if (snapshotId != null) { SnapshotInfo snapInfo = this._snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); + DataStore snapStore = snapInfo.getDataStore(); + if ( snapStore != null ){ + store = snapStore; // pick snapshot image store to create template + } future = this._tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store); } else if (volumeId != null) { VolumeInfo volInfo = this._volFactory.getVolume(volumeId); From 4c6ec5f3c63cb486566d9260e77d2f21c79d9c07 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 27 Aug 2013 17:37:39 -0700 Subject: [PATCH 039/251] CLOUDSTACK-4464,CLOUDSTACK-4529: use property collector to collect all information in one round, we have a few other places that need to do it the same way --- .../vmware/resource/VmwareResource.java | 53 ++++++++++++++----- .../cloud/hypervisor/vmware/mo/ClusterMO.java | 31 ++++++----- .../cloud/hypervisor/vmware/mo/HostMO.java | 15 ++++-- .../vmware/mo/HypervisorHostHelper.java | 18 +++---- 4 files changed, 74 insertions(+), 43 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 63d048266f1..fd88c7ea565 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -277,6 +277,7 @@ import com.vmware.vim25.AboutInfo; import com.vmware.vim25.BoolPolicy; import com.vmware.vim25.ClusterDasConfigInfo; import com.vmware.vim25.ComputeResourceSummary; +import com.vmware.vim25.CustomFieldStringValue; import com.vmware.vim25.DVPortConfigInfo; import com.vmware.vim25.DVPortConfigSpec; import com.vmware.vim25.DatastoreSummary; @@ -5799,10 +5800,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if(_recycleHungWorker) { s_logger.info("Scan hung worker VM to recycle"); + + int key = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + String instanceNameCustomField = "value[" + key + "]"; // GC worker that has been running for too long ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost( - new String[] {"name", "config.template", "runtime.powerState", "runtime.bootTime"}); + new String[] {"name", "config.template", "runtime.powerState", "runtime.bootTime", instanceNameCustomField }); if(ocs != null) { for(ObjectContent oc : ocs) { List props = oc.getPropSet(); @@ -5816,6 +5823,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa for(DynamicProperty prop : props) { if (prop.getName().equals("name")) vmName = prop.getVal().toString(); + else if(prop.getName().startsWith("value[")) { + if(prop.getVal() != null) + internalName = ((CustomFieldStringValue)prop.getVal()).getValue(); + } else if(prop.getName().equals("config.template")) template = (Boolean)prop.getVal(); else if(prop.getName().equals("runtime.powerState")) @@ -5825,15 +5836,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } VirtualMachineMO vmMo = new VirtualMachineMO(hyperHost.getContext(), oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - internalName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); - String name = null; if (internalName != null) { name = internalName; } else { name = vmName; } + if(!template && name.matches("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")) { boolean recycle = false; @@ -6274,9 +6283,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa private HashMap getVmStates() throws Exception { VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext()); + + int key = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + String instanceNameCustomField = "value[" + key + "]"; + // CLOUD_VM_INTERNAL_NAME stores the internal CS generated vm name. This was earlier stored in name. Now, name can be either the hostname or // the internal CS name, but the custom field CLOUD_VM_INTERNAL_NAME always stores the internal CS name. - ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost(new String[] { "name", "runtime.powerState", "config.template" }); + ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost(new String[] { "name", "runtime.powerState", "config.template", instanceNameCustomField }); HashMap newStates = new HashMap(); if (ocs != null && ocs.length > 0) { @@ -6297,13 +6313,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa powerState = (VirtualMachinePowerState) objProp.getVal(); } else if (objProp.getName().equals("name")) { name = (String) objProp.getVal(); - } else { + } else if(objProp.getName().contains(instanceNameCustomField)) { + if(objProp.getVal() != null) + VMInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue(); + } + else { assert (false); } } - VirtualMachineMO vmMo = new VirtualMachineMO(hyperHost.getContext(), oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - VMInternalCSName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if (VMInternalCSName != null) name = VMInternalCSName; @@ -6335,8 +6353,14 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } } + + int key = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + String instanceNameCustomField = "value[" + key + "]"; - ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost(new String[] {"name", "summary.config.numCpu", "summary.quickStats.overallCpuUsage"}); + ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost(new String[] {"name", "summary.config.numCpu", "summary.quickStats.overallCpuUsage", instanceNameCustomField}); if (ocs != null && ocs.length > 0) { for (ObjectContent oc : ocs) { List objProps = oc.getPropSet(); @@ -6349,16 +6373,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa for (DynamicProperty objProp : objProps) { if (objProp.getName().equals("name")) { vmNameOnVcenter = objProp.getVal().toString(); - } else if (objProp.getName().equals("summary.config.numCpu")) { + } else if(objProp.getName().contains(instanceNameCustomField)) { + if(objProp.getVal() != null) + vmInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue(); + } + else if (objProp.getName().equals("summary.config.numCpu")) { numberCPUs = objProp.getVal().toString(); } else if (objProp.getName().equals("summary.quickStats.overallCpuUsage")) { maxCpuUsage = objProp.getVal().toString(); } } VirtualMachineMO vmMo = new VirtualMachineMO(hyperHost.getContext(), oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - vmInternalCSName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); - if (vmInternalCSName != null) { name = vmInternalCSName; } else { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java index d1a4530d3a2..b91d9f47d17 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java @@ -98,25 +98,28 @@ public class ClusterMO extends BaseMO implements VmwareHypervisorHost { @Override public VirtualMachineMO findVmOnHyperHost(String name) throws Exception { - ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name" }); - return HypervisorHostHelper.findVmFromObjectContent(_context, ocs, name); + + int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + + String instanceNameCustomField = "value[" + key + "]"; + ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name", instanceNameCustomField }); + return HypervisorHostHelper.findVmFromObjectContent(_context, ocs, name, instanceNameCustomField); } @Override public VirtualMachineMO findVmOnPeerHyperHost(String name) throws Exception { - ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name" }); - if(ocs != null && ocs.length > 0) { - for(ObjectContent oc : ocs) { - List props = oc.getPropSet(); - if(props != null) { - for(DynamicProperty prop : props) { - if(prop.getVal().toString().equals(name)) - return new VirtualMachineMO(_context, oc.getObj()); - } - } - } + int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); } - return null; + + String instanceNameCustomField = "value[" + key + "]"; + + ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name", instanceNameCustomField }); + return HypervisorHostHelper.findVmFromObjectContent(_context, ocs, name, instanceNameCustomField); } @Override diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index 49a340c366e..c350aa581b8 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -510,9 +510,14 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { _vmCache.clear(); + int key = getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + if(key == 0) { + s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); + } + // name is the name of the VM as it appears in vCenter. The CLOUD_VM_INTERNAL_NAME custom // field value contains the name of the VM as it is maintained internally by cloudstack (i-x-y). - ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name" }); + ObjectContent[] ocs = getVmPropertiesOnHyperHost(new String[] { "name", "value[" + key + "]" }); if(ocs != null && ocs.length > 0) { for(ObjectContent oc : ocs) { List props = oc.getPropSet(); @@ -522,11 +527,11 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { for (DynamicProperty prop : props) { if (prop.getName().equals("name")) { vmVcenterName = prop.getVal().toString(); - } + } else if(prop.getName().startsWith("value[")) { + if(prop.getVal() != null) + vmInternalCSName = ((CustomFieldStringValue)prop.getVal()).getValue(); + } } - VirtualMachineMO vmMo = new VirtualMachineMO(_context, oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - vmInternalCSName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); String vmName = null; if (vmInternalCSName != null && isUserVMInternalCSName(vmInternalCSName)) { vmName = vmInternalCSName; diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index db4fe7382e4..43c59fcafa5 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -44,6 +44,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.vmware.vim25.AlreadyExistsFaultMsg; import com.vmware.vim25.BoolPolicy; +import com.vmware.vim25.CustomFieldStringValue; import com.vmware.vim25.DVPortSetting; import com.vmware.vim25.DVPortgroupConfigInfo; import com.vmware.vim25.DVPortgroupConfigSpec; @@ -61,7 +62,6 @@ import com.vmware.vim25.HttpNfcLeaseState; import com.vmware.vim25.LongPolicy; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.ObjectContent; -import com.vmware.vim25.OptionValue; import com.vmware.vim25.OvfCreateImportSpecParams; import com.vmware.vim25.OvfCreateImportSpecResult; import com.vmware.vim25.OvfFileItem; @@ -90,8 +90,8 @@ public class HypervisorHostHelper { private static final String UNTAGGED_VLAN_NAME = "untagged"; public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, - ObjectContent[] ocs, String name) { - + ObjectContent[] ocs, String name, String instanceNameCustomField) { + if(ocs != null && ocs.length > 0) { for(ObjectContent oc : ocs) { String vmNameInvCenter = null; @@ -101,16 +101,14 @@ public class HypervisorHostHelper { for(DynamicProperty objProp : objProps) { if(objProp.getName().equals("name")) { vmNameInvCenter = (String)objProp.getVal(); + } else if(objProp.getName().contains(instanceNameCustomField)) { + if(objProp.getVal() != null) + vmInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue(); } - VirtualMachineMO vmMo = new VirtualMachineMO(context, oc.getObj()); - // Check if vmMo has the custom property CLOUD_VM_INTERNAL_NAME set. - try { - vmInternalCSName = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); - } catch (Exception e) { - s_logger.error("Unable to retrieve custom field value for internal VM name"); - } + if ( (vmNameInvCenter != null && name.equalsIgnoreCase(vmNameInvCenter)) || (vmInternalCSName != null && name.equalsIgnoreCase(vmInternalCSName)) ) { + VirtualMachineMO vmMo = new VirtualMachineMO(context, oc.getObj()); return vmMo; } } From 6b4aed2acacb1663cfd3955cc64bc84adef402bc Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 27 Aug 2013 18:08:06 -0700 Subject: [PATCH 040/251] CLOUDSTACK-3535: fix regression introduced in 5d9fa5d42e13a8c8ee10df14c466f66de678e954 --- .../storage/volume/VolumeServiceImpl.java | 1 + .../kvm/src/com/cloud/ha/KVMInvestigator.java | 14 +++++++++----- .../com/cloud/vm/VirtualMachineManagerImpl.java | 8 ++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index faa81511590..8a97c1e4986 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -522,6 +522,7 @@ public class VolumeServiceImpl implements VolumeService { if (result.isSuccess()) { vo.processEvent(Event.OperationSuccessed, result.getAnswer()); } else { + vo.processEvent(Event.OperationFailed); volResult.setResult(result.getResult()); } diff --git a/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java b/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java index 1ec48e624d8..4d8322d5c76 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java +++ b/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java @@ -59,15 +59,19 @@ public class KVMInvestigator extends AdapterBase implements Investigator { return null; } CheckOnHostCommand cmd = new CheckOnHostCommand(agent); - List neighbors = _resourceMgr.listAllHostsInCluster(agent.getClusterId()); + List neighbors = _resourceMgr.listHostsInClusterByStatus(agent.getClusterId(), Status.Up); for (HostVO neighbor : neighbors) { if (neighbor.getId() == agent.getId() || neighbor.getHypervisorType() != Hypervisor.HypervisorType.KVM) { continue; } - Answer answer = _agentMgr.easySend(neighbor.getId(), cmd); - - return answer.getResult() ? Status.Down : Status.Up; - + try { + Answer answer = _agentMgr.easySend(neighbor.getId(), cmd); + if (answer != null) { + return answer.getResult() ? Status.Down : Status.Up; + } + } catch (Exception e) { + s_logger.debug("Failed to send command to host: " + neighbor.getId()); + } } return null; diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 2f5d51ba2c2..11904b96952 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1254,10 +1254,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (AgentUnavailableException e) { s_logger.warn("Unable to stop vm, agent unavailable: " + e.toString()); - throw e; + if (!forced) { + throw e; + } } catch (OperationTimedoutException e) { s_logger.warn("Unable to stop vm, operation timed out: " + e.toString()); - throw e; + if (!forced) { + throw e; + } } finally { if (!stopped) { if (!forced) { From 042d44f0341c8abc47f51ca4e2339784b411d9ce Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 27 Aug 2013 21:32:47 -0700 Subject: [PATCH 041/251] CLOUDSTACK-2121. DOC. Add procedure for deleting affinity groups. Replace button icon with smaller version. --- docs/en-US/host-allocation.xml | 11 +++++++++++ docs/en-US/images/change-affinity-button.png | Bin 8570 -> 7300 bytes 2 files changed, 11 insertions(+) diff --git a/docs/en-US/host-allocation.xml b/docs/en-US/host-allocation.xml index 9e752afbd53..36baee581d1 100644 --- a/docs/en-US/host-allocation.xml +++ b/docs/en-US/host-allocation.xml @@ -108,5 +108,16 @@ Click View Instances. The members of the group are listed. From here, you can click the name of any VM in the list to access all its details and controls. + Delete an Affinity Group + To delete an affinity group: + + In the left navigation bar, click Affinity Groups. + Click the name of the group you are interested in. + Click Delete. + Any VM that is a member of the affinity group will be disassociated from the group. + The former group members will continue to run normally on the current hosts, but if the + VM is restarted, it will no longer follow the host allocation rules from its former + affinity group. + diff --git a/docs/en-US/images/change-affinity-button.png b/docs/en-US/images/change-affinity-button.png index 36b4ae33fd4174b771a92044cfb3d810f5b8db96..c21ef758dc2544ba82eac469159f4f7ecfed1119 100644 GIT binary patch delta 1336 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 a@P9*Ba3~Ikf(;%30000 Date: Wed, 28 Aug 2013 00:18:25 -0700 Subject: [PATCH 042/251] CLOUDSTACK-818. DOC. Add how-to for Dedicate pod, cluster, and host to an account or domain. --- docs/en-US/accounts-users-domains.xml | 79 +++++++++++++++--- .../en-US/images/dedicate-resource-button.png | Bin 0 -> 7144 bytes 2 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 docs/en-US/images/dedicate-resource-button.png diff --git a/docs/en-US/accounts-users-domains.xml b/docs/en-US/accounts-users-domains.xml index 34372a7871c..e8b08a78ee9 100644 --- a/docs/en-US/accounts-users-domains.xml +++ b/docs/en-US/accounts-users-domains.xml @@ -51,12 +51,14 @@ Resources belong to the account, not individual users in that account. For example, billing, resource limits, and so on are maintained by the account, not the users. A user can operate on any resource in the account provided the user has privileges for that operation. - The privileges are determined by the role. + The privileges are determined by the role. + A root administrator can change the ownership of any virtual machine, network, + data disk, snapshot, template, or ISO from one account to any other account. A domain or + sub-domain administrator can do the same for items within the domain from one account to + any other account in the domain. -
+
Dedicating Resources to Accounts and Domains - You can dedicate infrastructure resources including zones, pods, clusters, or hosts to an account or domain. - The root administrator can dedicate resources to a specific domain or account that needs private infrastructure for additional security or performance guarantees. A zone, pod, cluster, or host can be reserved by the root administrator for a specific domain or account. @@ -65,14 +67,67 @@ There are several types of dedication available: - To explicitly dedicate a resource, use the explicit-dedicated type of Affinity Group. - For example, when creating a new VM, an end user can choose to place it on dedicated infrastructure. - See . - You can also use strict implicit dedication. - Strict Implicit dedication, when requested, means, a host will not be shared across multiple accounts – as an example, here is a reason: - for deployment of certain types of applications, such as desktops, due to licensing reasons, no host can be shared between different accounts. - You can also implicitly dedicate a resource with "preferred" implicit dedication. This means that the resource will be deployed - in dedicated infrastructure if possible. Otherwise, the resource can be deployed in shared infrastructure. + Explicit dedication. A zone, pod, cluster, or host is dedicated to an account or + domain by the root administrator during initial deployment and + configuration. + Strict implicit dedication. A host will not be shared across multiple accounts. For example, + strict implicit dedication is useful for deployment of certain types of + applications, such as desktops, where no host can be shared + between different accounts without violating the desktop software's terms of license. + Preferred implicit dedication. The VM will be deployed in dedicated infrastructure if + possible. Otherwise, the VM can be deployed in shared + infrastructure. +
+ How to Dedicate a Zone, Cluster, Pod, or Host to an Account or Domain + For explicit dedication: When deploying a new zone, pod, cluster, or host, the + root administrator can click the Dedicated checkbox, then choose a domain or account + to own the resource. + To explicitly dedicate an existing zone, pod, cluster, or host: log in as the root admin, + find the resource in the UI, and click the Dedicate button. + + + + + dedicate-resource-button.png: button to dedicate a zone, pod, cluster, or host + + + For implicit dedication: The administrator creates a compute service offering and + in the Deployment Planner field, chooses ImplicitDedicationPlanner. Then in Planner + Mode, the administrator specifies either Strict or Preferred, depending on whether + it is permissible to allow some use of shared resources when dedicated resources are + not available. Whenever a user creates a VM based on this service offering, it is + allocated on one of the dedicated hosts. +
+
+ How to Use Dedicated Hosts + To use an explicitly dedicated host, use the explicit-dedicated type of affinity + group (see ). For example, when creating a new VM, + an end user can choose to place it on dedicated infrastructure. This operation will + succeed only if some infrastructure has already been assigned as dedicated to the + user's account or domain. +
+
+ Behavior of Dedicated Hosts, Clusters, Pods, and Zones + The administrator can live migrate VMs away from dedicated hosts if desired, whether the destination + is a host reserved for a different account/domain or a host that is shared (not dedicated to any particular account or domain). + &PRODUCT; will generate an alert, but the operation is allowed. + Dedicated hosts can be used in conjunction with host tags. If both a host tag and dedication are requested, + the VM will be placed only on a host that meets both requirements. If there is no dedicated resource available + to that user that also has the host tag requested by the user, then the VM will not deploy. + If you delete an account or domain, any hosts, clusters, pods, and zones that were + dedicated to it are freed up. They will now be available to be shared by any account + or domain, or the administrator may choose to re-dedicate them to a different + account or domain. + System VMs and virtual routers affect the behavior of host dedication. + System VMs and virtual routers are owned by the &PRODUCT; system account, + and they can be deployed on any host. They do not adhere to explicit dedication. + The presence of system vms and virtual routers on a host makes it unsuitable for strict implicit dedication. + The host can not be used for strict implicit dedication, + because the host already has VMs of a specific account (the default system account). + However, a host with system VMs or virtual routers can be used + for preferred implicit dedication. + +
diff --git a/docs/en-US/images/dedicate-resource-button.png b/docs/en-US/images/dedicate-resource-button.png new file mode 100644 index 0000000000000000000000000000000000000000..0ac38e00eca6e455e8bc71acc3aa760b0e5a67af GIT binary patch literal 7144 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 Date: Wed, 28 Aug 2013 00:25:11 -0700 Subject: [PATCH 043/251] DOC. Fix doc type declaration typo in Host Allocation section. --- docs/en-US/host-allocation.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/host-allocation.xml b/docs/en-US/host-allocation.xml index 36baee581d1..dddffd553ac 100644 --- a/docs/en-US/host-allocation.xml +++ b/docs/en-US/host-allocation.xml @@ -1,5 +1,5 @@ - %BOOK_ENTITIES; ]> From 45877b009ed81c5659441d4488f21a3ec3188005 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 01:19:53 -0700 Subject: [PATCH 044/251] CLOUDSTACK-815. DOC. Update Regions documentation to replace API steps with UI steps. --- docs/en-US/region-add.xml | 64 +++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/docs/en-US/region-add.xml b/docs/en-US/region-add.xml index 802e462ce16..b4dd1814af8 100644 --- a/docs/en-US/region-add.xml +++ b/docs/en-US/region-add.xml @@ -43,17 +43,29 @@ geographic area where you want to set up the new region. Use the steps in the Installation guide. When you come to the step where you set up the database, use the additional command-line flag -r <region_id> to set a - region ID for the new region. The default region is automatically assigned a - region ID of 1, so your first additional region might be region 2. - cloudstack-setup-databases cloud:<dbpassword>@localhost --deploy-as=root:<password> -e <encryption_type> -m <management_server_key> -k <database_key> -r <region_id> + region ID for the new region. The default region is automatically assigned a + region ID of 1, so your first additional region might be region 2. + cloudstack-setup-databases cloud:<dbpassword>@localhost --deploy-as=root:<password> -e <encryption_type> -m <management_server_key> -k <database_key> -r <region_id> By the end of the installation procedure, the Management Server should have been started. Be sure that the Management Server installation was successful and complete. - Add region 2 to region 1. Use the API command addRegion. (For information about how to make an API call, see the Developer's Guide.) - http://<IP_of_region_1_Management_Server>:8080/client/api?command=addRegion&id=2&name=Western&endpoint=http://<region_2_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D - - Now perform the same command in reverse, adding region 1 to region 2. - http://<IP_of_region_2_Management_Server>:8080/client/api?command=addRegion&id=1&name=Northern&endpoint=http://<region_1_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D + Now add the new region to region 1 in &PRODUCT;. + + Log in to &PRODUCT; in the first region as root administrator + (that is, log in to <region.1.IP.address>:8080/client). + In the left navigation bar, click Regions. + Click Add Region. In the dialog, fill in the following fields: + + ID. A unique identifying number. Use the same number + you set in the database during Management Server installation in the new region; + for example, 2. + Name. Give the new region a descriptive name. + Endpoint. The URL where you can log in to the Management Server in the new region. + This has the format <region.2.IP.address>:8080/client. + + + + Now perform the same procedure in reverse. Log in to region 2, and add region 1. Copy the account, user, and domain tables from the region 1 database to the region 2 database. In the following commands, it is assumed that you have set the root password on the database, which is a &PRODUCT; recommended best practice. Substitute your own MySQL @@ -84,16 +96,23 @@ Install &PRODUCT; in each additional region. Set the region ID for each region during the database setup step. cloudstack-setup-databases cloud:<dbpassword>@localhost --deploy-as=root:<password> -e <encryption_type> -m <management_server_key> -k <database_key> -r <region_id> Once the Management Server is running, add your new region to all existing regions by - repeatedly calling the API command addRegion. For example, if you were adding + repeatedly using the Add Region button in the UI. For example, if you were adding region 3: - http://<IP_of_region_1_Management_Server>:8080/client/api?command=addRegion&id=3&name=Eastern&endpoint=http://<region_3_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D - -http://<IP_of_region_2_Management_Server>:8080/client/api?command=addRegion&id=3&name=Eastern&endpoint=http://<region_3_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D + + Log in to &PRODUCT; in the first region as root administrator + (that is, log in to <region.1.IP.address>:8080/client), and add a region with ID 3, the name of region 3, and the endpoint <region.3.IP.address>:8080/client. + Log in to &PRODUCT; in the second region as root administrator (that is, log in to <region.2.IP.address>:8080/client), and add a region with ID 3, the name of region 3, and the endpoint <region.3.IP.address>:8080/client. + + Repeat the procedure in reverse to add all existing regions to the new region. For example, for the third region, add the other two existing regions: - http://<IP_of_region_3_Management_Server>:8080/client/api?command=addRegion&id=1&name=Northern&endpoint=http://<region_1_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D - -http://<IP_of_region_3_Management_Server>:8080/client/api?command=addRegion&id=2&name=Western&endpoint=http://<region_2_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D + + Log in to &PRODUCT; in the third region as root administrator + (that is, log in to <region.3.IP.address>:8080/client). + Add a region with ID 1, the name of region 1, and the endpoint <region.1.IP.address>:8080/client. + Add a region with ID 2, the name of region 2, and the endpoint <region.2.IP.address>:8080/client. + + Copy the account, user, and domain tables from any existing region's database to the new region's database. In the following commands, it is assumed that you have set the root password on the @@ -109,7 +128,7 @@ http://<IP_of_region_3_Management_Server>:8080/client/api?command=addRegio - Remove project accounts. Run these commands on the region 2 database: + Remove project accounts. Run these commands on the region 3 database: mysql> delete from account where type = 5; Set the default zone as null: @@ -120,9 +139,14 @@ http://<IP_of_region_3_Management_Server>:8080/client/api?command=addRegio
Deleting a Region - To delete a region, use the API command removeRegion. Repeat the call to remove the region from all other regions. For example, to remove the 3rd region in a three-region cloud: - http://<IP_of_region_1_Management_Server>:8080/client/api?command=removeRegion&id=3&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D - -http://<IP_of_region_2_Management_Server>:8080/client/api?command=removeRegion&id=3&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D + Log in to each of the other regions, navigate to the one you want to delete, and click Remove Region. + For example, to remove the third region in a 3-region cloud: + + Log in to <region.1.IP.address>:8080/client. + In the left navigation bar, click Regions. + Click the name of the region you want to delete. + Click the Remove Region button. + Repeat these steps for <region.2.IP.address>:8080/client. +
From b9325cd334f02bd5ffb30478fab3fd89e362ea95 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 09:36:25 -0700 Subject: [PATCH 045/251] CLOUDSTACK-3298. DOC. New upper limit on userdata for a VM. --- docs/en-US/user-data-and-meta-data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/user-data-and-meta-data.xml b/docs/en-US/user-data-and-meta-data.xml index 3f03449554a..34007011de1 100644 --- a/docs/en-US/user-data-and-meta-data.xml +++ b/docs/en-US/user-data-and-meta-data.xml @@ -24,7 +24,7 @@
User Data and Meta Data - &PRODUCT; provides API access to attach user data to a deployed VM. Deployed VMs also have access to instance metadata via the virtual router. + &PRODUCT; provides API access to attach up to 32KB of user data to a deployed VM. Deployed VMs also have access to instance metadata via the virtual router. User data can be accessed once the IP address of the virtual router is known. Once the IP address is known, use the following steps to access the user data: Run the following command to find the virtual router. From 0d8ce418c54047bcbbd300c1445b4ddf338f81c2 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 10:07:10 -0700 Subject: [PATCH 046/251] CLOUDSTACK-2405. DOC. Changing ownership of a VM from one account to another in any domain. --- .../api/command/admin/vm/AssignVMCmd.java | 2 +- docs/en-US/accounts-users-domains.xml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java index 152dd4e14c2..2a60e192ca3 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java @@ -35,7 +35,7 @@ import org.apache.log4j.Logger; import com.cloud.user.Account; import com.cloud.uservm.UserVm; -@APICommand(name = "assignVirtualMachine", description="Assign a VM from one account to another under the same domain. This API is available for Basic zones with security groups and Advance zones with guest networks. The VM is restricted to move between accounts under same domain.", responseObject=UserVmResponse.class, since="3.0.0") +@APICommand(name = "assignVirtualMachine", description="Change ownership of a VM from one account to another. This API is available for Basic zones with security groups and Advanced zones with guest networks. A root administrator can reassign a VM from any account to any other account in any domain. A domain administrator can reassign a VM to any account in the same domain.", responseObject=UserVmResponse.class, since="3.0.0") public class AssignVMCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(AssignVMCmd.class.getName()); diff --git a/docs/en-US/accounts-users-domains.xml b/docs/en-US/accounts-users-domains.xml index e8b08a78ee9..3accbbe9b84 100644 --- a/docs/en-US/accounts-users-domains.xml +++ b/docs/en-US/accounts-users-domains.xml @@ -49,13 +49,13 @@ Resource Ownership Resources belong to the account, not individual users in that account. For example, - billing, resource limits, and so on are maintained by the account, not the users. A user can - operate on any resource in the account provided the user has privileges for that operation. - The privileges are determined by the role. - A root administrator can change the ownership of any virtual machine, network, - data disk, snapshot, template, or ISO from one account to any other account. A domain or - sub-domain administrator can do the same for items within the domain from one account to - any other account in the domain. + billing, resource limits, and so on are maintained by the account, not the users. A user + can operate on any resource in the account provided the user has privileges for that + operation. The privileges are determined by the role. A root administrator can change + the ownership of any virtual machine from one account to any other account by using the + assignVirtualMachine API. A domain or sub-domain administrator can do the same for VMs + within the domain from one account to any other account in the domain or any of its + sub-domains.
Dedicating Resources to Accounts and Domains From c5139d6bc90f39c5365ed8e12f769a4ee0578ac2 Mon Sep 17 00:00:00 2001 From: frank Date: Wed, 28 Aug 2013 11:09:46 -0700 Subject: [PATCH 047/251] CloudStack CLOUDSTACK-4426 [VMware] [upgrade] system VM not coming up after upgrade from 3.0.4 to 4.2 --- .../com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index b5e75354554..bd6ee6a1c2e 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -763,7 +763,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Change permissions for the mountpoint script = new Script(true, "chmod", _timeout, s_logger); - script.add("777", mountPoint); + script.add("777", "*", "-R", mountPoint); result = script.execute(); if (result != null) { s_logger.warn("Unable to set permissions for " + mountPoint + " due to " + result); From 63c8b147fe636316bc6a886631f17a96097fb755 Mon Sep 17 00:00:00 2001 From: rayeesn Date: Wed, 28 Aug 2013 04:07:09 -0700 Subject: [PATCH 048/251] CLOUDSTACK-4509 : management cache folder need to cleanup before upgrade, currently its cleaning up only for 3.x upgrade ie /var/cache/cloud, it should do that same for 4.2 also /var/cache/cloudstack --- packaging/centos63/cloud.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 5319fb61197..1ae43baf083 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -367,6 +367,7 @@ sed -i /"cloud soft nofile"/d /etc/security/limits.conf echo "cloud hard nofile 4096" >> /etc/security/limits.conf echo "cloud soft nofile 4096" >> /etc/security/limits.conf rm -rf %{_localstatedir}/cache/cloud +rm -rf %{_localstatedir}/cache/cloudstack # user harcoded here, also hardcoded on wscript # save old configs if they exist (for upgrade). Otherwise we may lose them From ad1633dda9f2f0ba5a1cf6e065b87a2f5978fb93 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 28 Aug 2013 11:17:29 -0700 Subject: [PATCH 049/251] CLOUDSTACK-4430:[Automation][vmware] Failed to deploy vm, if one host is down in a cluster. --- .../storage/VMTemplateStoragePoolVO.java | 5 +++ .../storage/volume/VolumeServiceImpl.java | 31 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/engine/schema/src/com/cloud/storage/VMTemplateStoragePoolVO.java b/engine/schema/src/com/cloud/storage/VMTemplateStoragePoolVO.java index 9331b038e49..b9886e08237 100644 --- a/engine/schema/src/com/cloud/storage/VMTemplateStoragePoolVO.java +++ b/engine/schema/src/com/cloud/storage/VMTemplateStoragePoolVO.java @@ -266,6 +266,11 @@ public class VMTemplateStoragePoolVO implements VMTemplateStorageResourceAssoc, return this.state; } + //TODO: this should be revisited post-4.2 to completely use state transition machine + public void setState(ObjectInDataStoreStateMachine.State state) { + this.state = state; + } + public long getUpdatedCount() { return this.updatedCount; } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 8a97c1e4986..1e6858955fb 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -49,6 +49,7 @@ import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.async.AsyncRpcContext; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.datastore.DataObjectManager; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; @@ -516,17 +517,41 @@ public class VolumeServiceImpl implements VolumeService { AsyncCallbackDispatcher callback, CreateVolumeFromBaseImageContext context) { DataObject vo = context.vo; + DataObject tmplOnPrimary = context.templateOnStore; CopyCommandResult result = callback.getResult(); VolumeApiResult volResult = new VolumeApiResult((VolumeObject) vo); if (result.isSuccess()) { vo.processEvent(Event.OperationSuccessed, result.getAnswer()); - } else { - + } else { vo.processEvent(Event.OperationFailed); volResult.setResult(result.getResult()); + // hack for Vmware: host is down, previously download template to the host needs to be re-downloaded, so we need to reset + // template_spool_ref entry here to NOT_DOWNLOADED and Allocated state + Answer ans = result.getAnswer(); + if ( ans != null && ans instanceof CopyCmdAnswer && ans.getDetails().contains("request template reload")){ + if (tmplOnPrimary != null){ + s_logger.info("Reset template_spool_ref entry so that vmware template can be reloaded in next try"); + VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(tmplOnPrimary.getDataStore().getId(), tmplOnPrimary.getId()); + if (templatePoolRef != null) { + long templatePoolRefId = templatePoolRef.getId(); + templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, 1200); + if (templatePoolRef == null) { + s_logger.warn("Reset Template State On Pool failed - unable to lock TemplatePoolRef " + templatePoolRefId); + } + + try { + templatePoolRef.setDownloadState(VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED); + templatePoolRef.setState(ObjectInDataStoreStateMachine.State.Allocated); + _tmpltPoolDao.update(templatePoolRefId, templatePoolRef); + } finally { + _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); + } + } + } + } } - + AsyncCallFuture future = context.getFuture(); future.complete(volResult); return null; From bdc87ab91533b6a2147554dd5fa63c1074889f36 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 10:07:10 -0700 Subject: [PATCH 050/251] CLOUDSTACK-2289. DOC. Granular configuration parameters. You can now configure settings for entities lower than the global level. --- docs/en-US/global-config.xml | 189 +++++++++++++++++------------------ 1 file changed, 93 insertions(+), 96 deletions(-) diff --git a/docs/en-US/global-config.xml b/docs/en-US/global-config.xml index 11952c382ac..407d97d2ee4 100644 --- a/docs/en-US/global-config.xml +++ b/docs/en-US/global-config.xml @@ -1,5 +1,5 @@ - %BOOK_ENTITIES; ]> @@ -19,115 +19,112 @@ 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 - settings. - - - Log in to the UI as administrator. - - - In the left navigation bar, click Global Settings. - - - In Select View, choose one of the following: - - - Global Settings. This displays a list of the parameters with brief descriptions and - current values. - - - Hypervisor Capabilities. This displays a list of hypervisor versions with the - maximum number of guests supported for each. - - - - - Use the search box to narrow down the list to those you are interested in. - - - Click the Edit icon to modify a value. If you are viewing Hypervisor Capabilities, you - must click the name of the hypervisor first to display the editing screen. - - -
+ Setting Configuration Parameters
- About Global Configuration Parameters + About 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." + need to set some of these configuration parameters, depending on what optional features + you are setting up. + You can set default values at the global level, which will be in effect throughout the cloud unless you override them at a lower level. + You can make local settings, which will override the global configuration parameter values, at the level of an account, zone, cluster, or primary storage. 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 + parameters. The following table shows a few of the more useful parameters. - - Field - Value - + + 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. - + + 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. +
+
+ Setting Global Configuration Parameters + Use the following steps to set global configuration parameters. These values will be the defaults in effect throughout your &PRODUCT; deployment. + + Log in to the UI as administrator. + In the left navigation bar, click Global Settings. + In Select View, choose one of the following: + + Global Settings. This displays a list of the parameters with brief descriptions and current values. + Hypervisor Capabilities. This displays a list of hypervisor versions with the maximum number of guests supported for each. + + + Use the search box to narrow down the list to those you are interested in. + In the Actions column, click the Edit icon to modify a value. If you are viewing Hypervisor Capabilities, you must click the name of the hypervisor first to display the editing screen. + +
+
+ Setting Local Configuration Parameters + Use the following steps to set local configuration parameters for an account, zone, cluster, or primary storage. + These values will override the global configuration settings. + + Log in to the UI as administrator. + In the left navigation bar, click Infrastructure or Accounts, depending on where you want to set a value. + Find the name of the particular resource that you want to work with. For example, if you are in Infrastructure, + click View All on the Zones, Clusters, or Primary Storage area. + Click the name of the resource where you want to set a limit. + Click the Settings tab. + Use the search box to narrow down the list to those you are interested in. + In the Actions column, click the Edit icon to modify a value. + +
From e14e1379b6a9626a0679834996ac16169f62b38d Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 11:47:10 -0700 Subject: [PATCH 051/251] CLOUDSTACK-900. DOC. Live storage migration, XenMotion, vMotion. --- docs/en-US/attaching-volume.xml | 2 +- docs/en-US/detach-move-volumes.xml | 5 +- docs/en-US/manual-live-migration.xml | 13 ++-- ...grate-datadisk-volume-new-storage-pool.xml | 61 ++++++++++++++++--- ...-vm-rootvolume-volume-new-storage-pool.xml | 34 +++++++---- docs/en-US/vm-storage-migration.xml | 26 +++++--- 6 files changed, 103 insertions(+), 38 deletions(-) diff --git a/docs/en-US/attaching-volume.xml b/docs/en-US/attaching-volume.xml index 7511ec32a4d..bb9196a93bb 100644 --- a/docs/en-US/attaching-volume.xml +++ b/docs/en-US/attaching-volume.xml @@ -37,7 +37,7 @@ In Select View, choose Volumes. - 4. Click the volume name in the Volumes list, then click the Attach Disk button + Click the volume name in the Volumes list, then click the Attach Disk button diff --git a/docs/en-US/detach-move-volumes.xml b/docs/en-US/detach-move-volumes.xml index 7103c305c4f..8922db12161 100644 --- a/docs/en-US/detach-move-volumes.xml +++ b/docs/en-US/detach-move-volumes.xml @@ -24,9 +24,8 @@
Detaching and Moving Volumes - This procedure is different from moving disk volumes from one storage pool to another. See - VM Storage Migration - + This procedure is different from moving volumes from one storage pool to another as described in . + A volume can be detached from a guest VM and attached to another guest. Both &PRODUCT; administrators and users can detach volumes from VMs and move them to other VMs. If the two VMs are in different clusters, and the volume is large, it may take several diff --git a/docs/en-US/manual-live-migration.xml b/docs/en-US/manual-live-migration.xml index 225f0ba3317..1daa6d3d937 100644 --- a/docs/en-US/manual-live-migration.xml +++ b/docs/en-US/manual-live-migration.xml @@ -26,10 +26,12 @@ The &PRODUCT; administrator can move a running VM from one host to another without interrupting service to users or going into maintenance mode. This is called manual live migration, and can be done under the following conditions: The root administrator is logged in. Domain admins and users can not perform manual live migration of VMs. - The VM is running. Stopped VMs can not be live migrated. - The destination host must be in the same cluster as the original host. - The VM must not be using local disk storage. + The VM is running. Stopped VMs can not be live migrated. The destination host must have enough available capacity. If not, the VM will remain in the "migrating" state until memory becomes available. + (KVM) The VM must not be using local disk storage. (On XenServer and VMware, VM live migration + with local disk is enabled by &PRODUCT; support for XenMotion and vMotion.) + (KVM) The destination host must be in the same cluster as the original host. + (On XenServer and VMware, VM live migration from one cluster to another is enabled by &PRODUCT; support for XenMotion and vMotion.) To manually live migrate a virtual machine @@ -44,7 +46,10 @@ Migrateinstance.png: button to migrate an instance - From the list of hosts, choose the one to which you want to move the VM. + From the list of suitable hosts, choose the one to which you want to move the VM. + If the VM's storage has to be migrated along with the VM, this will be noted in the host + list. &PRODUCT; will take care of the storage migration for you. + Click OK.
diff --git a/docs/en-US/migrate-datadisk-volume-new-storage-pool.xml b/docs/en-US/migrate-datadisk-volume-new-storage-pool.xml index 552fb319341..1ed6bbd7cd3 100644 --- a/docs/en-US/migrate-datadisk-volume-new-storage-pool.xml +++ b/docs/en-US/migrate-datadisk-volume-new-storage-pool.xml @@ -23,13 +23,56 @@ -->
- Migrating a Data Disk Volume to a New Storage Pool - - Log in to the &PRODUCT; UI as a user or admin. - Detach the data disk from the VM. See Detaching and Moving Volumes (but skip the “reattach” step at the end. You will do that after migrating to new storage). - Call the &PRODUCT; API command migrateVolume and pass in the volume ID and the ID of any storage pool in the zone. - Watch for the volume status to change to Migrating, then back to Ready. - Attach the volume to any desired VM running in the same cluster as the new storage server. See Attaching a Volume - + Migrating a Data Volume to a New Storage Pool + There are two situations when you might want to migrate a disk: + + Move the disk to new storage, but leave it attached to the same running VM. + Detach the disk from its current VM, move it to new storage, and attach it to a new VM. + +
+ Migrating Storage For a Running VM + (Supported on XenServer and VMware) + + Log in to the &PRODUCT; UI as a user or admin. + In the left navigation bar, click Instances, click the VM name, and click View Volumes. + Click the volume you want to migrate. + Detach the disk from the VM. + See but skip the “reattach” step at the end. You + will do that after migrating to new storage. + Click the Migrate Volume button + + + + + Migrateinstance.png: button to migrate a volume + + + and choose the destination from the dropdown list. + Watch for the volume status to change to Migrating, then back to Ready. +
- +
+ Migrating Storage and Attaching to a Different VM + + Log in to the &PRODUCT; UI as a user or admin. + Detach the disk from the VM. + See but skip the “reattach” step at the end. You + will do that after migrating to new storage. + Click the Migrate Volume button + + + + + Migrateinstance.png: button to migrate a volume + + + and choose the destination from the dropdown list. + Watch for the volume status to change to Migrating, then back to Ready. You can find the + volume by clicking Storage in the left navigation bar. Make sure that Volumes is + displayed at the top of the window, in the Select View dropdown. + Attach the volume to any desired VM running in the same cluster as the new storage server. See + + + +
+
diff --git a/docs/en-US/migrate-vm-rootvolume-volume-new-storage-pool.xml b/docs/en-US/migrate-vm-rootvolume-volume-new-storage-pool.xml index d615cfe7a5b..3bcaff53c63 100644 --- a/docs/en-US/migrate-vm-rootvolume-volume-new-storage-pool.xml +++ b/docs/en-US/migrate-vm-rootvolume-volume-new-storage-pool.xml @@ -23,15 +23,25 @@ -->
Migrating a VM Root Volume to a New Storage Pool - When migrating the root disk volume, the VM must first be stopped, and users can not access the VM. After migration is complete, the VM can be restarted. - - Log in to the &PRODUCT; UI as a user or admin. - Detach the data disk from the VM. See Detaching and Moving Volumes (but skip the “reattach” step at the end. You will do that after migrating to new storage). - Stop the VM. - Use the &PRODUCT; API command, migrateVirtualMachine, with the ID of the VM to migrate and - the IDs of a destination host and destination storage pool in the same zone. - Watch for the VM status to change to Migrating, then back to Stopped. - Restart the VM. - -
- + (XenServer, VMware) You can live migrate a VM's root disk from one storage pool to another, without stopping the VM first. + (KVM) When migrating the root disk volume, the VM must first be stopped, and users can not access the VM. After migration is complete, the VM can be restarted. + + Log in to the &PRODUCT; UI as a user or admin. + In the left navigation bar, click Instances, and click the VM name. + (KVM only) Stop the VM. + Click the Migrate button + + + + + Migrateinstance.png: button to migrate a VM or volume + + + and choose the destination from the dropdown list. + If the VM's storage has to be migrated along with the VM, this will be noted in the host + list. &PRODUCT; will take care of the storage migration for you. + Watch for the volume status to change to Migrating, then back to Running (or Stopped, in the case of KVM). This + can take some time. + (KVM only) Restart the VM. + +
\ No newline at end of file diff --git a/docs/en-US/vm-storage-migration.xml b/docs/en-US/vm-storage-migration.xml index e0dad57faa0..51c6f34a757 100644 --- a/docs/en-US/vm-storage-migration.xml +++ b/docs/en-US/vm-storage-migration.xml @@ -24,15 +24,23 @@
VM Storage Migration Supported in XenServer, KVM, and VMware. - - This procedure is different from moving disk volumes from one VM to another. See Detaching - and Moving Volumes . - - You can migrate a virtual machine’s root disk volume or any additional data disk volume from - one storage pool to another in the same zone. - You can use the storage migration feature to achieve some commonly desired administration - goals, such as balancing the load on storage pools and increasing the reliability of virtual - machines by moving them away from any storage pool that is experiencing issues. + This procedure is different from moving disk volumes from one VM to another as described in + . + + You can migrate a virtual machine’s root disk volume or any additional data disk volume from one storage pool to another in the same zone. + You can use the storage migration feature to achieve some commonly desired administration goals, such as balancing the load on storage pools and increasing the reliability of virtual machines by moving them away from any storage pool that is experiencing issues. + On XenServer and VMware, live migration of VM storage is enabled through &PRODUCT; + support for XenMotion and vMotion. + Live storage migration allows VMs to be moved from one host to another, where the VMs are + not located on storage shared between the two hosts. It provides the option to live + migrate a VM’s disks along with the VM itself. It is possible to migrate a VM from one + XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks are on + local storage, or even to migrate a VM’s disks from one storage repository to another, all + while the VM is running. + Because of a limitation in VMware, live migration of storage for a VM is allowed only + if the source and target storage pool are accessible to the source host; that is, the host + where the VM is running when the live migration operation is requested. + Date: Wed, 28 Aug 2013 11:56:49 -0700 Subject: [PATCH 052/251] CLOUDSTACK-864. DOC. Small typo fix for Talluri's review comment. --- docs/en-US/primary-storage-add.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/en-US/primary-storage-add.xml b/docs/en-US/primary-storage-add.xml index d8a81f6d9fa..d18dece54d9 100644 --- a/docs/en-US/primary-storage-add.xml +++ b/docs/en-US/primary-storage-add.xml @@ -52,10 +52,9 @@ Provide the following information in the dialog. The information required varies depending on your choice in Protocol. - Scope. Indicate whether the storage is available to all hosts in the zone or only to hosts in a single cluster. - Pod. (Visible only if you choose Cluster in the Scope field.) The pod for the storage device. - Cluster. (Visible only if you choose Cluster in the Scope field.) The cluster for the storage device. - Name. The name of the storage device + Scope. Indicate whether the storage is available to all hosts in the zone or only to hosts in a single cluster. + Pod. (Visible only if you choose Cluster in the Scope field.) The pod for the storage device. + Cluster. (Visible only if you choose Cluster in the Scope field.) The cluster for the storage device. Name. The name of the storage device. Protocol. For XenServer, choose either NFS, iSCSI, or PreSetup. For KVM, choose NFS or SharedMountPoint. For vSphere choose either VMFS (iSCSI or FiberChannel) or NFS. Server (for NFS, iSCSI, or PreSetup). The IP address or DNS name of the storage device. From 02641f851c2b303c91ee34ccae7a4cd7a8e48487 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 28 Aug 2013 13:29:47 -0700 Subject: [PATCH 053/251] CLOUDSTACK-4266: UI > Instrastructure > clusters > Add Cluster dialog > if configuration "vmware.use.nexus.switch" is set to false, hide VSM fields. --- ui/scripts/system.js | 198 ++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 107 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 63e316305f9..8eb113ed81d 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10306,29 +10306,96 @@ }); if ($form.find('.form-item[rel=hypervisor] select').val() == 'VMware' ) { - // VSM fields need to be required if a traffic override is selected and vSwitchType is 'nexusdvs'. - // This is done by switching out optional fields for required fields; - var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); - var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); + $form.find('.form-item[rel=vCenterHost]').css('display', 'inline-block'); + $form.find('.form-item[rel=vCenterUsername]').css('display', 'inline-block'); + $form.find('.form-item[rel=vCenterPassword]').css('display', 'inline-block'); + $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'inline-block'); + + //***** 'vmware.use.dvswitch' (begin) (whether to show override traffic checkbox) ***** + var dvSwitchEnabled = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.dvswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + dvSwitchEnabled = true; + } + } + }); + if (dvSwitchEnabled) { + $form.find('.form-item[rel=overridepublictraffic]').css('display', 'inline-block'); + $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); + } else { + $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); + + $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); + } + //***** 'vmware.use.dvswitch' (end) ***** + + //***** 'vmware.use.nexus.vswitch' (begin) (whether to show VSM fields) ***** + var vSwitchEnabled = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.nexus.vswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + vSwitchEnabled = true; + } + } + }); + if (vSwitchEnabled) { + //$vsmFields.css('display', 'inline-block'); + var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); + var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); + + var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); + + if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || + ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { + $vsmReqFields.css('display', 'inline-block'); + $vsmFields.hide(); + } else { + $vsmFields.css('display', 'inline-block'); + $vsmReqFields.hide(); + } + + } else { + //$vsmFields.css('display', 'none'); + $vsmFields.hide(); + $vsmReqFields.hide(); + } + //***** 'vmware.use.nexus.vswitch' (end) ***** - var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); - var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); - - - if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || - ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { - $vsmReqFields.css('display', 'inline-block'); - $vsmFields.hide(); - } else { - $vsmFields.css('display', 'inline-block'); - $vsmReqFields.hide(); - } - } else { + } else { //XenServer, KVM, etc (non-VMware) + $form.find('.form-item[rel=vCenterHost]').css('display', 'none'); + $form.find('.form-item[rel=vCenterUsername]').css('display', 'none'); + $form.find('.form-item[rel=vCenterPassword]').css('display', 'none'); + $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'none'); + $form.find('.form-item[rel=enableNexusVswitch]').css('display', 'none'); + + $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); + $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); $vsmFields.hide(); $vsmReqFields.hide(); - } - + } }); + + $form.trigger('click'); }, fields: { zoneid: { @@ -10365,10 +10432,7 @@ hypervisor: { label: 'label.hypervisor', docID: 'helpClusterHypervisor', - select: function(args) { - var vSwitchEnabled = false; - var dvSwitchEnabled = false; - + select: function(args) { $.ajax({ url: createURL("listHypervisors"), dataType: "json", @@ -10387,90 +10451,6 @@ }); } }); - - // Check whether vSwitch capability is enabled - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'vmware.use.nexus.vswitch' - }, - async: false, - success: function(json) { - if (json.listconfigurationsresponse.configuration[0].value == 'true') { - vSwitchEnabled = true; - } - } - }); - - //Check whether dvSwitch is enabled or not - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'vmware.use.dvswitch' - }, - async: false, - success: function(json) { - if (json.listconfigurationsresponse.configuration[0].value == 'true') { - dvSwitchEnabled = true; - } - } - }); - - - args.$select.bind("change", function(event) { - var $form = $(this).closest('form'); - - /* - var $vsmFields = $form.find('.form-item').filter(function() { - var vsmFields = [ - 'vsmipaddress', - 'vsmusername', - 'vsmpassword' - ]; - return $.inArray($(this).attr('rel'), vsmFields) > -1; - }); - */ - - if ($(this).val() == "VMware") { - if (dvSwitchEnabled) { - $form.find('.form-item[rel=overridepublictraffic]').css('display', 'inline-block'); - $form.find('.form-item[rel=overridepublictraffic]').find('input[type=checkbox]').removeAttr('checked'); - - $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); - $form.find('.form-item[rel=overrideguesttraffic]').find('input[type=checkbox]').removeAttr('checked'); - } else { - $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); - $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); - } - $form.find('.form-item[rel=vCenterHost]').css('display', 'inline-block'); - $form.find('.form-item[rel=vCenterUsername]').css('display', 'inline-block'); - $form.find('.form-item[rel=vCenterPassword]').css('display', 'inline-block'); - $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'inline-block'); - - /* - if (vSwitchEnabled) { - $vsmFields.css('display', 'inline-block'); - } else { - $vsmFields.css('display', 'none'); - } - */ - - } else { - $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); - $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); - - $form.find('.form-item[rel=vCenterHost]').css('display', 'none'); - $form.find('.form-item[rel=vCenterUsername]').css('display', 'none'); - $form.find('.form-item[rel=vCenterPassword]').css('display', 'none'); - $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'none'); - $form.find('.form-item[rel=enableNexusVswitch]').css('display', 'none'); - //$vsmFields.css('display', 'none'); - } - }); } }, podId: { @@ -10565,6 +10545,7 @@ //hypervisor==VMWare begins here vCenterHost: { label: 'label.vcenter.host', + isHidden: true, docID: 'helpClustervCenterHost', validation: { required: false @@ -10572,15 +10553,18 @@ }, vCenterUsername: { label: 'label.vcenter.username', + isHidden: true, docID: 'helpClustervCenterUsername' }, vCenterPassword: { label: 'label.vcenter.password', + isHidden: true, docID: 'helpClustervCenterPassword', isPassword: true }, vCenterDatacenter: { label: 'label.vcenter.datacenter', + isHidden: true, docID: 'helpClustervCenterDatacenter', validation: { required: false From cb9fc8bece7e34c397a15a32ec1525bddd95b257 Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Wed, 28 Aug 2013 15:00:03 -0600 Subject: [PATCH 054/251] Trim IQN so that the format /iqn/lun becomes iqn. --- .../vmware/resource/VmwareResource.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index fd88c7ea565..7ff4918e36e 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -4480,13 +4480,27 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return false; } + private String trimIqn(String iqn) { + String[] tmp = iqn.split("/"); + + if (tmp.length != 3) { + String msg = "Wrong format for iScsi path: " + iqn + ". It should be formatted as '/targetIQN/LUN'."; + + s_logger.warn(msg); + + throw new CloudRuntimeException(msg); + } + + return tmp[1].trim(); + } + public ManagedObjectReference handleDatastoreAndVmdkAttach(Command cmd, String iqn, String storageHost, int storagePort, String initiatorUsername, String initiatorPassword, String targetUsername, String targetPassword) throws Exception { VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); ManagedObjectReference morDs = createVmfsDatastore(hyperHost, getDatastoreName(iqn), - storageHost, storagePort, iqn, + storageHost, storagePort, trimIqn(iqn), initiatorUsername, initiatorPassword, targetUsername, targetPassword); @@ -4516,7 +4530,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); - deleteVmfsDatastore(hyperHost, getDatastoreName(iqn), storageHost, storagePort, iqn); + deleteVmfsDatastore(hyperHost, getDatastoreName(iqn), storageHost, storagePort, trimIqn(iqn)); } protected Answer execute(AttachVolumeCommand cmd) { From f21c8fa998dbcae80e66300660e3ee53b1fba9b0 Mon Sep 17 00:00:00 2001 From: frank Date: Wed, 28 Aug 2013 14:14:50 -0700 Subject: [PATCH 055/251] CloudStack CLOUDSTACK-4426 [VMware] [upgrade] system VM not coming up after upgrade from 3.0.4 to 4.2 --- .../com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index bd6ee6a1c2e..0c4bd146faf 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -763,7 +763,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Change permissions for the mountpoint script = new Script(true, "chmod", _timeout, s_logger); - script.add("777", "*", "-R", mountPoint); + script.add("-R", "777", mountPoint); result = script.execute(); if (result != null) { s_logger.warn("Unable to set permissions for " + mountPoint + " due to " + result); From f2874d0f228680b0dcd3299bb070215a2648a4a8 Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Wed, 28 Aug 2013 14:22:21 +0530 Subject: [PATCH 056/251] CLOUDSTACK-4434: EN: Ubuntu: Direct input "- _ ", "? /", "keyboard /" ,"keyboard -" keys are not working well for the US keyboard. Reviewed-by: Fang Wang Signed-off-by: Vijayendra Bhamidipati --- services/console-proxy/server/js/ajaxviewer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/console-proxy/server/js/ajaxviewer.js b/services/console-proxy/server/js/ajaxviewer.js index d3f3aa9557a..96432049227 100644 --- a/services/console-proxy/server/js/ajaxviewer.js +++ b/services/console-proxy/server/js/ajaxviewer.js @@ -140,9 +140,13 @@ KeyboardMapper.prototype = { this.jsX11KeysymMap[AjaxViewer.JS_KEY_SELECT_KEY] = AjaxViewer.X11_KEY_SELECT_KEY; this.jsX11KeysymMap[AjaxViewer.JS_KEY_DECIMAL_POINT] = AjaxViewer.X11_KEY_DECIMAL_POINT; this.jsKeyPressX11KeysymMap[45] = [{type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SUBSTRACT, modifiers: 0, shift: true }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SUBSTRACT, modifiers: 0, shift: false }]; + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SUBSTRACT, modifiers: 0, shift: true }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SUBSTRACT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SUBSTRACT, modifiers: 0, shift: false }]; this.jsKeyPressX11KeysymMap[47] = [{type: AjaxViewer.KEY_DOWN, code: 0x2f, modifiers: 0, shift: true }, - {type: AjaxViewer.KEY_DOWN, code: 0x2f, modifiers: 0, shift: false }]; + {type: AjaxViewer.KEY_UP, code: 0x2f, modifiers: 0, shift: true }, + {type: AjaxViewer.KEY_DOWN, code: 0x2f, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: 0x2f, modifiers: 0, shift: false }]; } }, RawkeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) { From 0ef6084d2c838a78eda29d258b1af98df96451b3 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 28 Aug 2013 15:45:18 -0700 Subject: [PATCH 057/251] CLOUDSTACK-4405: add a tool: cloudstack-agent-upgrade to upgrade bridge name on kvm host --- agent/bindir/cloudstack-agent-upgrade.in | 50 ++++++++++++++++++++++++ packaging/centos63/cloud.spec | 2 + python/lib/cloudutils/networkConfig.py | 5 +++ 3 files changed, 57 insertions(+) create mode 100644 agent/bindir/cloudstack-agent-upgrade.in diff --git a/agent/bindir/cloudstack-agent-upgrade.in b/agent/bindir/cloudstack-agent-upgrade.in new file mode 100644 index 00000000000..4972d3901fe --- /dev/null +++ b/agent/bindir/cloudstack-agent-upgrade.in @@ -0,0 +1,50 @@ +#!/usr/bin/python +# 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. +from cloudutils.networkConfig import networkConfig +from cloudutils.utilities import bash +def isOldStyleBridge(brName): + if brName.find("cloudVirBr") == 0: + return True + else: + return False +def upgradeBridgeName(brName, enslavedDev): + print("upgrade bridge: %s, %s"%(brName, enslavedDev)) + vlanId = brName.replace("cloudVirBr", "") + print("find vlan Id: %s"%vlanId) + phyDev = enslavedDev.split(".")[0] + print("find physical device %s"%phyDev) + newBrName = "br" + phyDev + "-" + vlanId + print("new bridge name %s"%newBrName) + bash("ip link set %s down"%brName) + bash("ip link set %s name %s"%(brName, newBrName)) + bash("ip link set %s up" %newBrName) +if __name__ == '__main__': + netlib = networkConfig() + bridges = netlib.listNetworks() + bridges = filter(isOldStyleBridge, bridges) + for br in bridges: + enslavedDev = netlib.getEnslavedDev(br, 1) + if enslavedDev is not None: + upgradeBridgeName(br, enslavedDev) + + bridges = netlib.listNetworks() + bridges = filter(isOldStyleBridge, bridges) + if len(bridges) > 0: + print("Warning: upgrade is not finished, still some bridges have the old style name:" + str(bridges)) + else: + print("Upgrade succeed") diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 1ae43baf083..01685e3b6d7 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -286,6 +286,7 @@ install -D agent/target/transformed/agent.properties ${RPM_BUILD_ROOT}%{_sysconf install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/environment.properties install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent +install -D agent/target/transformed/cloudstack-agent-upgrade ${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib @@ -547,6 +548,7 @@ fi %files agent %attr(0755,root,root) %{_bindir}/%{name}-setup-agent +%attr(0755,root,root) %{_bindir}/%{name}-agent-upgrade %attr(0755,root,root) %{_bindir}/%{name}-ssh %attr(0755,root,root) %{_sysconfdir}/init.d/%{name}-agent %attr(0755,root,root) %{_datadir}/%{name}-common/scripts/network/cisco diff --git a/python/lib/cloudutils/networkConfig.py b/python/lib/cloudutils/networkConfig.py index 405a3be519c..41ef9d93ed2 100644 --- a/python/lib/cloudutils/networkConfig.py +++ b/python/lib/cloudutils/networkConfig.py @@ -35,6 +35,11 @@ class networkConfig: self.method = None @staticmethod + def listNetworks(): + devs = os.listdir("/sys/class/net/") + devs = filter(networkConfig.isBridge, devs) + return devs + @staticmethod def getDefaultNetwork(): cmd = bash("route -n|awk \'/^0.0.0.0/ {print $2,$8}\'") if not cmd.isSuccess(): From 52f4683099e03e16916dfdf741f2d5544491aaad Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 28 Aug 2013 14:06:07 -0700 Subject: [PATCH 058/251] CLOUDSTACK-4528: [Vmware] new mapping vmware datacenter cloudstack zone - Virtual Router host migration across cluster FAIL Changes: - listHosts within same cluster for migration of system and router VMs --- server/src/com/cloud/server/ManagementServerImpl.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index b20bf48cded..3341c05da97 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1090,10 +1090,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // Check if the vm can be migrated with storage. boolean canMigrateWithStorage = false; - HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion( - srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); - if (capabilities != null) { - canMigrateWithStorage = capabilities.isStorageMotionSupported(); + + if (vm.getType() == VirtualMachine.Type.User) { + HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion( + srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); + if (capabilities != null) { + canMigrateWithStorage = capabilities.isStorageMotionSupported(); + } } // Check if the vm is using any disks on local storage. From b24e9a6dd5281b2163ce088e6f5b1fa730c33f7d Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 28 Aug 2013 15:54:30 -0700 Subject: [PATCH 059/251] CLOUDSTACK-4528 [Vmware] new mapping vmware datacenter cloudstack zone - Virtual Router host migration across cluster FAIL Change: - Also add a check in migrateSystemVM API to check that source and destination host are in the same cluster --- server/src/com/cloud/vm/UserVmManagerImpl.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index ce77630246b..251c203f13b 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3950,6 +3950,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + destinationHost.getResourceState()); } + if (vm.getType() != VirtualMachine.Type.User) { + // for System VMs check that the destination host is within the same + // cluster + HostVO srcHost = _hostDao.findById(srcHostId); + if (srcHost != null && srcHost.getClusterId() != null && destinationHost.getClusterId() != null) { + if (srcHost.getClusterId().longValue() != destinationHost.getClusterId().longValue()) { + throw new InvalidParameterValueException( + "Cannot migrate the VM, destination host is not in the same cluster as current host of the VM"); + } + } + } + checkHostsDedication(vm, srcHostId, destinationHost.getId()); // call to core process From cb12cf93544308cdac3ffd6062cc75e10cf16ab4 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 28 Aug 2013 16:34:56 -0700 Subject: [PATCH 060/251] CLOUDSTACK-4089: UI > zone wizard > hypervisor VMware > physical network > vmware_network_label: remove trailing comma if there is any. --- ui/scripts/zoneWizard.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 58119092db0..0ecddee3fdf 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -57,8 +57,13 @@ trafficLabel += trafficConfig.vSwitchType; } - if (trafficLabel.length == 0) //trafficLabel == '' + if (trafficLabel.length == 0) { //trafficLabel == '' trafficLabel = null; + } else if (trafficLabel.length >= 1) { + if (trafficLabel.charAt(trafficLabel.length-1) == ',') { //if last character is comma + trafficLabel = trafficLabel.substring(0, trafficLabel.length - 1); //remove the last character (which is comma) + } + } } } From 55583c0f97cecb2e4a44d5020e52209d609cb0cd Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 16:39:31 -0700 Subject: [PATCH 061/251] CLOUDSTACK-815. DOC. Fix Regions documentation for reviewer comments. --- docs/en-US/about-regions.xml | 5 +++-- docs/en-US/region-add.xml | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en-US/about-regions.xml b/docs/en-US/about-regions.xml index 432faeb6c5e..a12c183abd3 100644 --- a/docs/en-US/about-regions.xml +++ b/docs/en-US/about-regions.xml @@ -44,6 +44,7 @@ region-overview.png: Nested structure of a region. - Regions are visible to the end user. When a user starts a guest VM, the user must select a region for their guest. - Users might also be required to copy their private templates to additional regions to enable creation of guest VMs using their templates in those regions. + Regions are visible to the end user. When a user starts a guest VM on a particular &PRODUCT; Management Server, + the user is implicitly selecting that region for their guest. + Users might also be required to copy their private templates to additional regions to enable creation of guest VMs using their templates in those regions.
\ No newline at end of file diff --git a/docs/en-US/region-add.xml b/docs/en-US/region-add.xml index b4dd1814af8..212047ad89b 100644 --- a/docs/en-US/region-add.xml +++ b/docs/en-US/region-add.xml @@ -30,9 +30,8 @@ The First Region: The Default Region If you do not take action to define regions, then all the zones in your cloud will be automatically grouped into a single default region. This region is assigned the region - ID of 1. - You can change the name or URL of the default region by using the API command updateRegion. For example: - http://<IP_of_Management_Server>:8080/client/api?command=updateRegion&id=1&name=Northern&endpoint=http://<region_1_IP_address_here>:8080/client&apiKey=miVr6X7u6bN_sdahOBpjNejPgEsT35eXq-jB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ&signature=Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D + ID of 1. You can change the name or URL of the default region by displaying the region in + the &PRODUCT; UI and clicking the Edit button.
Adding a Region From 5036e4f9f9b3b06a1268657c6c0fcc1e1ec5e83a Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 17:01:30 -0700 Subject: [PATCH 062/251] CLOUDSTACK-883. DOC. How to integrate your 3rd-party plugin with the UI. Contains brief third-party plugin code tutorial. --- docs/en-US/Developers_Guide.xml | 1 + docs/en-US/images/plugin1.jpg | Bin 0 -> 32999 bytes docs/en-US/images/plugin2.jpg | Bin 0 -> 35149 bytes docs/en-US/images/plugin3.jpg | Bin 0 -> 41983 bytes docs/en-US/images/plugin4.jpg | Bin 0 -> 32125 bytes docs/en-US/images/plugin_intro.jpg | Bin 0 -> 22247 bytes docs/en-US/third-party-ui-plugin.xml | 347 +++++++++++++++++++++++++++ 7 files changed, 348 insertions(+) create mode 100644 docs/en-US/images/plugin1.jpg create mode 100644 docs/en-US/images/plugin2.jpg create mode 100644 docs/en-US/images/plugin3.jpg create mode 100644 docs/en-US/images/plugin4.jpg create mode 100644 docs/en-US/images/plugin_intro.jpg create mode 100644 docs/en-US/third-party-ui-plugin.xml diff --git a/docs/en-US/Developers_Guide.xml b/docs/en-US/Developers_Guide.xml index e6fb5ce9a41..7452e29ecf2 100644 --- a/docs/en-US/Developers_Guide.xml +++ b/docs/en-US/Developers_Guide.xml @@ -51,6 +51,7 @@ + diff --git a/docs/en-US/images/plugin1.jpg b/docs/en-US/images/plugin1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..970233d84750173bb91f03c89b19e8c1715572f6 GIT binary patch literal 32999 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 I(!MGF4_^TpE&u=k literal 0 HcmV?d00001 diff --git a/docs/en-US/images/plugin2.jpg b/docs/en-US/images/plugin2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9c8a6107ba92cca8fa7118cbca43c74cb2b15c14 GIT binary patch literal 35149 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

_J&#=$ZO5oUX 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 ZpB}DUi{YlnCdtx!71N4#|J{B~{};j52Aco? literal 0 HcmV?d00001 diff --git a/docs/en-US/images/plugin3.jpg b/docs/en-US/images/plugin3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..07fae790e22f416320beb52691ca03a06b7ccba4 GIT binary patch literal 41983 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 z

8B+8{RJ_mYU9TWj5 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 M`v1s6fqqZ_4>XQAe*gdg literal 0 HcmV?d00001 diff --git a/docs/en-US/images/plugin4.jpg b/docs/en-US/images/plugin4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2bcec9f773a04dbd5e159d3527ad39e690e78f9f GIT binary patch literal 32125 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 + +%BOOK_ENTITIES; +]> + + + Third-Party UI Plugin Framework + Using the new third-party plugin framework, you can write and install extensions to + &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the + other features. + The code for the plugin is simply placed in a special directory + within &PRODUCT;’s installed code at any time after &PRODUCT; installation. The new plugin + appears only when it is enabled by the cloud administrator. + + + + + + plugin_intro.jpg: New plugin button in product navbar + + + The left navigation bar of the &PRODUCT; UI has a new Plugins button to help you work with UI plugins. +

+ How to Write a Plugin: Overview + The basic procedure for writing a plugin is: + + + Write the code and create the other files needed. You will need the plugin code + itself (in Javascript), a thumbnail image, the plugin listing, and a CSS file. + + + + + + plugin1.jpg: Write the plugin code + + + All UI plugins have the following set of files: + +-- cloudstack/ + +-- ui/ + +-- plugins/ + +-- csMyFirstPlugin/ + +-- config.js --> Plugin metadata (title, author, vendor URL, etc.) + +-- icon.png --> Icon, shown on side nav bar and plugin listing + (should be square, and ~50x50px) + +-- csMyFirstPlugin.css --> CSS file, loaded automatically when plugin loads + +-- csMyFirstPlugin.js --> Main JS file, containing plugin code + + The same files must also be present at /tomcat/webapps/client/plugins. + + + The &PRODUCT; administrator adds the folder containing your plugin code under the + &PRODUCT; PLUGINS folder. + + + + + + plugin2.jpg: The plugin code is placed in the PLUGINS folder + + + + + The administrator also adds the name of your plugin to the plugin.js file in the + PLUGINS folder. + + + + + + plugin3.jpg: The plugin name is added to plugin.js in the PLUGINS + folder + + + + + The next time the user refreshes the UI in the browser, your plugin will appear in + the left navigation bar. + + + + + + plugin4.jpg: The plugin appears in the UI + + + + +
+
+ How to Write a Plugin: Implementation Details + This section requires an understanding of JavaScript and the &PRODUCT; API. You don't + need knowledge of specific frameworks for this tutorial (jQuery, etc.), since the + &PRODUCT; UI handles the front-end rendering for you. + There is much more to the &PRODUCT; UI framework than can be described here. The UI is + very flexible to handle many use cases, so there are countless options and variations. The + best reference right now is to read the existing code for the main UI, which is in the /ui + folder. Plugins are written in a very similar way to the main UI. + + + Create the directory to hold your plugin. + All plugins are composed of set of required files in the directory + /ui/plugins/pluginID, where pluginID is a short name for your plugin. It's recommended + that you prefix your folder name (for example, bfMyPlugin) to avoid naming conflicts + with other people's plugins. + In this example, the plugin is named csMyFirstPlugin. + $ cd cloudstack/ui/plugins +$ mkdir csMyFirstPlugin +$ ls -l + +total 8 +drwxr-xr-x 2 bgregory staff 68 Feb 11 14:44 csMyFirstPlugin +-rw-r--r-- 1 bgregory staff 101 Feb 11 14:26 plugins.js + + + + Change to your new plugin directory. + $ cd csMyFirstPlugin + + + + Set up the listing. + Add the file config.js, using your favorite editor. + $ vi config.js + Add the following content to config.js. This information will be displayed on the + plugin listing page in the UI: + (function (cloudStack) { + cloudStack.plugins.csMyFirstPlugin.config = { + title: 'My first plugin', + desc: 'Tutorial plugin', + externalLink: 'http://www.cloudstack.org/', + authorName: 'Test Plugin Developer', + authorEmail: 'plugin.developer@example.com' + }; +}(cloudStack)); + + + + Add a new main section. + Add the file csMyFirstPlugin.js, using your favorite editor. + $ vi csMyFirstPlugin.js + Add the following content to csMyFirstPlugin.js: + (function (cloudStack) { + cloudStack.plugins.csMyFirstPlugin = function(plugin) { + plugin.ui.addSection({ + id: 'csMyFirstPlugin', + title: 'My Plugin', + preFilter: function(args) { + return isAdmin(); + }, + show: function() { + return $('<div>').html('Content will go here'); + } + }); + }; +}(cloudStack)); + + + + Register the plugin. + You now have the minimal content needed to run the plugin, so you can activate the + plugin in the UI by adding it to plugins.js. First, edit the file: + $ cd cloudstack/ui/plugins +$ vi plugins.js + + Now add the following to plugins.js: + (function($, cloudStack) { + cloudStack.plugins = [ + 'csMyFirstPlugin' + ]; +}(jQuery, cloudStack)); + + + + Check the plugin in the UI. + First, copy all the plugin code that you have created so far to + /tomcat/webapps/client/plugins. Then refresh the browser and click Plugins in the side + navigation bar. You should see your new plugin. + + + Make the plugin do something. + Right now, you just have placeholder content in the new plugin. It's time to add + real code. In this example, you will write a basic list view, which renders data from + an API call. You will list all virtual machines owned by the logged-in user. To do + this, replace the 'show' function in the plugin code with a 'listView' block, + containing the required syntax for a list view. To get the data, use the + listVirtualMachines API call. Without any parameters, it will return VMs only for your + active user. Use the provided 'apiCall' helper method to handle the server call. Of + course, you are free to use any other method for making the AJAX call (for example, + jQuery's $.ajax method). + First, open your plugin's JavaScript source file in your favorite editor: + $ cd csMyFirstPlugin +$ vi csMyFirstPlugin.js + + Add the following code in csMyFirstPlugin.js: + (function (cloudStack) { + cloudStack.plugins.csMyFirstPlugin = function(plugin) { + plugin.ui.addSection({ + id: 'csMyFirstPlugin', + title: 'My Plugin', + preFilter: function(args) { + return isAdmin(); + }, + + // Render page as a list view + listView: { + id: 'testPluginInstances', + fields: { + name: { label: 'label.name' }, + instancename: { label: 'label.internal.name' }, + displayname: { label: 'label.display.name' }, + zonename: { label: 'label.zone.name' } + }, + dataProvider: function(args) { + // API calls go here, to retrive the data asynchronously + // + // On successful retrieval, call + // args.response.success({ data: [data array] }); + plugin.ui.apiCall('listVirtualMachines', { + success: function(json) { + var vms = json.listvirtualmachinesresponse.virtualmachine; + + args.response.success({ data: vms }); + }, + error: function(errorMessage) { + args.response.error(errorMessage) + } + }); + } + } + }); + }; +}(cloudStack)); + + + + Test the plugin. + First, copy all the plugin code that you have created so far to + /tomcat/webapps/client/plugins. Then refresh the browser. You can see that your + placeholder content was replaced with a list table, containing 4 columns of virtual + machine data. + + + Add an action button. + Let's add an action button to the list view, which will reboot the VM. To do this, + add an actions block under listView. After specifying the correct format, the actions + will appear automatically to the right of each row of data. + $ vi csMyFirstPlugin.js + + Now add the following new code in csMyFirstPlugin.js. (The dots ... show where we + have omitted some existing code for the sake of space. Don't actually cut and paste + that part): + ... + listView: { + id: 'testPluginInstances', + ... + + actions: { + // The key/ID you specify here will determine what icon is + // shown in the UI for this action, + // and will be added as a CSS class to the action's element + // (i.e., '.action.restart') + // + // -- here, 'restart' is a predefined name in &PRODUCT; that will + // automatically show a 'reboot' arrow as an icon; + // this can be changed in csMyFirstPlugin.css + restart: { + label: 'Restart VM', + messages: { + confirm: function() { return 'Are you sure you want to restart this VM?' }, + notification: function() { return 'Rebooted VM' } + }, + action: function(args) { + // Get the instance object of the selected row from context + // + // -- all currently loaded state is stored in 'context' as objects, + // such as the selected list view row, + // the selected section, and active user + // + // -- for list view actions, the object's key will be the same as + // listView.id, specified above; + // always make sure you specify an 'id' for the listView, + // or else it will be 'undefined!' + var instance = args.context.testPluginInstances[0]; + + plugin.ui.apiCall('rebootVirtualMachine', { + // These will be appended to the API request + // + // i.e., rebootVirtualMachine&id=... + data: { + id: instance.id + }, + success: function(json) { + args.response.success({ + // This is an async job, so success here only indicates + // that the job was initiated. + // + // To pass the job ID to the notification UI + // (for checking to see when action is completed), + // '_custom: { jobID: ... }' needs to always be passed on success, + // in the same format as below + _custom: { jobId: json.rebootvirtualmachineresponse.jobid } + }); + }, + + + error: function(errorMessage) { + args.response.error(errorMessage); // Cancel action, show error message returned + } + }); + }, + + // Because rebootVirtualMachine is an async job, we need to add + // a poll function, which will perodically check + // the management server to see if the job is ready + // (via pollAsyncJobResult API call) + // + // The plugin API provides a helper function, 'plugin.ui.pollAsyncJob', + / which will work for most jobs + // in &PRODUCT; + notification: { + poll: plugin.ui.pollAsyncJob + } + } + }, + + dataProvider: function(args) { + ... +... + + + + Add the thumbnail icon. + Create an icon file; it should be square, about 50x50 pixels, and named icon.png. + Copy it into the same directory with your plugin code: + cloudstack/ui/plugins/csMyFirstPlugin/icon.png. + + + Add the stylesheet. + Create a CSS file, with the same name as your .js file. Copy it into the same + directory with your plugin code: + cloudstack/ui/plugins/csMyFirstPlugin/csMyFirstPlugin.css. + + +
+ From 4fe0fcf2631271ec0dc5686358bb624a6fd4f02a Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 17:09:30 -0700 Subject: [PATCH 063/251] CLOUDSTACK-883. DOC. Small fix to comment in UI plugins section. --- docs/en-US/third-party-ui-plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/third-party-ui-plugin.xml b/docs/en-US/third-party-ui-plugin.xml index 62ee485b1d1..aaef167aada 100644 --- a/docs/en-US/third-party-ui-plugin.xml +++ b/docs/en-US/third-party-ui-plugin.xml @@ -4,7 +4,7 @@ %BOOK_ENTITIES; ]> - + Third-Party UI Plugin Framework Using the new third-party plugin framework, you can write and install extensions to &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the From 56ebf3b9b83a087b60d8de51f0276387a04c15a4 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 28 Aug 2013 17:24:34 -0700 Subject: [PATCH 064/251] CLOUDSTACK-883. DOC. Fix license header in UI plugins section. --- docs/en-US/third-party-ui-plugin.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/en-US/third-party-ui-plugin.xml b/docs/en-US/third-party-ui-plugin.xml index aaef167aada..297fdaa857f 100644 --- a/docs/en-US/third-party-ui-plugin.xml +++ b/docs/en-US/third-party-ui-plugin.xml @@ -3,6 +3,23 @@ %BOOK_ENTITIES; ]> + Third-Party UI Plugin Framework From a30a33c14d38a30840be19619c9aff65ed14cf2f Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Wed, 28 Aug 2013 10:58:02 -0700 Subject: [PATCH 065/251] CLOUDSTACK-4539: [VMWARE] vmware.create.full.clone is set to true in upgraded setup;default nature of vms are full clone Description: Do not overwrite value of vmware.create.full.clone flag in the cloud db if it already exists. This will preserve the configured clone creation behaviour across upgrades. --- setup/db/db/schema-410to420.sql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 4d688384922..723791deda5 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -403,7 +403,12 @@ CREATE TABLE `cloud`.`user_vm_clone_setting` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'UserVmManager', 'vmware.create.full.clone' , 'true', 'If set to true, creates VMs as full clones on ESX hypervisor'); +INSERT INTO `cloud`.`configuration` (category, instance, component, name, value, description) + SELECT tmp.category, tmp.instance, tmp.component, tmp.name, tmp.value, tmp.description FROM + (SELECT 'Advanced' category, 'DEFAULT' instance, 'UserVmManager' component, 'vmware.create.full.clone' name, 'true' value, 'If set to true, creates VMs as full clones on ESX hypervisor' description) tmp + WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE name = 'vmware.create.full.clone'); + + CREATE TABLE `cloud`.`affinity_group` ( `id` bigint unsigned NOT NULL auto_increment, From a316ffa27f84c261cace31982aaaa9c3aaf52e6a Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Wed, 28 Aug 2013 17:50:44 -0700 Subject: [PATCH 066/251] CLOUDSTACK-4519: reimplementdestroy-volume to take consideration of on-disk snapshot --- .../com/cloud/hypervisor/guru/VMwareGuru.java | 8 --- .../resource/VmwareStorageLayoutHelper.java | 42 ++++++++++++++-- .../resource/VmwareStorageProcessor.java | 33 ++++++------ .../vmware/mo/VirtualMachineMO.java | 50 ++++++++++++++++--- 4 files changed, 102 insertions(+), 31 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 7c70c6a6160..c8e82ab13b0 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -425,14 +425,6 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { return tokens[0] + "@" + vCenterIp; } - @Override - public List finalizeExpunge(VirtualMachine vm) { - UnregisterVMCommand unregisterVMCommand = new UnregisterVMCommand(vm.getInstanceName()); - List commands = new ArrayList(); - commands.add(unregisterVMCommand); - return commands; - } - @Override public List finalizeExpungeNics(VirtualMachine vm, List nics) { List commands = new ArrayList(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java index d60abad3519..20544df3d06 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage.resource; +import java.util.List; + import org.apache.log4j.Logger; import com.cloud.hypervisor.vmware.mo.DatacenterMO; @@ -166,6 +168,40 @@ public class VmwareStorageLayoutHelper { s_logger.info("Fixup folder-synchronization. move " + fileDsFullPath + " -> " + targetPath); ds.moveDatastoreFile(fileDsFullPath, dcMo.getMor(), ds.getMor(), targetPath, dcMo.getMor(), true); } + + public static void moveVolumeToRootFolder(DatacenterMO dcMo, List detachedDisks) throws Exception { + if(detachedDisks.size() > 0) { + for(String fileFullDsPath : detachedDisks) { + DatastoreFile file = new DatastoreFile(fileFullDsPath); + + s_logger.info("Check if we need to move " + fileFullDsPath + " to its root location"); + DatastoreMO dsMo = new DatastoreMO(dcMo.getContext(), dcMo.findDatastore(file.getDatastoreName())); + if(dsMo.getMor() != null) { + DatastoreFile targetFile = new DatastoreFile(file.getDatastoreName(), file.getFileName()); + if(!targetFile.getPath().equalsIgnoreCase(file.getPath())) { + s_logger.info("Move " + file.getPath() + " -> " + targetFile.getPath()); + dsMo.moveDatastoreFile(file.getPath(), dcMo.getMor(), dsMo.getMor(), targetFile.getPath(), dcMo.getMor(), true); + + String pairSrcFilePath = file.getCompanionPath(file.getFileBaseName() + "-flat.vmdk"); + String pairTargetFilePath = targetFile.getCompanionPath(file.getFileBaseName() + "-flat.vmdk"); + if(dsMo.fileExists(pairSrcFilePath)) { + s_logger.info("Move " + pairSrcFilePath + " -> " + pairTargetFilePath); + dsMo.moveDatastoreFile(pairSrcFilePath, dcMo.getMor(), dsMo.getMor(), pairTargetFilePath, dcMo.getMor(), true); + } + + pairSrcFilePath = file.getCompanionPath(file.getFileBaseName() + "-delta.vmdk"); + pairTargetFilePath = targetFile.getCompanionPath(file.getFileBaseName() + "-delta.vmdk"); + if(dsMo.fileExists(pairSrcFilePath)) { + s_logger.info("Move " + pairSrcFilePath + " -> " + pairTargetFilePath); + dsMo.moveDatastoreFile(pairSrcFilePath, dcMo.getMor(), dsMo.getMor(), pairTargetFilePath, dcMo.getMor(), true); + } + } + } else { + s_logger.warn("Datastore for " + fileFullDsPath + " no longer exists, we have to skip"); + } + } + } + } public static String getTemplateOnSecStorageFilePath(String secStorageMountPoint, String templateRelativeFolderPath, String templateName, String fileExtension) { @@ -227,7 +263,7 @@ public class VmwareStorageLayoutHelper { if(!dsMo.fileExists(fileFullPath)) fileFullPath = dsMo.searchFileInSubFolders(fileName, false); if(fileFullPath != null) { - dsMo.deleteFile(fileFullPath, dcMo.getMor(), false); + dsMo.deleteFile(fileFullPath, dcMo.getMor(), true); } else { s_logger.warn("Unable to locate VMDK file: " + fileName); } @@ -237,7 +273,7 @@ public class VmwareStorageLayoutHelper { if(!dsMo.fileExists(fileFullPath)) fileFullPath = dsMo.searchFileInSubFolders(fileName, false); if(fileFullPath != null) { - dsMo.deleteFile(fileFullPath, dcMo.getMor(), false); + dsMo.deleteFile(fileFullPath, dcMo.getMor(), true); } else { s_logger.warn("Unable to locate VMDK file: " + fileName); } @@ -247,7 +283,7 @@ public class VmwareStorageLayoutHelper { if(!dsMo.fileExists(fileFullPath)) fileFullPath = dsMo.searchFileInSubFolders(fileName, false); if(fileFullPath != null) { - dsMo.deleteFile(fileFullPath, dcMo.getMor(), false); + dsMo.deleteFile(fileFullPath, dcMo.getMor(), true); } else { s_logger.warn("Unable to locate VMDK file: " + fileName); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 4a0b1925c75..20c26fbc441 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -63,6 +63,7 @@ import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; import com.cloud.hypervisor.vmware.mo.NetworkDetails; +import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfo; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.resource.VmwareResource; @@ -1323,7 +1324,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } else { try{ vmMo.unmountToolsInstaller(); - }catch(Throwable e){ + } catch(Throwable e){ vmMo.detachIso(null); } } @@ -1462,18 +1463,6 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.info("Executing resource DestroyCommand: " + _gson.toJson(cmd)); } - /* - * DestroyCommand content example - * - * {"volume": {"id":5,"name":"Volume1", "mountPoint":"/export/home/kelven/vmware-test/primary", - * "path":"6bb8762f-c34c-453c-8e03-26cc246ceec4", "size":0,"type":"DATADISK","resourceType": - * "STORAGE_POOL","storagePoolType":"NetworkFilesystem", "poolId":0,"deviceId":0 } } - * - * {"volume": {"id":1, "name":"i-2-1-KY-ROOT", "mountPoint":"/export/home/kelven/vmware-test/primary", - * "path":"i-2-1-KY-ROOT","size":0,"type":"ROOT", "resourceType":"STORAGE_POOL", "storagePoolType":"NetworkFilesystem", - * "poolId":0,"deviceId":0 } } - */ - try { VmwareContext context = hostService.getServiceContext(null); VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null); @@ -1494,6 +1483,7 @@ public class VmwareStorageProcessor implements StorageProcessor { ClusterMO clusterMo = new ClusterMO(context, morCluster); if (vol.getVolumeType() == Volume.Type.ROOT) { + String vmName = vol.getVmName(); if (vmName != null) { VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(vmName); @@ -1502,6 +1492,14 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.info("Destroy root volume and VM itself. vmName " + vmName); } + // Remove all snapshots to consolidate disks for removal + vmMo.removeAllSnapshots(); + + VirtualMachineDiskInfo diskInfo = null; + if(vol.getChainInfo() != null) + diskInfo = _gson.fromJson(vol.getChainInfo(), VirtualMachineDiskInfo.class); + + HostMO hostMo = vmMo.getRunningHost(); List networks = vmMo.getNetworksWithDetails(); @@ -1509,7 +1507,12 @@ public class VmwareStorageProcessor implements StorageProcessor { if (resource.getVmState(vmMo) != State.Stopped) { vmMo.safePowerOff(_shutdown_waitMs); } - vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); + + List detachedDisks = vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ? diskInfo.getDiskDeviceBusName() : null); + VmwareStorageLayoutHelper.moveVolumeToRootFolder(new DatacenterMO(context, morDc), detachedDisks); + + // let vmMo.destroy to delete volume for us + // vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); vmMo.destroy(); for (NetworkDetails netDetails : networks) { @@ -1521,11 +1524,13 @@ public class VmwareStorageProcessor implements StorageProcessor { } } +/* if (s_logger.isInfoEnabled()) { s_logger.info("Destroy volume by original name: " + vol.getPath() + ".vmdk"); } VmwareStorageLayoutHelper.deleteVolumeVmdkFiles(dsMo, vol.getPath(), new DatacenterMO(context, morDc)); +*/ return new Answer(cmd, true, "Success"); } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 71ab609b54a..7dd947c39a9 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -991,13 +991,11 @@ public class VirtualMachineMO extends BaseMO { VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, getScsiDeviceControllerKey(), vmdkDatastorePathChain, morDs, -1, 1); VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); deviceConfigSpec.setDevice(newDisk); deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); - //deviceConfigSpecArray[0] = deviceConfigSpec; reConfigSpec.getDeviceChange().add(deviceConfigSpec); ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); @@ -1024,13 +1022,11 @@ public class VirtualMachineMO extends BaseMO { VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, controllerKey, vmdkDatastorePathChain, -1, 1); VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); deviceConfigSpec.setDevice(newDisk); deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); - //deviceConfigSpecArray[0] = deviceConfigSpec; reConfigSpec.getDeviceChange().add(deviceConfigSpec); ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); @@ -1067,7 +1063,6 @@ public class VirtualMachineMO extends BaseMO { List> chain = getDiskDatastorePathChain(deviceInfo.first(), true); VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); - //VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1]; VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); deviceConfigSpec.setDevice(deviceInfo.first()); @@ -1076,7 +1071,6 @@ public class VirtualMachineMO extends BaseMO { } deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE); - //deviceConfigSpecArray[0] = deviceConfigSpec; reConfigSpec.getDeviceChange().add(deviceConfigSpec); ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); @@ -1964,6 +1958,50 @@ public class VirtualMachineMO extends BaseMO { throw new Exception("Unable to find device controller"); } + public List detachAllDisksExcept(String vmdkBaseName, String deviceBusName) throws Exception { + List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); + List detachedDiskFiles = new ArrayList(); + + for(VirtualDevice device : devices) { + if(device instanceof VirtualDisk) { + VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); + + VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)device.getBacking(); + + DatastoreFile dsBackingFile = new DatastoreFile(diskBackingInfo.getFileName()); + String backingBaseName = dsBackingFile.getFileBaseName(); + String deviceNumbering = getDeviceBusName(devices, device); + if(backingBaseName.equalsIgnoreCase(vmdkBaseName) || (deviceBusName != null && deviceBusName.equals(deviceNumbering))) { + continue; + } else { + s_logger.info("Detach " + diskBackingInfo.getFileName() + " from " + getName()); + + detachedDiskFiles.add(diskBackingInfo.getFileName()); + + deviceConfigSpec.setDevice(device); + deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE); + + reConfigSpec.getDeviceChange().add(deviceConfigSpec); + } + } + } + + if(detachedDiskFiles.size() > 0) { + ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); + boolean result = _context.getVimClient().waitForTask(morTask); + if(result) { + _context.waitForTaskProgressDone(morTask); + } else { + s_logger.warn("Unable to reconfigure the VM to detach disks"); + throw new Exception("Unable to reconfigure the VM to detach disks"); + } + } + + return detachedDiskFiles; + } + public VirtualDisk[] getAllDiskDevice() throws Exception { List deviceList = new ArrayList(); List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); From 995e193be2a1aad8ffe0eeb03357e23fb5c63769 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Wed, 28 Aug 2013 18:37:45 -0700 Subject: [PATCH 067/251] CLOUDSTACK-4458: Volume attach/detach command needs to sent to hypervisor resource even when target VM is in Stopped state --- .../storage/snapshot/SnapshotServiceImpl.java | 1 - .../src/com/cloud/hypervisor/guru/VMwareGuru.java | 2 +- .../storage/resource/VmwareStorageProcessor.java | 2 +- server/src/com/cloud/storage/VolumeManagerImpl.java | 12 +++++++++++- .../cloud/hypervisor/vmware/mo/VirtualMachineMO.java | 3 +-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index e53bf9c0529..00d96316e24 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -247,7 +247,6 @@ public class SnapshotServiceImpl implements SnapshotService { AsyncCallFuture future = new AsyncCallFuture(); SnapshotResult result = new SnapshotResult(snapshot, null); try { - snapObj.processEvent(Snapshot.Event.BackupToSecondary); DataStore imageStore = this.findSnapshotImageStore(snapshot); 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 c8e82ab13b0..73cc8e3e5a2 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -351,7 +351,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { } } } - + if(!needDelegation) { return new Pair(Boolean.FALSE, new Long(hostId)); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 20c26fbc441..14fca3afd8d 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -969,7 +969,7 @@ public class VmwareStorageProcessor implements StorageProcessor { throw new Exception("unable to prepare snapshot backup directory"); } } - } + } VirtualMachineMO clonedVm = null; try { diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 35fe72a43a3..85a19ca63b3 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -2026,6 +2026,16 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName(); boolean sendCommand = (vm.getState() == State.Running); + Long hostId = vm.getHostId(); + if (hostId == null) { + hostId = vm.getLastHostId(); + HostVO host = _hostDao.findById(hostId); + if (host != null + && host.getHypervisorType() == HypervisorType.VMware) { + sendCommand = true; + } + } + Answer answer = null; if (sendCommand) { @@ -2044,7 +2054,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { cmd.set_iScsiName(volume.get_iScsiName()); try { - answer = _agentMgr.send(vm.getHostId(), cmd); + answer = _agentMgr.send(hostId, cmd); } catch (Exception e) { throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 7dd947c39a9..6007bf0f1aa 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1055,8 +1055,7 @@ public class VirtualMachineMO extends BaseMO { // VirtualDisk, we only perform prefix matching Pair deviceInfo = getDiskDevice(vmdkDatastorePath, false); if(deviceInfo == null) { - if(s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - detachDisk() done (failed)"); + s_logger.warn("vCenter API trace - detachDisk() done (failed)"); throw new Exception("No such disk device: " + vmdkDatastorePath); } From 4a7d7324d0f71888365c0b8f235b30909c9cbceb Mon Sep 17 00:00:00 2001 From: bharat kumar Date: Thu, 29 Aug 2013 12:24:45 +0530 Subject: [PATCH 068/251] CLOUDSTACK-4538 Should update the vmware clusters with the global overporvisioning factors after upgrade to 4.2. Signed off by : Nitin Mehta --- .../cloud/upgrade/dao/Upgrade410to420.java | 66 ++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 5db64200a9e..a2c6b4e126a 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -845,31 +845,67 @@ public class Upgrade410to420 implements DbUpgrade { PreparedStatement pstmt = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 =null; - ResultSet rs = null; - + PreparedStatement pstmt3 = null; + ResultSet rs1 = null; + ResultSet rscpu_global = null; + ResultSet rsmem_global = null; try { - pstmt = conn.prepareStatement("select id from `cloud`.`cluster`"); - pstmt1=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'cpuOvercommitRatio', '1')"); - pstmt2=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'memoryOvercommitRatio', '1')"); - rs = pstmt.executeQuery(); - while (rs.next()) { - long id = rs.getLong(1); - //update cluster_details table with the default overcommit ratios. - pstmt1.setLong(1,id); - pstmt1.execute(); - pstmt2.setLong(1,id); - pstmt2.execute(); + pstmt = conn.prepareStatement("select id, hypervisor_type from `cloud`.`cluster`"); + pstmt1=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'cpuOvercommitRatio', ?)"); + pstmt2=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'memoryOvercommitRatio', ?)"); + pstmt3=conn.prepareStatement("select value from `cloud`.`configuration` where name=?"); + pstmt3.setString(1,"cpu.overprovisioning.factor"); + rscpu_global = pstmt3.executeQuery(); + rscpu_global.next(); + String global_cpu_overprovisioning_factor=rscpu_global.getString(1); + pstmt3.setString(1,"mem.overprovisioning.factor"); + rsmem_global = pstmt3.executeQuery(); + rsmem_global.next(); + String global_mem_overprovisioning_factor = rsmem_global.getString(1); + rs1 = pstmt.executeQuery(); + + while (rs1.next()) { + long id = rs1.getLong(1); + String hypervisor_type = rs1.getString(2); + if (hypervisor_type.equalsIgnoreCase(HypervisorType.VMware.toString())) { + pstmt1.setLong(1,id); + pstmt1.setString(2,global_cpu_overprovisioning_factor); + pstmt1.execute(); + pstmt2.setLong(1,id); + pstmt2.setString(2,global_mem_overprovisioning_factor); + pstmt2.execute(); + }else { + //update cluster_details table with the default overcommit ratios. + pstmt1.setLong(1,id); + pstmt1.setString(2,"1"); + pstmt1.execute(); + pstmt2.setLong(1,id); + pstmt2.setString(2,"1"); + pstmt2.execute(); + } } } catch (SQLException e) { throw new CloudRuntimeException("Unable to update cluster_details with default overcommit ratios.", e); } finally { try { - if (rs != null) { - rs.close(); + if (rs1 != null) { + rs1.close(); + } + if (rsmem_global != null) { + rsmem_global.close(); + } + if (rscpu_global != null) { + rsmem_global.close(); } if (pstmt != null) { pstmt.close(); } + if (pstmt2 != null) { + pstmt2.close(); + } + if (pstmt3 != null) { + pstmt3.close(); + } } catch (SQLException e) { } } From dc071e963e3df9e504f951a28117f567558bb496 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Thu, 29 Aug 2013 12:33:26 +0530 Subject: [PATCH 069/251] CLOUDSTACK-4499: Wait until hosts are up before adding storage pools This works around the delay caused in adding Xen 6.1/6.2 hosts where host takes two attempts to transition to Up state. We will wait for one minute per cluster before attempting to add storage pool in the cluster. Signed-off-by: Prasanna Santhanam (cherry picked from commit 22ee499c3571b2a6b6921abb36c679128893c415) --- tools/marvin/marvin/deployDataCenter.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index 53520299a2b..4c34a5099cf 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -22,6 +22,7 @@ import cloudstackTestClient import logging from cloudstackAPI import * from os import path +from time import sleep from optparse import OptionParser @@ -87,9 +88,28 @@ specify a valid config file" % cfgFile) if cluster.hypervisor.lower() != "vmware": self.addHosts(cluster.hosts, zoneId, podId, clusterId, cluster.hypervisor) + self.wait_for_host(zoneId, clusterId) self.createPrimaryStorages(cluster.primaryStorages, zoneId, podId, clusterId) + def wait_for_host(self, zoneId, clusterId): + """ + Wait for the hosts in the zoneid, clusterid to be up + + 2 retries with 30s delay + """ + retry, timeout = 2, 30 + cmd = listHosts.listHostsCmd() + cmd.clusterid, cmd.zoneid = clusterId, zoneId + hosts = self.apiClient.listHosts(cmd) + while retry != 0: + for host in hosts: + if host.state != 'Up': + break + sleep(timeout) + retry = retry - 1 + + def createPrimaryStorages(self, primaryStorages, zoneId, podId, clusterId): if primaryStorages is None: return From de86e5e63dcb5f11c139acf4f0f027fbecbaa00d Mon Sep 17 00:00:00 2001 From: Girish Shilamkar Date: Mon, 26 Aug 2013 18:27:53 +0530 Subject: [PATCH 070/251] CLOUDSTACK-4407: Use extractTemplate API to get hypervisor specific template information Template url, hypervisor and format were defined in Service class to be Xenserver specific and therefore registering a new template failed on Vmware and KVM. Fixed this to get hypervisor specific info for registering new template. Signed-off-by: Prasanna Santhanam (cherry picked from commit 20256706b376551fe8993ee2e73c61df31dcb6de) --- test/integration/component/test_templates.py | 72 ++++++++++--------- tools/marvin/marvin/integration/lib/base.py | 11 +++ tools/marvin/marvin/integration/lib/common.py | 19 +++++ 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/test/integration/component/test_templates.py b/test/integration/component/test_templates.py index e4599d41981..ea4b27755ca 100644 --- a/test/integration/component/test_templates.py +++ b/test/integration/component/test_templates.py @@ -174,67 +174,69 @@ class TestCreateTemplate(cloudstackTestCase): # tar bzip template. # 6. Verify VMs & Templates is up and in ready state - for k, v in self.services["templates"].items(): + builtin_info = get_builtin_template_info(self.apiclient, self.zone.id) + self.services["templates"][0]["url"] = builtin_info[0] + self.services["templates"][0]["hypervisor"] = builtin_info[1] + self.services["templates"][0]["format"] = builtin_info[2] - # Register new template - template = Template.register( + # Register new template + template = Template.register( self.apiclient, - v, + self.services["templates"][0], zoneid=self.zone.id, account=self.account.name, domainid=self.account.domainid ) - self.debug( + self.debug( "Registered a template of format: %s with ID: %s" % ( - v["format"], + self.services["templates"][0]["format"], template.id )) - # Wait for template to download - template.download(self.apiclient) - self.cleanup.append(template) + # Wait for template to download + template.download(self.apiclient) + self.cleanup.append(template) - # Wait for template status to be changed across - time.sleep(self.services["sleep"]) - timeout = self.services["timeout"] - while True: - list_template_response = list_templates( + # Wait for template status to be changed across + time.sleep(self.services["sleep"]) + timeout = self.services["timeout"] + while True: + list_template_response = list_templates( self.apiclient, - templatefilter=\ - self.services["templatefilter"], + templatefilter='all', id=template.id, zoneid=self.zone.id, account=self.account.name, domainid=self.account.domainid ) - if isinstance(list_template_response, list): - break - elif timeout == 0: - raise Exception("List template failed!") + if isinstance(list_template_response, list): + break + elif timeout == 0: + raise Exception("List template failed!") - time.sleep(5) - timeout = timeout - 1 - #Verify template response to check whether template added successfully - self.assertEqual( + time.sleep(5) + timeout = timeout - 1 + #Verify template response to check whether template added successfully + self.assertEqual( isinstance(list_template_response, list), True, "Check for list template response return valid data" ) - self.assertNotEqual( + self.assertNotEqual( len(list_template_response), 0, "Check template available in List Templates" ) - template_response = list_template_response[0] - self.assertEqual( + template_response = list_template_response[0] + self.assertEqual( template_response.isready, True, "Check display text of newly created template" ) - # Deploy new virtual machine using template - virtual_machine = VirtualMachine.create( + # Deploy new virtual machine using template + virtual_machine = VirtualMachine.create( self.apiclient, self.services["virtual_machine"], templateid=template.id, @@ -243,26 +245,26 @@ class TestCreateTemplate(cloudstackTestCase): serviceofferingid=self.service_offering.id, mode=self.services["mode"] ) - self.debug("creating an instance with template ID: %s" % template.id) - vm_response = list_virtual_machines( + self.debug("creating an instance with template ID: %s" % template.id) + vm_response = list_virtual_machines( self.apiclient, id=virtual_machine.id, account=self.account.name, domainid=self.account.domainid ) - self.assertEqual( + self.assertEqual( isinstance(vm_response, list), True, "Check for list VMs response after VM deployment" ) #Verify VM response to check whether VM deployment was successful - self.assertNotEqual( + self.assertNotEqual( len(vm_response), 0, "Check VMs available in List VMs response" ) - vm = vm_response[0] - self.assertEqual( + vm = vm_response[0] + self.assertEqual( vm.state, 'Running', "Check the state of VM created from Template" diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index c528cea24fc..215263772d1 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -902,6 +902,17 @@ class Template: if isinstance(template, list): return Template(template[0].__dict__) + @classmethod + def extract(cls, apiclient, id, mode, zoneid=None): + "Extract template " + + cmd = extractTemplate.extractTemplateCmd() + cmd.id = id + cmd.mode = mode + cmd.zoneid = zoneid + + return apiclient.extractTemplate(cmd) + @classmethod def create_from_snapshot(cls, apiclient, snapshot, services, random_name=True): diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index f27e87df38f..478aa318908 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -265,6 +265,25 @@ def wait_for_ssvms(apiclient, zoneid, podid, interval=60): break return +def get_builtin_template_info(apiclient, zoneid): + """Returns hypervisor specific infor for templates""" + + list_template_response = Template.list( + apiclient, + templatefilter='featured', + zoneid=zoneid, + ) + + for b_template in list_template_response: + if b_template.templatetype == 'BUILTIN': + break + + extract_response = Template.extract(apiclient, + b_template.id, + 'HTTP_DOWNLOAD', + zoneid) + + return extract_response.url, b_template.hypervisor, b_template.format def download_builtin_templates(apiclient, zoneid, hypervisor, host, linklocalip, interval=60): From f585c605e2e071a49e4175049f9d02ca2b498283 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Mon, 29 Jul 2013 04:00:08 -0400 Subject: [PATCH 071/251] Automation: Limit Resources - Adding tests related to CPU limits Reviewed-by: Sanjay Tripathi Signed-off-by: Prasanna Santhanam (cherry picked from commit 95e4b7afa80a9d4d6567e5058bbcd7b755c87680) --- .../component/cpu_limits/test_cpu_limits.py | 747 +++++++++++++++++ .../cpu_limits/test_domain_limits.py | 761 ++++++++++++++++++ .../cpu_limits/test_maximum_limits.py | 374 +++++++++ .../cpu_limits/test_project_limits.py | 347 ++++++++ tools/marvin/marvin/integration/lib/base.py | 30 + tools/marvin/marvin/integration/lib/common.py | 57 ++ 6 files changed, 2316 insertions(+) create mode 100644 test/integration/component/cpu_limits/test_cpu_limits.py create mode 100644 test/integration/component/cpu_limits/test_domain_limits.py create mode 100644 test/integration/component/cpu_limits/test_maximum_limits.py create mode 100644 test/integration/component/cpu_limits/test_project_limits.py diff --git a/test/integration/component/cpu_limits/test_cpu_limits.py b/test/integration/component/cpu_limits/test_cpu_limits.py new file mode 100644 index 00000000000..96be62b12d1 --- /dev/null +++ b/test/integration/component/cpu_limits/test_cpu_limits.py @@ -0,0 +1,747 @@ +# 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. + +""" Tests for cpu resource limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Domain, + Resources + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + find_suitable_host, + get_resource_type + ) + + +class Services: + """Test resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 4, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestCPULimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestCPULimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + self.vm = self.createInstance(service_off=self.service_offering) + + self.cleanup = [self.account, ] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.apiclient + + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_multiplecore_start_stop_instance(self): + """Test Deploy VM with multiple core CPU & verify the usage""" + + # Validate the following + # 1. Deploy VM with multiple core CPU & verify the usage + # 2. Stop VM & verify the update resource count of Root Admin Account + # 3. Start VM & verify the update resource count of Root Admin Account + # 4. Resource count should list properly. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % self.vm.name) + try: + self.vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % self.vm.name) + try: + self.vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_start, + "Resource count should be same after stopping the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_multiplecore_migrate_instance(self): + """Test Deploy VM with multiple core CPU & verify the usage""" + + # Validate the following + # 1. Deploy VM with multiple core CPU & verify the usage + # 2. Migrate VM & verify updated resource count of Root Admin Account + # 3. Resource count should list properly. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, self.vm) + self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) + try: + self.vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after migrating the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_multiplecore_delete_instance(self): + """Test Deploy VM with multiple core CPU & verify the usage""" + + # Validate the following + # 1. Deploy VM with multiple core CPU & verify the usage + # 2. Destroy VM & verify update resource count of Root Admin Account + # 3. Resource count should list properly. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % self.vm.name) + try: + self.vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_04_deploy_multiple_vm_with_multiple_cpus(self): + """Test Deploy multiple VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create compute offering with 4 core CPU + # 2. Deploy multiple VMs with this service offering + # 3. List Resource count for the root admin CPU usage + # 4. CPU usage should list properly + # 5. Destroy one VM among multiple VM's and verify the resource count + # 6. Migrate VM from & verify resource updates + # 7. List resource count for Root Admin + # 8. Failed to deploy VM and verify the resource usage + + self.debug("Creating service offering with 4 CPU cores") + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(service_off=self.service_offering) + vm_2 = self.createInstance(service_off=self.service_offering) + self.createInstance(service_off=self.service_offering) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 Vms + self.assertTrue(resource_count == expected_resource_count, + "Resource count does not match the expected vavlue") + return + +class TestDomainCPULimitsConfiguration(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDomainCPULimitsConfiguration, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.apiclient + + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a sub-domain under: %s" % self.domain.name) + self.child_domain_1 = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + self.child_do_admin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_1.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_1) + self.cleanup.append(self.child_domain_1) + + self.child_domain_2 = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + + self.child_do_admin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_2.id + ) + + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_2) + self.cleanup.append(self.child_domain_2) + + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_stop_start_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create compute offering with 4 core CPU & Deploy VM + # 2. List Resource count CPU usage + # 3. Stop and Start instance, check resource count. + # 4. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % vm.name) + try: + vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].cputotal + + self.assertEqual(resource_count_after_stop, resource_count_after_start, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_migrate_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create compute offering with 4 core CPU & Deploy VM + # 2. List Resource count + # 3. Migrate instance to another host + # 4. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") + + host = find_suitable_host(self.apiclient, vm) + self.debug("Migrating instance: %s to host: %s" % + (vm.name, host.name)) + try: + vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_delete_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create compute offering with 4 core CPU & Deploy VM + # 2. List Resource count for the CPU usage + # 3. Delete instance + # 4. Resource count should list as 0 + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % vm.name) + try: + vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + return + + @attr(tags=["advanced", "advancedns","simulator"]) + @attr(configuration='max.account.cpus') + def test_04_deploy_multiple_vm_with_multiple_cpus(self): + """Test Deploy multiple VM with 4 core CPU & verify the usage""" + #keep the configuration value - max.account.cpus number = 16 + + # Validate the following + # 1. Create compute offering with 4 core CPU + # 2. Deploy multiple VMs with this service offering + # 3. List Resource count for the root admin CPU usage + # 4. CPU usage should list properly + + self.debug("Creating service offering with 4 CPU cores") + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + cpu_account_gc = Resources.list(self.apiclient, + resourcetype = 8, #CPU + account = self.account.name, + domainid = self.domain.id + ) + + if cpu_account_gc[0].max != 16: + self.skipTest("This test case requires configuration value max.account.cpus to be 16") + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) + vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance - CPU capacity is fully utilized") + with self.assertRaises(Exception): + self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 vms + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") + + self.debug("Destroying instance: %s" % vm_1.name) + try: + vm_1.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].cputotal + + expected_resource_count -= int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count_after_delete, expected_resource_count, + "Resource count should be less than before after deleting the instance") + + host = find_suitable_host(self.apiclient, vm_2) + self.debug("Migrating instance: %s to host: %s" % (vm_2.name, + host.name)) + try: + vm_2.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal + + self.debug(resource_count_after_migrate) + self.assertEqual(resource_count_after_delete, resource_count_after_migrate, + "Resource count should be same after migrating the instance") + return diff --git a/test/integration/component/cpu_limits/test_domain_limits.py b/test/integration/component/cpu_limits/test_domain_limits.py new file mode 100644 index 00000000000..057c6b9a0b3 --- /dev/null +++ b/test/integration/component/cpu_limits/test_domain_limits.py @@ -0,0 +1,761 @@ +# 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. + +""" Tests for cpu resource limits related to domains +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Resources, + Domain + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + find_suitable_host, + get_resource_type + ) + +class Services: + """Test resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 4, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestDomainCPULimitsUpdateResources(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDomainCPULimitsUpdateResources, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.apiclient + + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a sub-domain under: %s" % self.domain.name) + self.child_domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + self.child_do_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) + + Resources.updateLimit( + self.apiclient, + resourcetype=8, + max=16, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid + ) + + self.domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + + self.admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + # Cleanup the resources created at end of test + self.cleanup.append(self.admin) + self.cleanup.append(self.domain) + + Resources.updateLimit( + self.apiclient, + resourcetype=8, + max=16, + account=self.admin.name, + domainid=self.admin.domainid + ) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_multiple_core_vm_start_stop_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create two domains and set specific resource (cpu) limit for them + # 2. Create compute offering with 4 core CPU & deploy vm + # 3. Update Resource count for the domains + # 4. Reboot instance and check resource count + # 5. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same as before, after stopping the instance") + + self.debug("Starting instance: %s" % vm.name) + try: + vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].cputotal + + self.assertEqual(resource_count_after_stop, resource_count_after_start, + "Resource count should be same as before, after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_multiple_core_vm_migrate_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create two domains and set specific resource (cpu) limit for them + # 2. Create compute offering with 4 core CPU & deploy vm + # 3. Update Resource count for the domains + # 4. Migrate instance to new host and check resource count + # 5. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, vm) + self.debug("Migrating instance: %s to host: %s" % + (vm.name, host.name)) + try: + vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same as before, after migrating the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_multiple_core_vm_delete_instance(self): + """Test Deploy VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create two domains and set specific resource (cpu) limit for them + # 2. Create compute offering with 4 core CPU & deploy vm + # 3. Update Resource count for the domains + # 4. delete instance and check resource count + # 5. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") + + self.debug("Destroying instance: %s" % vm.name) + try: + vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].cputotal + + self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_04_deploy_multiple_vm_with_multiple_core(self): + """Test Deploy multiple VM with 4 core CPU & verify the usage""" + + # Validate the following + # 1. Create compute offering with 4 core CPU + # 2. Deploy multiple VMs within domain with this service offering + # 3. Update Resource count for the domain + # 4. CPU usage should list properly + + self.debug("Creating service offering with 4 CPU cores") + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) + vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance - CPU capacity is fully utilized") + with self.assertRaises(Exception): + self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 VMs + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should be 4") + + self.debug("Destroying instance: %s" % vm_1.name) + try: + vm_1.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].cputotal + + expected_resource_count -= int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count_after_delete, expected_resource_count, + "Resource count should match with the expected count") + + host = find_suitable_host(self.apiclient, vm_2) + self.debug("Migrating instance: %s to host: %s" % (vm_2.name, + host.name)) + try: + vm_2.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal + + self.assertEqual(resource_count_after_migrate, resource_count_after_delete, + "Resource count should not change after migrating the instance") + return + +class TestMultipleChildDomains(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMultipleChildDomains, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls._cleanup = [] + 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, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, account, service_off, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.apiclient + + self.debug("Deploying an instance in account: %s" % + account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.parent_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.parentd_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.debug("Updating the Memory resource limit for domain: %s" % + self.domain.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=10, + domainid=self.parentd_admin.domainid, + account=self.parentd_admin.name) + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + + self.cadmin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_1.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cdomain_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=4, + domainid=self.cadmin_1.domainid) + + self.debug("Updating the Memory resource count for account: %s" % + self.cadmin_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=2, + account=self.cadmin_1.name, + domainid=self.cadmin_1.domainid) + + self.cadmin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_2.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cdomain_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=5, + domainid=self.cadmin_2.domainid) + + self.debug("Updating the Memory resource count for account: %s" % + self.cadmin_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=3, + account=self.cadmin_2.name, + domainid=self.cadmin_2.domainid) + # Cleanup the resources created at end of test + self.cleanup.append(self.cadmin_1) + self.cleanup.append(self.cadmin_2) + self.cleanup.append(self.cdomain_1) + self.cleanup.append(self.cdomain_2) + self.cleanup.append(self.parentd_admin) + self.cleanup.append(self.parent_domain) + + users = { + self.parent_domain: self.parentd_admin, + self.cdomain_1: self.cadmin_1, + self.cdomain_2: self.cadmin_2 + } + return users + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_multiple_child_domains(self): + """Test CPU limits with multiple child domains""" + + # Validate the following + # 1. Create Domain1 with 10 core CPU and 2 child domains with 4 core + # each.Assign 2 cores for Domain1 admin1 & Domain1 User1 .Assign 2 + # cores for Domain2 admin1 & Domain2 User1 + # 2. Deploy VM's by Domain1 admin1/user1/ Domain2 user1/Admin1 account + # and verify the resource updates + # 3. Deploy VM by admin account after reaching max parent domain limit + # 4. Deploy VM with child account after reaching max child domain limit + # 5. Destroy user/admin account VM's and verify the child & Parent + # domain resource updates + + self.debug("Creating service offering with 2 CPU cores") + self.services["service_offering"]["cpunumber"] = 2 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + + api_client_cadmin_1 = self.testClient.createUserApiClient( + UserName=self.cadmin_1.name, + DomainName=self.cadmin_1.domain) + + api_client_cadmin_2 = self.testClient.createUserApiClient( + UserName=self.cadmin_2.name, + DomainName=self.cadmin_2.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(account=self.cadmin_1, + service_off=self.service_offering, api_client=api_client_cadmin_1) + + vm_2 = self.createInstance(account=self.cadmin_2, + service_off=self.service_offering, api_client=api_client_cadmin_2) + + self.debug("Checking resource count for account: %s" % self.cadmin_1.name) + + account_list = Account.list(self.apiclient, id=self.cadmin_1.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_1 = account_list[0].cputotal + + self.debug(resource_count_cadmin_1) + + self.debug("Checking resource count for account: %s" % self.cadmin_2.name) + account_list = Account.list(self.apiclient, id=self.cadmin_2.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_2 = account_list[0].cputotal + + self.debug(resource_count_cadmin_2) + + self.debug( + "Creating instance when CPU limit is fully used in child domain 1") + with self.assertRaises(Exception): + self.createInstance(account=self.cadmin_1, + service_off=self.service_offering, api_client=api_client_cadmin_1) + + self.debug( + "Creating instance when CPU limit is fully used in child domain 2") + with self.assertRaises(Exception): + self.createInstance(account=self.cadmin_2, + service_off=self.service_offering, api_client=api_client_cadmin_2) + self.debug("Destroying instances: %s, %s" % (vm_1.name, vm_2.name)) + try: + vm_1.delete(self.apiclient) + vm_2.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + self.debug("Checking resource count for account: %s" % self.cadmin_1.name) + + account_list = Account.list(self.apiclient, id=self.cadmin_1.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_1 = account_list[0].cputotal + + self.debug(resource_count_cadmin_1) + self.assertEqual(resource_count_cadmin_1, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + + self.debug("Checking resource count for account: %s" % self.cadmin_2.name) + account_list = Account.list(self.apiclient, id=self.cadmin_2.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_2 = account_list[0].cputotal + + self.debug(resource_count_cadmin_2) + self.assertEqual(resource_count_cadmin_2, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + return diff --git a/test/integration/component/cpu_limits/test_maximum_limits.py b/test/integration/component/cpu_limits/test_maximum_limits.py new file mode 100644 index 00000000000..dec84139645 --- /dev/null +++ b/test/integration/component/cpu_limits/test_maximum_limits.py @@ -0,0 +1,374 @@ +# 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. + +""" Tests for cpu resource limits - Maximum Limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Resources, + Domain, + Project + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources + ) + +class Services: + """Test resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 5, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestMaxCPULimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMaxCPULimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls._cleanup = [] + 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, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, account=None, + project=None, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.apiclient + + self.debug("Deploying instance") + try: + if account: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + elif project: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self, account_limit=2, domain_limit=2, project_limit=2): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.child_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + + self.debug("domain crated with domain id %s" % self.child_domain.id) + + self.child_do_admin = Account.create(self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain.id) + + self.debug("domain admin created for domain id %s" % + self.child_do_admin.domainid) + + # Create project as a domain admin + self.project = Project.create(self.apiclient, + self.services["project"], + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + # Cleanup created project at end of test + self.cleanup.append(self.project) + + # Cleanup accounts created + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) + + self.debug("Updating the CPU resource count for domain: %s" % + self.child_domain.name) + # Update resource limits for account 1 + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=account_limit, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + + self.debug("CPU Resource count for child domain admin account is now: %s" % + responses.max) + + self.debug("Updating the CPU limit for project") + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=project_limit, + projectid=self.project.id) + + self.debug("CPU Resource count for project is now") + self.debug(responses.max) + + self.debug("Updating the CPU limit for domain only") + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=domain_limit, + domainid=self.child_domain.id) + + self.debug("CPU Resource count for domain %s with id %s is now %s" % + (responses.domain, responses.domainid, responses.max)) + + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_deploy_vm_domain_limit_reached(self): + """Test Try to deploy VM with admin account where account has not used + the resources but @ domain they are not available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ domain they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Creating service offering with 3 CPU cores") + self.services["service_offering"]["cpunumber"] = 3 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=4, domain_limit=2) + + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + with self.assertRaises(Exception): + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client_admin) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_deploy_vm_account_limit_reached(self): + """Test Try to deploy VM with admin account where account has used + the resources but @ domain they are available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has used the + # resources but @ domain they are available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Creating service offering with 4 CPU cores") + self.services["service_offering"]["cpunumber"] = 4 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=6, domain_limit=8) + + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance with account: %s" % + self.child_do_admin.name) + + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client_admin) + + self.debug("Deploying instance when CPU limit is reached in account") + + with self.assertRaises(Exception): + self.createInstance(account=self.chid_do_admin, + service_off=self.service_offering, api_client=api_client_admin) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_deploy_vm_project_limit_reached(self): + """Test TTry to deploy VM with admin account where account has not used + the resources but @ project they are not available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ project they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Creating service offering with 3 CPU cores") + self.services["service_offering"]["cpunumber"] = 3 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=4, domain_limit=4, project_limit=2) + + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance in account 2 when CPU limit is reached") + + with self.assertRaises(Exception): + self.createInstance(project=self.project, + service_off=self.service_offering, api_client=api_client_admin) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_04_deployVm__account_limit_reached(self): + """Test Try to deploy VM with admin account where account has used + the resources but @ project they are available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has used the + # resources but @ project they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Creating service offering with 4 CPU cores") + self.services["service_offering"]["cpunumber"] = 4 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=6, domain_limit=6, project_limit=6) + + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance with account: %s" % + self.child_do_admin.name) + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client_admin) + + self.debug("Deploying instance in project when CPU limit is reached in account") + + with self.assertRaises(Exception): + self.createInstance(project=self.project, + service_off=self.service_offering) + return diff --git a/test/integration/component/cpu_limits/test_project_limits.py b/test/integration/component/cpu_limits/test_project_limits.py new file mode 100644 index 00000000000..9bf3328e360 --- /dev/null +++ b/test/integration/component/cpu_limits/test_project_limits.py @@ -0,0 +1,347 @@ +# 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. + +""" Tests for cpu resource limits related to projects +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Domain, + Project + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + find_suitable_host, + get_resource_type + ) + +class Services: + """Test resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 4, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestProjectsCPULimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestProjectsCPULimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + + self.cleanup = [self.account, ] + + self.debug("Setting up account and domain hierarchy") + self.setupProjectAccounts() + + api_client = self.testClient.createUserApiClient( + UserName=self.admin.name, + DomainName=self.admin.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + self.vm = self.createInstance(project=self.project, + service_off=self.service_offering, api_client=api_client) + + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + pass + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, project, service_off, networks=None, api_client=None): + """Creates an instance in account""" + + if api_client is None: + api_client = self.api_client + + try: + self.vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=self.vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return self.vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupProjectAccounts(self): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + # Create project as a domain admin + self.project = Project.create(self.apiclient, + self.services["project"], + account=self.admin.name, + domainid=self.admin.domainid) + # Cleanup created project at end of test + self.cleanup.append(self.project) + self.cleanup.append(self.admin) + self.cleanup.append(self.domain) + self.debug("Created project with domain admin with name: %s" % + self.project.name) + + projects = Project.list(self.apiclient, id=self.project.id, + listall=True) + + self.assertEqual(isinstance(projects, list), True, + "Check for a valid list projects response") + project = projects[0] + self.assertEqual(project.name, self.project.name, + "Check project name from list response") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_project_counts_start_stop_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Stop VM of an accounts added to the project. + # 4. Resource count should list properly. + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.debug(project_list) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % self.vm.name) + try: + self.vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_stop = project_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % self.vm.name) + try: + self.vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_start = project_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_start, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_project_counts_migrate_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Migrate VM of an accounts added to the project to a new host + # 4. Resource count should list properly. + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, self.vm) + self.debug("Migrating instance: %s to host: %s" % + (self.vm.name, host.name)) + try: + self.vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_migrate = project_list[0].cputotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after migrating the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_project_counts_delete_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Destroy VM of an accounts added to the project to a new host + # 4. Resource count should list properly. + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].cputotal + + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % self.vm.name) + try: + self.vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_delete = project_list[0].cputotal + self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + return diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 215263772d1..782ad6b868a 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -3548,3 +3548,33 @@ class ApplicationLoadBalancer: cmd = listLoadBalancers.listLoadBalancersCmd() [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listLoadBalancerRules(cmd)) + +class Resources: + """Manage resource limits""" + + def __init__(self, items, services): + self.__dict__.update(items) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists resource limits""" + + cmd = listResourceLimits.listResourceLimitsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listResourceLimits(cmd)) + + @classmethod + def updateLimit(cls, apiclient, **kwargs): + """Updates resource limits""" + + cmd = updateResourceLimit.updateResourceLimitCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.updateResourceLimit(cmd)) + + @classmethod + def updateCount(cls, apiclient, **kwargs): + """Updates resource count""" + + cmd = updateResourceCount.updateResourceCountCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.updateResourceCount(cmd)) diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index 478aa318908..d71c749d35f 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -624,3 +624,60 @@ def list_vpc_offerings(apiclient, **kwargs): cmd = listVPCOfferings.listVPCOfferingsCmd() [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listVPCOfferings(cmd)) + +def update_resource_count(apiclient, domainid, accountid=None, + projectid=None, rtype=None): + """updates the resource count + 0 - VM + 1 - Public IP + 2 - Volume + 3 - Snapshot + 4 - Template + 5 - Projects + 6 - Network + 7 - VPC + 8 - CPUs + 9 - RAM + 10 - Primary (shared) storage (Volumes) + 11 - Secondary storage (Snapshots, Templates & ISOs) + """ + + Resources.updateCount(apiclient, + domainid=domainid, + account=accountid if accountid else None, + projectid=projectid if projectid else None, + resourcetype=rtype if rtype else None + ) + return + +def find_suitable_host(apiclient, vm): + """Returns a suitable host for VM migration""" + + hosts = Host.list(apiclient, + virtualmachineid=vm.id, + listall=True) + + if isinstance(hosts, list): + assert len(hosts) > 0, "List host should return valid response" + else: + raise Exception("Exception: List host should return valid response") + return hosts[0] + +def get_resource_type(resource_id): + """Returns resource type""" + + lookup = { 0: "VM", + 1: "Public IP", + 2: "Volume", + 3: "Snapshot", + 4: "Template", + 5: "Projects", + 6: "Network", + 7: "VPC", + 8: "CPUs", + 9: "RAM", + 10: "Primary (shared) storage (Volumes)", + 11: "Secondary storage (Snapshots, Templates & ISOs)" + } + + return lookup[resource_id] From fb9521c92fa195eba03b125ae05be946b750b3ec Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Thu, 29 Aug 2013 14:26:12 +0530 Subject: [PATCH 072/251] Formatting and tab errors in the new test suites for limits Signed-off-by: Prasanna Santhanam (cherry picked from commit f5eb81e669a8a454fc02b90a44c4ce8ffc7ac9ac) --- .../component/cpu_limits/test_cpu_limits.py | 384 +++++++------- .../cpu_limits/test_domain_limits.py | 483 +++++++++--------- .../cpu_limits/test_maximum_limits.py | 229 +++++---- .../cpu_limits/test_project_limits.py | 48 +- 4 files changed, 574 insertions(+), 570 deletions(-) diff --git a/test/integration/component/cpu_limits/test_cpu_limits.py b/test/integration/component/cpu_limits/test_cpu_limits.py index 96be62b12d1..8acf8b7036a 100644 --- a/test/integration/component/cpu_limits/test_cpu_limits.py +++ b/test/integration/component/cpu_limits/test_cpu_limits.py @@ -149,31 +149,31 @@ class TestCPULimits(cloudstackTestCase): return def createInstance(self, service_off, networks=None, api_client=None): - """Creates an instance in account""" + """Creates an instance in account + """ + if api_client is None: + api_client = self.apiclient - if api_client is None: - api_client = self.apiclient - - self.debug("Deploying an instance in account: %s" % - self.account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=self.account.name, - domainid=self.account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) @attr(tags=["advanced", "advancedns","simulator"]) def test_01_multiplecore_start_stop_instance(self): @@ -396,66 +396,66 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): return def createInstance(self, service_off, networks=None, api_client=None): - """Creates an instance in account""" + """Creates an instance in account + """ + if api_client is None: + api_client = self.apiclient - if api_client is None: - api_client = self.apiclient - - self.debug("Deploying an instance in account: %s" % - self.account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=self.account.name, - domainid=self.account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): self.debug("Creating a sub-domain under: %s" % self.domain.name) - self.child_domain_1 = Domain.create( - self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id - ) - self.child_do_admin_1 = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.child_domain_1.id - ) - # Cleanup the resources created at end of test - self.cleanup.append(self.child_do_admin_1) - self.cleanup.append(self.child_domain_1) + self.child_domain_1 = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + self.child_do_admin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_1.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_1) + self.cleanup.append(self.child_domain_1) - self.child_domain_2 = Domain.create( - self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id - ) + self.child_domain_2 = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) - self.child_do_admin_2 = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.child_domain_2.id - ) + self.child_do_admin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_2.id + ) - # Cleanup the resources created at end of test - self.cleanup.append(self.child_do_admin_2) - self.cleanup.append(self.child_domain_2) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_2) + self.cleanup.append(self.child_domain_2) return @@ -478,57 +478,57 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) - self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) - vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should match with the expected resource count") + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") - self.debug("Stopping instance: %s" % vm.name) - try: - vm.stop(self.apiclient) - except Exception as e: - self.fail("Failed to stop instance: %s" % e) + self.debug("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count_after_stop = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].cputotal - self.assertEqual(resource_count, resource_count_after_stop, - "Resource count should be same after stopping the instance") + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") - self.debug("Starting instance: %s" % vm.name) - try: - vm.start(self.apiclient) - except Exception as e: - self.fail("Failed to start instance: %s" % e) + self.debug("Starting instance: %s" % vm.name) + try: + vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count_after_start = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].cputotal - self.assertEqual(resource_count_after_stop, resource_count_after_start, - "Resource count should be same after starting the instance") + self.assertEqual(resource_count_after_stop, resource_count_after_start, + "Resource count should be same after starting the instance") return @attr(tags=["advanced", "advancedns","simulator"]) @@ -550,43 +550,43 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) - self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) - vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should with the expected resource count") + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") - host = find_suitable_host(self.apiclient, vm) - self.debug("Migrating instance: %s to host: %s" % - (vm.name, host.name)) - try: - vm.migrate(self.apiclient, host.id) - except Exception as e: - self.fail("Failed to migrate instance: %s" % e) + host = find_suitable_host(self.apiclient, vm) + self.debug("Migrating instance: %s to host: %s" % + (vm.name, host.name)) + try: + vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count_after_migrate = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].cputotal - self.assertEqual(resource_count, resource_count_after_migrate, - "Resource count should be same after starting the instance") + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after starting the instance") return @attr(tags=["advanced", "advancedns","simulator"]) @@ -608,47 +608,46 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) - self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) - vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count = account_list[0].cputotal + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal - expected_resource_count = int(self.services["service_offering"]["cpunumber"]) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should match with the expected resource count") + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") - self.debug("Destroying instance: %s" % vm.name) - try: - vm.delete(self.apiclient) - except Exception as e: - self.fail("Failed to delete instance: %s" % e) + self.debug("Destroying instance: %s" % vm.name) + try: + vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) - account_list = Account.list(self.apiclient, id=self.account.id) - self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) - resource_count = account_list[0].cputotal - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].cputotal + self.assertEqual(resource_count, 0, "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU return @attr(tags=["advanced", "advancedns","simulator"]) @attr(configuration='max.account.cpus') def test_04_deploy_multiple_vm_with_multiple_cpus(self): """Test Deploy multiple VM with 4 core CPU & verify the usage""" - #keep the configuration value - max.account.cpus number = 16 - + #keep the configuration value - max.account.cpus number = 16 # Validate the following # 1. Create compute offering with 4 core CPU # 2. Deploy multiple VMs with this service offering @@ -681,12 +680,12 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): if cpu_account_gc[0].max != 16: self.skipTest("This test case requires configuration value max.account.cpus to be 16") - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) self.createInstance(service_off=self.service_offering, api_client=api_client) @@ -698,15 +697,15 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count = account_list[0].cputotal expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 vms self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should with the expected resource count") + "Initial resource count should with the expected resource count") self.debug("Destroying instance: %s" % vm_1.name) try: @@ -716,15 +715,15 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_delete = account_list[0].cputotal expected_resource_count -= int(self.services["service_offering"]["cpunumber"]) self.assertEqual(resource_count_after_delete, expected_resource_count, - "Resource count should be less than before after deleting the instance") + "Resource count should be less than before after deleting the instance") host = find_suitable_host(self.apiclient, vm_2) self.debug("Migrating instance: %s to host: %s" % (vm_2.name, @@ -736,12 +735,11 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_migrate = account_list[0].cputotal self.debug(resource_count_after_migrate) self.assertEqual(resource_count_after_delete, resource_count_after_migrate, - "Resource count should be same after migrating the instance") - return + "Resource count should be same after migrating the instance") diff --git a/test/integration/component/cpu_limits/test_domain_limits.py b/test/integration/component/cpu_limits/test_domain_limits.py index 057c6b9a0b3..2668204361f 100644 --- a/test/integration/component/cpu_limits/test_domain_limits.py +++ b/test/integration/component/cpu_limits/test_domain_limits.py @@ -139,80 +139,81 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): def createInstance(self, service_off, networks=None, api_client=None): """Creates an instance in account""" - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % - self.account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=self.account.name, - domainid=self.account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying an instance in account: %s" % + self.account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): self.debug("Creating a sub-domain under: %s" % self.domain.name) - self.child_domain = Domain.create( - self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id - ) - self.child_do_admin = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.child_domain.id - ) - # Cleanup the resources created at end of test - self.cleanup.append(self.child_do_admin) - self.cleanup.append(self.child_domain) - Resources.updateLimit( - self.apiclient, - resourcetype=8, - max=16, - account=self.child_do_admin.name, - domainid=self.child_do_admin.domainid - ) + self.child_domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) + self.child_do_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) - self.domain = Domain.create( - self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id - ) + Resources.updateLimit( + self.apiclient, + resourcetype=8, + max=16, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid + ) - self.admin = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.domain.id - ) + self.domain = Domain.create( + self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id + ) - # Cleanup the resources created at end of test - self.cleanup.append(self.admin) - self.cleanup.append(self.domain) + self.admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) - Resources.updateLimit( - self.apiclient, - resourcetype=8, - max=16, - account=self.admin.name, - domainid=self.admin.domainid - ) + # Cleanup the resources created at end of test + self.cleanup.append(self.admin) + self.cleanup.append(self.domain) + + Resources.updateLimit( + self.apiclient, + resourcetype=8, + max=16, + account=self.admin.name, + domainid=self.admin.domainid + ) return @attr(tags=["advanced", "advancedns","simulator"]) @@ -235,25 +236,25 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm = self.createInstance(service_off=self.service_offering, api_client=api_client) account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count = account_list[0].cputotal expected_resource_count = int(self.services["service_offering"]["cpunumber"]) self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should match with the expected resource count") + "Initial resource count should match with the expected resource count") self.debug("Stopping instance: %s" % vm.name) try: @@ -263,13 +264,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_stop = account_list[0].cputotal self.assertEqual(resource_count, resource_count_after_stop, - "Resource count should be same as before, after stopping the instance") + "Resource count should be same as before, after stopping the instance") self.debug("Starting instance: %s" % vm.name) try: @@ -279,13 +280,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_start = account_list[0].cputotal self.assertEqual(resource_count_after_stop, resource_count_after_start, - "Resource count should be same as before, after starting the instance") + "Resource count should be same as before, after starting the instance") return @attr(tags=["advanced", "advancedns","simulator"]) @@ -308,29 +309,29 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm = self.createInstance(service_off=self.service_offering, api_client=api_client) account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count = account_list[0].cputotal expected_resource_count = int(self.services["service_offering"]["cpunumber"]) self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should match with the expected resource count") + "Initial resource count should match with the expected resource count") host = find_suitable_host(self.apiclient, vm) self.debug("Migrating instance: %s to host: %s" % - (vm.name, host.name)) + (vm.name, host.name)) try: vm.migrate(self.apiclient, host.id) except Exception as e: @@ -338,13 +339,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_migrate = account_list[0].cputotal self.assertEqual(resource_count, resource_count_after_migrate, - "Resource count should be same as before, after migrating the instance") + "Resource count should be same as before, after migrating the instance") return @attr(tags=["advanced", "advancedns","simulator"]) @@ -367,25 +368,25 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm = self.createInstance(service_off=self.service_offering, api_client=api_client) account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count = account_list[0].cputotal expected_resource_count = int(self.services["service_offering"]["cpunumber"]) self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should with the expected resource count") + "Initial resource count should with the expected resource count") self.debug("Destroying instance: %s" % vm.name) try: @@ -395,12 +396,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_delete = account_list[0].cputotal - self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + self.assertEqual(resource_count_after_delete, 0, + "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU return @attr(tags=["advanced", "advancedns","simulator"]) @@ -430,12 +432,12 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.account = admin self.domain = domain - api_client = self.testClient.createUserApiClient( - UserName=self.account.name, - DomainName=self.account.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) self.createInstance(service_off=self.service_offering, api_client=api_client) @@ -447,15 +449,15 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count = account_list[0].cputotal expected_resource_count = int(self.services["service_offering"]["cpunumber"]) * 4 #Total 4 VMs self.assertEqual(resource_count, expected_resource_count, - "Initial resource count should be 4") + "Initial resource count should be 4") self.debug("Destroying instance: %s" % vm_1.name) try: @@ -465,15 +467,15 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_delete = account_list[0].cputotal expected_resource_count -= int(self.services["service_offering"]["cpunumber"]) self.assertEqual(resource_count_after_delete, expected_resource_count, - "Resource count should match with the expected count") + "Resource count should match with the expected count") host = find_suitable_host(self.apiclient, vm_2) self.debug("Migrating instance: %s to host: %s" % (vm_2.name, @@ -485,13 +487,13 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.account.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_after_migrate = account_list[0].cputotal self.assertEqual(resource_count_after_migrate, resource_count_after_delete, - "Resource count should not change after migrating the instance") + "Resource count should not change after migrating the instance") return class TestMultipleChildDomains(cloudstackTestCase): @@ -542,116 +544,117 @@ class TestMultipleChildDomains(cloudstackTestCase): def createInstance(self, account, service_off, networks=None, api_client=None): """Creates an instance in account""" - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % - account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=account.name, - domainid=account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying an instance in account: %s" % + account.name) + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): self.debug("Creating a domain under: %s" % self.domain.name) - self.parent_domain = Domain.create(self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id) - self.parentd_admin = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.domain.id - ) - self.debug("Updating the Memory resource limit for domain: %s" % - self.domain.name) - Resources.updateLimit(self.apiclient, - resourcetype=8, - max=10, - domainid=self.parentd_admin.domainid, - account=self.parentd_admin.name) - self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) - self.cdomain_1 = Domain.create(self.apiclient, - services=self.services["domain"], - parentdomainid=self.parent_domain.id) + self.parent_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.parentd_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) - self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) - self.cdomain_2 = Domain.create(self.apiclient, - services=self.services["domain"], - parentdomainid=self.parent_domain.id) + self.debug("Updating the Memory resource limit for domain: %s" % + self.domain.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=10, + domainid=self.parentd_admin.domainid, + account=self.parentd_admin.name) + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) - self.cadmin_1 = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.cdomain_1.id - ) + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) - self.debug("Updating the Memory resource count for domain: %s" % - self.cdomain_1.name) - Resources.updateLimit(self.apiclient, - resourcetype=8, - max=4, - domainid=self.cadmin_1.domainid) - - self.debug("Updating the Memory resource count for account: %s" % - self.cadmin_1.name) - Resources.updateLimit(self.apiclient, - resourcetype=8, - max=2, - account=self.cadmin_1.name, - domainid=self.cadmin_1.domainid) - - self.cadmin_2 = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.cdomain_2.id - ) + self.cadmin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_1.id + ) self.debug("Updating the Memory resource count for domain: %s" % - self.cdomain_2.name) - Resources.updateLimit(self.apiclient, - resourcetype=8, - max=5, - domainid=self.cadmin_2.domainid) + self.cdomain_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=4, + domainid=self.cadmin_1.domainid) self.debug("Updating the Memory resource count for account: %s" % - self.cadmin_2.name) - Resources.updateLimit(self.apiclient, - resourcetype=8, - max=3, - account=self.cadmin_2.name, - domainid=self.cadmin_2.domainid) - # Cleanup the resources created at end of test - self.cleanup.append(self.cadmin_1) - self.cleanup.append(self.cadmin_2) - self.cleanup.append(self.cdomain_1) - self.cleanup.append(self.cdomain_2) - self.cleanup.append(self.parentd_admin) - self.cleanup.append(self.parent_domain) + self.cadmin_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=2, + account=self.cadmin_1.name, + domainid=self.cadmin_1.domainid) - users = { - self.parent_domain: self.parentd_admin, + self.cadmin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_2.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cdomain_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=5, + domainid=self.cadmin_2.domainid) + + self.debug("Updating the Memory resource count for account: %s" % + self.cadmin_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=3, + account=self.cadmin_2.name, + domainid=self.cadmin_2.domainid) + # Cleanup the resources created at end of test + self.cleanup.append(self.cadmin_1) + self.cleanup.append(self.cadmin_2) + self.cleanup.append(self.cdomain_1) + self.cleanup.append(self.cdomain_2) + self.cleanup.append(self.parentd_admin) + self.cleanup.append(self.parent_domain) + + users = { + self.parent_domain: self.parentd_admin, self.cdomain_1: self.cadmin_1, self.cdomain_2: self.cadmin_2 - } + } return users @attr(tags=["advanced", "advancedns","simulator"]) @@ -681,29 +684,29 @@ class TestMultipleChildDomains(cloudstackTestCase): self.debug("Setting up account and domain hierarchy") self.setupAccounts() - api_client_cadmin_1 = self.testClient.createUserApiClient( - UserName=self.cadmin_1.name, - DomainName=self.cadmin_1.domain) + api_client_cadmin_1 = self.testClient.createUserApiClient( + UserName=self.cadmin_1.name, + DomainName=self.cadmin_1.domain) - api_client_cadmin_2 = self.testClient.createUserApiClient( - UserName=self.cadmin_2.name, - DomainName=self.cadmin_2.domain) + api_client_cadmin_2 = self.testClient.createUserApiClient( + UserName=self.cadmin_2.name, + DomainName=self.cadmin_2.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) vm_1 = self.createInstance(account=self.cadmin_1, - service_off=self.service_offering, api_client=api_client_cadmin_1) + service_off=self.service_offering, api_client=api_client_cadmin_1) vm_2 = self.createInstance(account=self.cadmin_2, - service_off=self.service_offering, api_client=api_client_cadmin_2) + service_off=self.service_offering, api_client=api_client_cadmin_2) self.debug("Checking resource count for account: %s" % self.cadmin_1.name) account_list = Account.list(self.apiclient, id=self.cadmin_1.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_cadmin_1 = account_list[0].cputotal self.debug(resource_count_cadmin_1) @@ -711,9 +714,9 @@ class TestMultipleChildDomains(cloudstackTestCase): self.debug("Checking resource count for account: %s" % self.cadmin_2.name) account_list = Account.list(self.apiclient, id=self.cadmin_2.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_cadmin_2 = account_list[0].cputotal self.debug(resource_count_cadmin_2) @@ -722,13 +725,13 @@ class TestMultipleChildDomains(cloudstackTestCase): "Creating instance when CPU limit is fully used in child domain 1") with self.assertRaises(Exception): self.createInstance(account=self.cadmin_1, - service_off=self.service_offering, api_client=api_client_cadmin_1) + service_off=self.service_offering, api_client=api_client_cadmin_1) self.debug( "Creating instance when CPU limit is fully used in child domain 2") with self.assertRaises(Exception): self.createInstance(account=self.cadmin_2, - service_off=self.service_offering, api_client=api_client_cadmin_2) + service_off=self.service_offering, api_client=api_client_cadmin_2) self.debug("Destroying instances: %s, %s" % (vm_1.name, vm_2.name)) try: vm_1.delete(self.apiclient) @@ -740,22 +743,22 @@ class TestMultipleChildDomains(cloudstackTestCase): account_list = Account.list(self.apiclient, id=self.cadmin_1.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_cadmin_1 = account_list[0].cputotal self.debug(resource_count_cadmin_1) - self.assertEqual(resource_count_cadmin_1, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + self.assertEqual(resource_count_cadmin_1, 0, "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU self.debug("Checking resource count for account: %s" % self.cadmin_2.name) account_list = Account.list(self.apiclient, id=self.cadmin_2.id) self.assertIsInstance(account_list, - list, - "List Accounts should return a valid response" - ) + list, + "List Accounts should return a valid response" + ) resource_count_cadmin_2 = account_list[0].cputotal self.debug(resource_count_cadmin_2) - self.assertEqual(resource_count_cadmin_2, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + self.assertEqual(resource_count_cadmin_2, 0, "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU return diff --git a/test/integration/component/cpu_limits/test_maximum_limits.py b/test/integration/component/cpu_limits/test_maximum_limits.py index dec84139645..23025044777 100644 --- a/test/integration/component/cpu_limits/test_maximum_limits.py +++ b/test/integration/component/cpu_limits/test_maximum_limits.py @@ -134,97 +134,96 @@ class TestMaxCPULimits(cloudstackTestCase): project=None, networks=None, api_client=None): """Creates an instance in account""" - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient - self.debug("Deploying instance") - try: - if account: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=account.name, - domainid=account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - elif project: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - projectid=project.id, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying instance") + try: + if account: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + elif project: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self, account_limit=2, domain_limit=2, project_limit=2): - self.debug("Creating a domain under: %s" % self.domain.name) - self.child_domain = Domain.create(self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id) + self.debug("Creating a domain under: %s" % self.domain.name) + self.child_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.debug("domain crated with domain id %s" % self.child_domain.id) - self.debug("domain crated with domain id %s" % self.child_domain.id) + self.child_do_admin = Account.create(self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain.id) - self.child_do_admin = Account.create(self.apiclient, - self.services["account"], - admin=True, - domainid=self.child_domain.id) + self.debug("domain admin created for domain id %s" % + self.child_do_admin.domainid) - self.debug("domain admin created for domain id %s" % - self.child_do_admin.domainid) + # Create project as a domain admin + self.project = Project.create(self.apiclient, + self.services["project"], + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + # Cleanup created project at end of test + self.cleanup.append(self.project) - # Create project as a domain admin - self.project = Project.create(self.apiclient, - self.services["project"], - account=self.child_do_admin.name, - domainid=self.child_do_admin.domainid) - # Cleanup created project at end of test - self.cleanup.append(self.project) + # Cleanup accounts created + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) - # Cleanup accounts created - self.cleanup.append(self.child_do_admin) - self.cleanup.append(self.child_domain) + self.debug("Updating the CPU resource count for domain: %s" % + self.child_domain.name) + # Update resource limits for account 1 + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=account_limit, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) - self.debug("Updating the CPU resource count for domain: %s" % - self.child_domain.name) - # Update resource limits for account 1 - responses = Resources.updateLimit(self.apiclient, - resourcetype=8, - max=account_limit, - account=self.child_do_admin.name, - domainid=self.child_do_admin.domainid) + self.debug("CPU Resource count for child domain admin account is now: %s" % + responses.max) - self.debug("CPU Resource count for child domain admin account is now: %s" % - responses.max) + self.debug("Updating the CPU limit for project") + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=project_limit, + projectid=self.project.id) - self.debug("Updating the CPU limit for project") - responses = Resources.updateLimit(self.apiclient, - resourcetype=8, - max=project_limit, - projectid=self.project.id) + self.debug("CPU Resource count for project is now") + self.debug(responses.max) - self.debug("CPU Resource count for project is now") - self.debug(responses.max) + self.debug("Updating the CPU limit for domain only") + responses = Resources.updateLimit(self.apiclient, + resourcetype=8, + max=domain_limit, + domainid=self.child_domain.id) - self.debug("Updating the CPU limit for domain only") - responses = Resources.updateLimit(self.apiclient, - resourcetype=8, - max=domain_limit, - domainid=self.child_domain.id) - - self.debug("CPU Resource count for domain %s with id %s is now %s" % - (responses.domain, responses.domainid, responses.max)) + self.debug("CPU Resource count for domain %s with id %s is now %s" % + (responses.domain, responses.domainid, responses.max)) return @@ -240,24 +239,25 @@ class TestMaxCPULimits(cloudstackTestCase): # with "resource limit exceeds" self.debug("Creating service offering with 3 CPU cores") - self.services["service_offering"]["cpunumber"] = 3 + + self.services["service_offering"]["cpunumber"] = 3 self.service_offering = ServiceOffering.create( - self.apiclient, - self.services["service_offering"] - ) + self.apiclient, + self.services["service_offering"] + ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) self.debug("Setting up account and domain hierarchy") self.setupAccounts(account_limit=4, domain_limit=2) - api_client_admin = self.testClient.createUserApiClient( - UserName=self.child_do_admin.name, - DomainName=self.child_do_admin.domain) + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) with self.assertRaises(Exception): self.createInstance(account=self.child_do_admin, - service_off=self.service_offering, api_client=api_client_admin) + service_off=self.service_offering, api_client=api_client_admin) return @attr(tags=["advanced", "advancedns","simulator"]) @@ -272,32 +272,33 @@ class TestMaxCPULimits(cloudstackTestCase): # with "resource limit exceeds" self.debug("Creating service offering with 4 CPU cores") - self.services["service_offering"]["cpunumber"] = 4 + + self.services["service_offering"]["cpunumber"] = 4 self.service_offering = ServiceOffering.create( - self.apiclient, - self.services["service_offering"] - ) + self.apiclient, + self.services["service_offering"] + ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) self.debug("Setting up account and domain hierarchy") self.setupAccounts(account_limit=6, domain_limit=8) - api_client_admin = self.testClient.createUserApiClient( - UserName=self.child_do_admin.name, - DomainName=self.child_do_admin.domain) + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) - self.debug("Deploying instance with account: %s" % - self.child_do_admin.name) + self.debug("Deploying instance with account: %s" % + self.child_do_admin.name) self.createInstance(account=self.child_do_admin, - service_off=self.service_offering, api_client=api_client_admin) + service_off=self.service_offering, api_client=api_client_admin) self.debug("Deploying instance when CPU limit is reached in account") with self.assertRaises(Exception): self.createInstance(account=self.chid_do_admin, - service_off=self.service_offering, api_client=api_client_admin) + service_off=self.service_offering, api_client=api_client_admin) return @attr(tags=["advanced", "advancedns","simulator"]) @@ -312,26 +313,27 @@ class TestMaxCPULimits(cloudstackTestCase): # with "resource limit exceeds" self.debug("Creating service offering with 3 CPU cores") - self.services["service_offering"]["cpunumber"] = 3 + + self.services["service_offering"]["cpunumber"] = 3 self.service_offering = ServiceOffering.create( - self.apiclient, - self.services["service_offering"] - ) + self.apiclient, + self.services["service_offering"] + ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) self.debug("Setting up account and domain hierarchy") self.setupAccounts(account_limit=4, domain_limit=4, project_limit=2) - api_client_admin = self.testClient.createUserApiClient( - UserName=self.child_do_admin.name, - DomainName=self.child_do_admin.domain) + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) self.debug("Deploying instance in account 2 when CPU limit is reached") with self.assertRaises(Exception): self.createInstance(project=self.project, - service_off=self.service_offering, api_client=api_client_admin) + service_off=self.service_offering, api_client=api_client_admin) return @attr(tags=["advanced", "advancedns","simulator"]) @@ -346,29 +348,30 @@ class TestMaxCPULimits(cloudstackTestCase): # with "resource limit exceeds" self.debug("Creating service offering with 4 CPU cores") - self.services["service_offering"]["cpunumber"] = 4 + + self.services["service_offering"]["cpunumber"] = 4 self.service_offering = ServiceOffering.create( - self.apiclient, - self.services["service_offering"] - ) + self.apiclient, + self.services["service_offering"] + ) # Adding to cleanup list after execution self.cleanup.append(self.service_offering) self.debug("Setting up account and domain hierarchy") self.setupAccounts(account_limit=6, domain_limit=6, project_limit=6) - api_client_admin = self.testClient.createUserApiClient( - UserName=self.child_do_admin.name, - DomainName=self.child_do_admin.domain) + api_client_admin = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) self.debug("Deploying instance with account: %s" % - self.child_do_admin.name) + self.child_do_admin.name) self.createInstance(account=self.child_do_admin, - service_off=self.service_offering, api_client=api_client_admin) + service_off=self.service_offering, api_client=api_client_admin) self.debug("Deploying instance in project when CPU limit is reached in account") with self.assertRaises(Exception): self.createInstance(project=self.project, - service_off=self.service_offering) + service_off=self.service_offering) return diff --git a/test/integration/component/cpu_limits/test_project_limits.py b/test/integration/component/cpu_limits/test_project_limits.py index 9bf3328e360..3c432db7db7 100644 --- a/test/integration/component/cpu_limits/test_project_limits.py +++ b/test/integration/component/cpu_limits/test_project_limits.py @@ -136,14 +136,14 @@ class TestProjectsCPULimits(cloudstackTestCase): self.debug("Setting up account and domain hierarchy") self.setupProjectAccounts() - api_client = self.testClient.createUserApiClient( - UserName=self.admin.name, - DomainName=self.admin.domain) + api_client = self.testClient.createUserApiClient( + UserName=self.admin.name, + DomainName=self.admin.domain) self.debug("Creating an instance with service offering: %s" % - self.service_offering.name) + self.service_offering.name) self.vm = self.createInstance(project=self.project, - service_off=self.service_offering, api_client=api_client) + service_off=self.service_offering, api_client=api_client) return @@ -159,26 +159,26 @@ class TestProjectsCPULimits(cloudstackTestCase): def createInstance(self, project, service_off, networks=None, api_client=None): """Creates an instance in account""" - if api_client is None: - api_client = self.api_client + if api_client is None: + api_client = self.api_client - try: - self.vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - projectid=project.id, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=self.vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return self.vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + try: + self.vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=self.vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return self.vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupProjectAccounts(self): From 6022d69e21137f854cad9d8a69f48289986ff3b0 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Thu, 29 Aug 2013 14:30:00 +0530 Subject: [PATCH 073/251] cpu_limits module of tests (cherry picked from commit a6117192f8e6d74227af70af29e0eee86868230f) --- .../integration/component/cpu_limits/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/integration/component/cpu_limits/__init__.py diff --git a/test/integration/component/cpu_limits/__init__.py b/test/integration/component/cpu_limits/__init__.py new file mode 100644 index 00000000000..d216be4ddc9 --- /dev/null +++ b/test/integration/component/cpu_limits/__init__.py @@ -0,0 +1,16 @@ +# 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. \ No newline at end of file From a3ac7d957fe942cd31340f870ecdd25461d692c1 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 29 Aug 2013 14:29:15 +0530 Subject: [PATCH 074/251] Before 4.X TO 4.2 upgrade manually clean up /var/cache/cloudstack --- docs/en-US/Release_Notes.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 8fde51ca251..f3f160227af 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -21,7 +21,7 @@ under the License. - + Welcome to &PRODUCT; 4.2 Welcome to the 4.2.0 release of &PRODUCT;, the second major release from the Apache CloudStack project since its graduation from the Apache Incubator. &PRODUCT; 4.2 includes more @@ -574,6 +574,8 @@ under the License. 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.2.0 source, or check the Apache CloudStack downloads page at - Copy the CloudPlatform 4.2 tar file to the host, untar it, and change directory - to the resulting directory. + Manually clean up /var/cache/cloudstack. + + + Copy the 4.2 tar file to the host, untar it, and change directory to the + resulting directory. Stop the running agent. @@ -1263,10 +1268,7 @@ service cloudstack-agent start # apt-get update # apt-get upgrade cloud-* - - Start the agent. - # service cloudstack-agent start - + Edit /etc/cloudstack/agent/agent.properties to change the resource parameter from @@ -1275,6 +1277,7 @@ service cloudstack-agent start Start the cloud agent and cloud management services. + # service cloudstack-agent start When the Management Server is up and running, log in to the CloudStack UI and @@ -1290,7 +1293,6 @@ service cloudstack-agent start 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. From 68b668ae8f126e339de971c99e4c29c7f86b3d6e Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 29 Aug 2013 11:32:34 +0200 Subject: [PATCH 075/251] CLOUDSTACK-4538: fix small typo --- engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index a2c6b4e126a..f0b459a4a77 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -895,7 +895,7 @@ public class Upgrade410to420 implements DbUpgrade { rsmem_global.close(); } if (rscpu_global != null) { - rsmem_global.close(); + rscpu_global.close(); } if (pstmt != null) { pstmt.close(); From 61ebfad4493d4f3fe4ced948985e69202080654b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 29 Aug 2013 12:41:18 +0200 Subject: [PATCH 076/251] CLOUDSTACK-4538: set default value of cpu.overprovisioning.factor and mem.overprovisioning.factor to 1 On a fresh environment, some values in cloud.configuration table are persisted in com.cloud.server.ConfigurationServerImpl.persistDefaultValues() A default value need to be set before com.cloud.upgrade.DatabaseUpgradeChecker (cherry picked from commit 1a67750cb66cf74175e2776ba3f3ba76fde0d1bb) --- .../src/com/cloud/upgrade/dao/Upgrade410to420.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index f0b459a4a77..2e58dddd0d3 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -856,12 +856,14 @@ public class Upgrade410to420 implements DbUpgrade { pstmt3=conn.prepareStatement("select value from `cloud`.`configuration` where name=?"); pstmt3.setString(1,"cpu.overprovisioning.factor"); rscpu_global = pstmt3.executeQuery(); - rscpu_global.next(); - String global_cpu_overprovisioning_factor=rscpu_global.getString(1); + String global_cpu_overprovisioning_factor = "1"; + if (rscpu_global.next()) + global_cpu_overprovisioning_factor = rscpu_global.getString(1); pstmt3.setString(1,"mem.overprovisioning.factor"); rsmem_global = pstmt3.executeQuery(); - rsmem_global.next(); - String global_mem_overprovisioning_factor = rsmem_global.getString(1); + String global_mem_overprovisioning_factor = "1"; + if (rsmem_global.next()) + global_mem_overprovisioning_factor = rsmem_global.getString(1); rs1 = pstmt.executeQuery(); while (rs1.next()) { From eaea8f8c5f21257f036d4c13ba1ec50c69664a52 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 29 Aug 2013 14:52:20 +0530 Subject: [PATCH 077/251] CLOUDSTACK-4435 [VMWARE]System VM's are failed to start with Nexus enabled Zone Since introducing pool of session contexts we no more have a dedicated context for each VMware hypervisor host. Hence vsm credentials stored in session context cannot be retrieved always correctly. Fix is to register the vsm credentials after fetching context and the context gets recycled after use. Signed-off-by: Sateesh Chodapuneedi --- .../vmware/resource/VmwareResource.java | 15 +++++---------- .../vmware/mo/HypervisorHostHelper.java | 7 ++++++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 7ff4918e36e..16b829ae5ab 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -46,7 +46,6 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.configuration.Config; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; @@ -202,6 +201,7 @@ import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.configuration.Config; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.Vlan; import com.cloud.exception.InternalErrorException; @@ -219,7 +219,6 @@ import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.FeatureKeyConstants; import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO; -import com.cloud.hypervisor.vmware.mo.HostFirewallSystemMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; @@ -362,6 +361,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected String _privateNetworkVSwitchName; protected VmwareTrafficLabel _guestTrafficInfo = new VmwareTrafficLabel(TrafficType.Guest); protected VmwareTrafficLabel _publicTrafficInfo = new VmwareTrafficLabel(TrafficType.Public); + protected Map _vsmCredentials = null; protected int _portsPerDvPortGroup; protected boolean _fullCloneFlag = false; protected boolean _instanceNameFlag = false; @@ -1956,7 +1956,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } else { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", - vmMo.getRunningHost(), vlanId, null, null, null, _ops_timeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan); + vmMo.getRunningHost(), vlanId, null, null, null, _ops_timeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, _vsmCredentials); } int nicIndex = allocPublicNicIndex(vmMo); @@ -3525,7 +3525,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } networkInfo = HypervisorHostHelper.prepareNetwork(switchName.first(), namePrefix, hostMo, vlanId, svlanId, nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _ops_timeout, switchType, - _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType()); + _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType(), _vsmCredentials); } return networkInfo; @@ -6660,14 +6660,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VmwareHypervisorHost hostMo = this.getHyperHost(context); _hostName = hostMo.getHyperHostName(); - Map vsmCredentials; if (_guestTrafficInfo.getVirtualSwitchType() == VirtualSwitchType.NexusDistributedVirtualSwitch || _publicTrafficInfo.getVirtualSwitchType() == VirtualSwitchType.NexusDistributedVirtualSwitch) { - vsmCredentials = mgr.getNexusVSMCredentialsByClusterId(Long.parseLong(_cluster)); - if (vsmCredentials != null) { - s_logger.info("Stocking credentials while configuring resource."); - context.registerStockObject("vsmcredentials", vsmCredentials); - } + _vsmCredentials = mgr.getNexusVSMCredentialsByClusterId(Long.parseLong(_cluster)); _privateNetworkVSwitchName = mgr.getPrivateVSwitchName(Long.parseLong(_dcId), HypervisorType.VMware); } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index 43c59fcafa5..014a9f8ba47 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -444,7 +444,8 @@ public class HypervisorHostHelper { public static Pair prepareNetwork(String physicalNetwork, String namePrefix, HostMO hostMo, String vlanId, String secondaryvlanId, Integer networkRateMbps, Integer networkRateMulticastMbps, long timeOutMs, - VirtualSwitchType vSwitchType, int numPorts, String gateway, boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType) throws Exception { + VirtualSwitchType vSwitchType, int numPorts, String gateway, boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType, + Map vsmCredentials) throws Exception { ManagedObjectReference morNetwork = null; VmwareContext context = hostMo.getContext(); ManagedObjectReference dcMor = hostMo.getHyperHostDatacenter(); @@ -577,6 +578,10 @@ public class HypervisorHostHelper { // TODO(sateesh): Optionally let user specify the burst coefficient long burstSize = 5 * averageBandwidth / 8; + if (vsmCredentials != null) { + s_logger.info("Stocking credentials of Nexus VSM"); + context.registerStockObject("vsmcredentials", vsmCredentials); + } if (!dataCenterMo.hasDvPortGroup(networkName)) { s_logger.info("Port profile " + networkName + " not found."); createPortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize, gateway, configureVServiceInNexus); From db2d9703c16aac0aa0ebcadaa75022981fbfe6cc Mon Sep 17 00:00:00 2001 From: frank Date: Thu, 29 Aug 2013 10:29:17 -0700 Subject: [PATCH 078/251] [Upgrade from 3.0.6 to 4.2][vmware] After upgrade the system vms fail to come up because "Secondary storage mount point" is pointing to a wrong location when secondary storage is mounted as read-only, changing permission of files on it will fail. But we should still stick to current mount point instread of returning a wrong mount point /mnt/sec --- .../com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 0c4bd146faf..a1671c9fa90 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -767,7 +767,6 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw result = script.execute(); if (result != null) { s_logger.warn("Unable to set permissions for " + mountPoint + " due to " + result); - return null; } return mountPoint; } From 1a333f369b33c8d46d6a034daac311e284bc8e49 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 29 Aug 2013 10:33:47 -0700 Subject: [PATCH 079/251] CLOUDSTACK-4559: fix devcloud --- .../vm/hypervisor/xenserver/xcposs/vmopsSnapshot | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot index 31f26ad3c3e..53f31a99eed 100644 --- a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot +++ b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot @@ -372,8 +372,16 @@ def unmountSnapshotsDir(session, args): return "1" -def getPrimarySRPath(primaryStorageSRUuid, isISCSI): - if isISCSI: +def getPrimarySRPath(session, primaryStorageSRUuid, isISCSI): + sr = session.xenapi.SR.get_by_uuid(primaryStorageSRUuid) + srrec = session.xenapi.SR.get_record(sr) + srtype = srrec["type"] + if srtype == "file": + pbd = session.xenapi.SR.get_PBDs(sr)[0] + pbdrec = session.xenapi.PBD.get_record(pbd) + primarySRPath = pbdrec["device_config"]["location"] + return primarySRPath + elif isISCSI: primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid return os.path.join(lvhdutil.VG_LOCATION, primarySRDir) else: @@ -472,7 +480,7 @@ def getVhdParent(session, args): snapshotUuid = args['snapshotUuid'] isISCSI = getIsTrueString(args['isISCSI']) - primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) util.SMlog("primarySRPath: " + primarySRPath) baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) @@ -490,7 +498,7 @@ def backupSnapshot(session, args): isISCSI = getIsTrueString(args['isISCSI']) path = args['path'] localMountPoint = args['localMountPoint'] - primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) util.SMlog("primarySRPath: " + primarySRPath) baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) From 3121133c03c4db30c9bc0a3a5546e19678b2ee76 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 29 Aug 2013 23:14:42 +0200 Subject: [PATCH 080/251] CLOUDSTACK-1192: GetVmDiskStatsCommand return null as disk I/O statistics does not support xen/vmware --- .../cloud/hypervisor/vmware/resource/VmwareResource.java | 8 ++++++++ .../cloud/hypervisor/xen/resource/CitrixResourceBase.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 16b829ae5ab..97c9acc284a 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -91,6 +91,8 @@ import com.cloud.agent.api.GetHostStatsAnswer; import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmDiskStatsAnswer; +import com.cloud.agent.api.GetVmDiskStatsCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVncPortAnswer; @@ -445,6 +447,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((GetHostStatsCommand) cmd); } else if (clz == GetVmStatsCommand.class) { answer = execute((GetVmStatsCommand) cmd); + } else if (clz == GetVmDiskStatsCommand.class) { + answer = execute((GetDiskVmStatsCommand) cmd); } else if (clz == CheckHealthCommand.class) { answer = execute((CheckHealthCommand) cmd); } else if (clz == StopCommand.class) { @@ -3808,6 +3812,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return answer; } + protected Answer execute(GetVmDiskStatsCommand cmd) { + return new GetVmDiskStatsAnswer(cmd, null, null, null); + } + protected Answer execute(CheckHealthCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource CheckHealthCommand: " + _gson.toJson(cmd)); 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 4de9ab94dfa..815299b361c 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 @@ -56,6 +56,8 @@ import com.cloud.agent.api.GetHostStatsAnswer; import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmDiskStatsAnswer; +import com.cloud.agent.api.GetVmDiskStatsCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVncPortAnswer; @@ -489,6 +491,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((GetHostStatsCommand) cmd); } else if (clazz == GetVmStatsCommand.class) { return execute((GetVmStatsCommand) cmd); + } else if (clazz == GetVmDiskStatsCommand.class) { + return execute((GetVmDiskStatsCommand) cmd); } else if (clazz == CheckHealthCommand.class) { return execute((CheckHealthCommand) cmd); } else if (clazz == StopCommand.class) { @@ -2757,6 +2761,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return vmResponseMap; } + protected GetVmDiskStatsAnswer execute(GetVmDiskStatsCommand cmd) { + return new GetVmDiskStatsAnswer(cmd, null, null, null); + } + protected Object[] getRRDData(Connection conn, int flag) { /* From d10babe824723e9a9ccfffdadc34398db94e12d5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 29 Aug 2013 14:13:00 -0700 Subject: [PATCH 081/251] CLOUDSTACK-4266: UI > Infrastructure > clusters > add cluster dialog > if configuration 'vmware.use.dvswitch' is set to false (i.e. override public/guest traffic checkboxes are hidden), hide VSM fields (regardless of another configuration 'vmware.use.nexus.switch'). --- ui/scripts/system.js | 141 +++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8eb113ed81d..43f7b2abfe6 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10285,9 +10285,8 @@ createForm: { title: 'label.add.cluster', preFilter: function(args) { - var $form = args.$form; - - $form.click(function() { + var $form = args.$form; + $form.click(function() { var $vsmFields = $form.find('.form-item').filter(function() { var vsmFields = [ 'vsmipaddress', @@ -10297,21 +10296,26 @@ return $.inArray($(this).attr('rel'), vsmFields) > -1; }); var $vsmReqFields = $form.find('.form-item').filter(function() { - var vsmFields = [ + var vsmReqFields = [ 'vsmipaddress_req', 'vsmusername_req', 'vsmpassword_req' ]; - return $.inArray($(this).attr('rel'), vsmFields) > -1; - }); + return $.inArray($(this).attr('rel'), vsmReqFields) > -1; + }); if ($form.find('.form-item[rel=hypervisor] select').val() == 'VMware' ) { $form.find('.form-item[rel=vCenterHost]').css('display', 'inline-block'); $form.find('.form-item[rel=vCenterUsername]').css('display', 'inline-block'); $form.find('.form-item[rel=vCenterPassword]').css('display', 'inline-block'); $form.find('.form-item[rel=vCenterDatacenter]').css('display', 'inline-block'); - - //***** 'vmware.use.dvswitch' (begin) (whether to show override traffic checkbox) ***** + + var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); + var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); + var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); + var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); + + //***** 'vmware.use.dvswitch' : whether to show override traffic checkbox (begin) ***** var dvSwitchEnabled = false; $.ajax({ url: createURL('listConfigurations'), @@ -10325,57 +10329,53 @@ } } }); - if (dvSwitchEnabled) { + if (dvSwitchEnabled == true) { $form.find('.form-item[rel=overridepublictraffic]').css('display', 'inline-block'); - $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); - } else { + $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); + + //'vmware.use.nexus.vswitch': whether to show VSM fields (begin) + var vSwitchEnabled = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.nexus.vswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + vSwitchEnabled = true; + } + } + }); + if (vSwitchEnabled == true) { + if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || + ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { + $vsmReqFields.css('display', 'inline-block'); + $vsmFields.hide(); + } else { + $vsmFields.css('display', 'inline-block'); + $vsmReqFields.hide(); + } + + } else { //vSwitchEnabled == false + $vsmFields.hide(); + $vsmReqFields.hide(); + } + //***** 'vmware.use.dvswitch' : whether to show override traffic checkbox (end) ***** + + } else { //dvSwitchEnabled == false $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); - } - //***** 'vmware.use.dvswitch' (end) ***** - - //***** 'vmware.use.nexus.vswitch' (begin) (whether to show VSM fields) ***** - var vSwitchEnabled = false; - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'vmware.use.nexus.vswitch' - }, - async: false, - success: function(json) { - if (json.listconfigurationsresponse.configuration[0].value == 'true') { - vSwitchEnabled = true; - } - } - }); - if (vSwitchEnabled) { - //$vsmFields.css('display', 'inline-block'); - var $overridePublicTraffic = $form.find('.form-item[rel=overridepublictraffic] input[type=checkbox]'); - var $vSwitchPublicType = $form.find('.form-item[rel=vSwitchPublicType] select'); - - var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); - var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); - - if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || - ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { - $vsmReqFields.css('display', 'inline-block'); - $vsmFields.hide(); - } else { - $vsmFields.css('display', 'inline-block'); - $vsmReqFields.hide(); - } - - } else { - //$vsmFields.css('display', 'none'); - $vsmFields.hide(); + $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); + + $vsmFields.hide(); $vsmReqFields.hide(); - } - //***** 'vmware.use.nexus.vswitch' (end) ***** + } + //***** 'vmware.use.dvswitch' (end) ***** } else { //XenServer, KVM, etc (non-VMware) $form.find('.form-item[rel=vCenterHost]').css('display', 'none'); @@ -10385,14 +10385,26 @@ $form.find('.form-item[rel=enableNexusVswitch]').css('display', 'none'); $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); - $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); - $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); + $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); $vsmFields.hide(); $vsmReqFields.hide(); - } + } + + if ($form.find('.form-item[rel=overridepublictraffic]').css('display') != 'none' && $overridePublicTraffic.is(':checked')) { + $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'inline-block'); + $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'inline-block'); + } else { + $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); + } + + if ($form.find('.form-item[rel=overrideguesttraffic]').css('display') != 'none' && $overrideGuestTraffic.is(':checked')) { + $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'inline-block'); + $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'inline-block'); + } else { + $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); + $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); + } }); $form.trigger('click'); @@ -10630,14 +10642,12 @@ args.response.success({ data: items }); - }, - isHidden: true, - dependsOn: 'overridepublictraffic' + }, + isHidden: true }, vSwitchPublicName: { - label: 'Public Traffic vSwitch Name', - dependsOn: 'overridepublictraffic', + label: 'Public Traffic vSwitch Name', isHidden: true }, @@ -10649,7 +10659,6 @@ docID: 'helpOverrideGuestNetwork' }, - vSwitchGuestType: { label: 'Guest Traffic vSwitch Type', select: function(args) { @@ -10703,13 +10712,11 @@ data: items }); }, - isHidden: true, - dependsOn: 'overrideguesttraffic' + isHidden: true }, vSwitchGuestName: { - label: ' Guest Traffic vSwitch Name', - dependsOn: 'overrideguesttraffic', + label: ' Guest Traffic vSwitch Name', isHidden: true }, From 93bfe708d9cc4da6a7bec3ecaaa17812b2b73048 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 29 Aug 2013 14:24:35 -0700 Subject: [PATCH 082/251] CLOUDSTACK-4459: fix silly bug, one more time --- .../com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index dc7981c336b..c0e10002930 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -129,7 +129,7 @@ public class LibvirtStoragePool implements KVMStoragePool { try { disk = this._storageAdaptor.getPhysicalDisk(volumeUuid, this); } catch (CloudRuntimeException e) { - if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) || + if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) && (this.getStoragePoolType() != StoragePoolType.Filesystem)) { throw e; } From ca0679951ec9092cd6f840a351c71d77ebc59ca0 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 29 Aug 2013 14:33:50 -0700 Subject: [PATCH 083/251] CLOUDSTACK-4562: addS3/addSwift API is retired (being replaced with addImageStore API) and old configuration 's3.enable'/'swift.enable' has been removed from database. So, remove obsolete UI that calls addS3/addSwift API. --- ui/scripts/system.js | 199 ------------------------------------------- 1 file changed, 199 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 43f7b2abfe6..78f02428e93 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5612,205 +5612,6 @@ }); } } - }, - - // Enable swift - enableSwift: { - label: 'label.enable.swift', - isHeader: true, - addRow: false, - preFilter: function(args) { - var swiftEnableConfiguration = false; - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'swift.enable' - }, - async: false, - success: function(json) { - swiftEnableConfiguration = json.listconfigurationsresponse.configuration[0].value == 'true' ? true : false; - } - }); - - var havingSwift = false; - $.ajax({ - url: createURL("listImageStores"), - data: { - provider: 'Swift' - }, - async: false, - success: function(json) { - var items = json.listimagestoreresponse.imagestore; - if (items != null && items.length > 0) - havingSwift = true; - } - }); - - if(swiftEnableConfiguration == true && havingSwift == false) - return true; - else - return false; - }, - messages: { - notification: function(args) { - return 'label.enable.swift'; - } - }, - createForm: { - desc: 'confirm.enable.swift', - fields: { - url: { - label: 'label.url', - validation: { - required: true - } - }, - account: { - label: 'label.account' - }, - username: { - label: 'label.username' - }, - key: { - label: 'label.key' - } - } - }, - action: function(args) { - $.ajax({ - url: createURL('addSwift'), - data: { - url: args.data.url, - account: args.data.account, - username: args.data.username, - key: args.data.key - }, - success: function(json) { - args.response.success(); - - cloudStack.dialog.notice({ - message: 'message.after.enable.swift' - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } - }, - - enableS3: { - label: 'label.enable.s3', - isHeader: true, - addRow: false, - - preFilter: function(args) { - var s3EnableConfiguration = false; - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 's3.enable' - }, - async: false, - success: function(json) { - s3EnableConfiguration = json.listconfigurationsresponse.configuration[0].value == 'true' ? true : false; - } - }); - - var havingS3 = false; - $.ajax({ - url: createURL("listImageStores"), - data: { - provider: 'S3' - }, - async: false, - success: function(json) { - var items = json.listimagestoreresponse.imagestore; - if (items != null && items.length > 0) { - havingS3 = true; - } - } - }); - - if(s3EnableConfiguration == true && havingS3 == false) - return true; - else - return false; - }, - - messages: { - notification: function(args) { - return 'label.enable.s3'; - } - }, - - createForm: { - desc: 'confirm.enable.s3', - fields: { - accesskey: { - label: 'label.s3.access_key', - validation: { - required: true - } - }, - secretkey: { - label: 'label.s3.secret_key', - validation: { - required: true - } - }, - bucket: { - label: 'label.s3.bucket', - validation: { - required: true - } - }, - endpoint: { - label: 'label.s3.endpoint' - }, - usehttps: { - label: 'label.s3.use_https', - isEditable: true, - isBoolean: true, - isChecked: true, - converter: cloudStack.converters.toBooleanText - }, - connectiontimeout: { - label: 'label.s3.connection_timeout' - }, - maxerrorretry: { - label: 'label.s3.max_error_retry' - }, - sockettimeout: { - label: 'label.s3.socket_timeout' - } - } - }, - action: function(args) { - $.ajax({ - url: createURL('addS3'), - data: { - accesskey: args.data.accesskey, - secretkey: args.data.secretkey, - bucket: args.data.bucket, - endpoint: args.data.endpoint, - usehttps: (args.data.usehttps != null && args.data.usehttps == 'on' ? 'true' : 'false'), - connectiontimeout: args.data.connectiontimeout, - maxerrorretry: args.data.maxerrorretry, - sockettimeout: args.data.sockettimeout - }, - success: function(json) { - args.response.success(); - - cloudStack.dialog.notice({ - message: 'message.after.enable.s3' - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } } }, From 0b9b36cbca0a4294ccdb3f68ababefc314d9fd8e Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Thu, 29 Aug 2013 14:33:36 -0700 Subject: [PATCH 084/251] CLOUDSTACK-4561: DeployVm failed after upgrading from earlier version having a private zone to 4.2 Changes: - In the upgrade path, for a private zone, entry needs to be added in the affinity_group_domain_map to provide access to the private zone for the domains it belongs too. --- .../src/com/cloud/upgrade/dao/Upgrade410to420.java | 9 +++++++++ setup/db/db/schema-410to420.sql | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 2e58dddd0d3..773ad62764c 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -392,6 +392,15 @@ public class Upgrade410to420 implements DbUpgrade { if (rs2.next()) { affinityGroupId = rs2.getLong(1); } + + // add the domain map + String sqlMap = "INSERT INTO `cloud`.`affinity_group_domain_map` (`domain_id`, `affinity_group_id`) VALUES (?, ?)"; + pstmtUpdate = conn.prepareStatement(sqlMap); + pstmtUpdate.setLong(1, domainId); + pstmtUpdate.setLong(2, affinityGroupId); + pstmtUpdate.executeUpdate(); + pstmtUpdate.close(); + } rs2.close(); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 723791deda5..6eb753454c5 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -440,7 +440,7 @@ CREATE TABLE `cloud`.`affinity_group_domain_map` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `domain_id` bigint unsigned NOT NULL COMMENT 'domain id', `affinity_group_id` bigint unsigned NOT NULL COMMENT 'affinity group id', - `subdomain_access` int(1) unsigned COMMENT '1 if affinity group can be accessible from the subdomain', + `subdomain_access` int(1) unsigned DEFAULT 1 COMMENT '1 if affinity group can be accessible from the subdomain', PRIMARY KEY (`id`), CONSTRAINT `fk_affinity_group_domain_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_affinity_group_domain_map__affinity_group_id` FOREIGN KEY (`affinity_group_id`) REFERENCES `affinity_group`(`id`) ON DELETE CASCADE From 07d79c7ad660375cfb0e88794048993c4e349cf0 Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Thu, 29 Aug 2013 09:02:22 -0700 Subject: [PATCH 085/251] CLOUDSTACK-4539: [VMWARE] vmware.create.full.clone is set to true in upgraded setup;default nature of vms are full clone Reviwed-by: Kelven Yang Description: Change the criterion for overriding/preserving the vmware.create.full.clone flag. The earlier criterion was simply the version numbers, but that is not a good business logic basis for the change. With this fix, if past version deployments have any deployments (data centers), this flag will be set to false. Else, it will be set to true. --- .../cloud/upgrade/dao/Upgrade410to420.java | 48 +++++++++++++++++++ setup/db/db/schema-410to420.sql | 7 --- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 773ad62764c..839ab86479e 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -113,6 +113,54 @@ public class Upgrade410to420 implements DbUpgrade { migrateDatafromIsoIdInVolumesTable(conn); setRAWformatForRBDVolumes(conn); migrateVolumeOnSecondaryStorage(conn); + createFullCloneFlag(conn); + } + + private void createFullCloneFlag(Connection conn) { + ResultSet rs = null; + PreparedStatement delete = null; + PreparedStatement query = null; + PreparedStatement update = null; + int numRows = 0; + try { + delete = conn.prepareStatement("delete from `cloud`.`configuration` where name='vmware.create.full.clone';"); + delete.executeUpdate(); + query = conn.prepareStatement("select count(*) from `cloud`.`data_center`"); + rs = query.executeQuery(); + if (rs.next()) { + numRows = rs.getInt(1); + } + if (numRows > 0) { + update = conn.prepareStatement("insert into `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'UserVmManager', 'vmware.create.full.clone' , 'false', 'If set to true, creates VMs as full clones on ESX hypervisor');"); + } else { + update = conn.prepareStatement("insert into `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'UserVmManager', 'vmware.create.full.clone' , 'true', 'If set to true, creates VMs as full clones on ESX hypervisor');"); + } + update.executeUpdate(); + } catch (SQLException e) { + throw new CloudRuntimeException("Failed to set global flag vmware.create.full.clone: ", e); + } finally { + if (update != null) { + try { + update.close(); + } catch (SQLException e) { + + } + } + if (query != null) { + try { + query.close(); + } catch (SQLException e) { + + } + } + if (delete != null) { + try { + delete.close(); + } catch (SQLException e) { + + } + } + } } private void migrateVolumeOnSecondaryStorage(Connection conn) { diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 6eb753454c5..6e9fe72b63e 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -403,13 +403,6 @@ CREATE TABLE `cloud`.`user_vm_clone_setting` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `cloud`.`configuration` (category, instance, component, name, value, description) - SELECT tmp.category, tmp.instance, tmp.component, tmp.name, tmp.value, tmp.description FROM - (SELECT 'Advanced' category, 'DEFAULT' instance, 'UserVmManager' component, 'vmware.create.full.clone' name, 'true' value, 'If set to true, creates VMs as full clones on ESX hypervisor' description) tmp - WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE name = 'vmware.create.full.clone'); - - - CREATE TABLE `cloud`.`affinity_group` ( `id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL, From 2848af8126f47f92ebb90191e62778461193cb9b Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 29 Aug 2013 16:18:36 -0700 Subject: [PATCH 086/251] Fix the build cause by typo in a un-test check-in! --- .../com/cloud/hypervisor/vmware/resource/VmwareResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 97c9acc284a..96ee201bac1 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -448,7 +448,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } else if (clz == GetVmStatsCommand.class) { answer = execute((GetVmStatsCommand) cmd); } else if (clz == GetVmDiskStatsCommand.class) { - answer = execute((GetDiskVmStatsCommand) cmd); + answer = execute((GetVmDiskStatsCommand) cmd); } else if (clz == CheckHealthCommand.class) { answer = execute((CheckHealthCommand) cmd); } else if (clz == StopCommand.class) { From 3cca6502a1b48556ca7bb103ed226d44557dc399 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 29 Aug 2013 16:37:45 -0700 Subject: [PATCH 087/251] CLOUDSTACK-4405: fix vm migration during the upgrade to 4.2 --- agent/bindir/libvirtqemuhook.in | 53 +++++++++++++++++++ packaging/centos63/cloud.spec | 2 + .../kvm/resource/BridgeVifDriver.java | 19 ++++--- 3 files changed, 67 insertions(+), 7 deletions(-) create mode 100755 agent/bindir/libvirtqemuhook.in diff --git a/agent/bindir/libvirtqemuhook.in b/agent/bindir/libvirtqemuhook.in new file mode 100755 index 00000000000..7bf9634fdf5 --- /dev/null +++ b/agent/bindir/libvirtqemuhook.in @@ -0,0 +1,53 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import sys +from xml.dom.minidom import parse +from cloudutils.configFileOps import configFileOps +from cloudutils.networkConfig import networkConfig +def isOldStyleBridge(brName): + if brName.find("cloudVirBr") == 0: + return True + else: + return False +def getGuestNetworkDevice(): + netlib = networkConfig() + cfo = configFileOps("/etc/cloudstack/agent/agent.properties") + guestDev = cfo.getEntry("guest.network.device") + enslavedDev = netlib.getEnslavedDev(guestDev, 1) + return enslavedDev +def handleMigrateBegin(): + try: + domain = parse(sys.stdin) + for interface in domain.getElementsByTagName("interface"): + source = interface.getElementsByTagName("source")[0] + bridge = source.getAttribute("bridge") + if not isOldStyleBridge(bridge): + continue + vlanId = bridge.replace("cloudVirBr","") + phyDev = getGuestNetworkDevice() + newBrName="br" + phyDev + "-" + vlanId + source.setAttribute("bridge", newBrName) + print(domain.toxml()) + except: + pass +if __name__ == '__main__': + if len(sys.argv) != 5: + sys.exit(0) + + if sys.argv[2] == "migrate" and sys.argv[3] == "begin": + handleMigrateBegin() diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 01685e3b6d7..2b814f871df 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -287,6 +287,7 @@ install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}%{_s install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent install -D agent/target/transformed/cloudstack-agent-upgrade ${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade +install -D agent/target/transformed/libvirtqemuhook ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib @@ -555,6 +556,7 @@ fi %config(noreplace) %{_sysconfdir}/%{name}/agent %dir %{_localstatedir}/log/%{name}/agent %attr(0644,root,root) %{_datadir}/%{name}-agent/lib/*.jar +%attr(0755,root,root) %{_datadir}/%{name}-agent/lib/libvirtqemuhook %dir %{_datadir}/%{name}-agent/plugins %{_defaultdocdir}/%{name}-agent-%{version}/LICENSE %{_defaultdocdir}/%{name}-agent-%{version}/NOTICE diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index 195cf409e5d..e3779a77cd7 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -45,6 +45,7 @@ public class BridgeVifDriver extends VifDriverBase { private static final Object _vnetBridgeMonitor = new Object(); private String _modifyVlanPath; + private String bridgeNameSchema; @Override public void configure(Map params) throws ConfigurationException { @@ -60,6 +61,8 @@ public class BridgeVifDriver extends VifDriverBase { networkScriptsDir = "scripts/vm/network/vnet"; } + bridgeNameSchema = (String) params.get("network.bridge.name.schema"); + String value = (String) params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000; @@ -144,13 +147,15 @@ public class BridgeVifDriver extends VifDriverBase { } private String setVnetBrName(String pifName, String vnetId) { - String brName = "br" + pifName + "-"+ vnetId; - String oldStyleBrName = "cloudVirBr" + vnetId; - - String cmdout = Script.runSimpleBashScript("brctl show | grep " + oldStyleBrName); - if (cmdout != null && cmdout.contains(oldStyleBrName)) { - s_logger.info("Using old style bridge name for vlan " + vnetId + " because existing bridge " + oldStyleBrName + " was found"); - brName = oldStyleBrName; + String brName = null; + if (bridgeNameSchema != null) { + if (bridgeNameSchema.equalsIgnoreCase("3.0")) { + brName = "cloudVirBr" + vnetId; + } else if (bridgeNameSchema.equalsIgnoreCase("4.0")) { + brName = "br" + pifName + "-"+ vnetId; + } + } else { + brName = "br" + pifName + "-"+ vnetId; } return brName; From 7a8a3ca26bb1eb68a2570954ad9892ab9ddee46b Mon Sep 17 00:00:00 2001 From: radhikap Date: Fri, 30 Aug 2013 11:25:43 +0530 Subject: [PATCH 088/251] cherry pick error fixed --- docs/en-US/Release_Notes.xml | 31 +++++++- ...ange-network-offering-on-guest-network.xml | 75 +++++++++++-------- docs/en-US/guest-traffic.xml | 23 +++--- docs/en-US/pvlan.xml | 8 +- docs/en-US/site-to-site-vpn.xml | 11 ++- 5 files changed, 101 insertions(+), 47 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index f3f160227af..facd144cf40 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -221,6 +221,28 @@ under the License. check policies in the cloud. You can override this value for an individual health check policy.
+
+ Snaphotting, backups, cloning and System VMs for RBD Primary Storage + + These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt + 0.9.14 on the KVM hypervisors. + + With this release &PRODUCT; will leverage the features of RBD format 2. This allows + snapshotting and backing up those snapshots. + Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they + are not RBD diffs. This because when restoring a backup of a snapshot it is not mandatory + that this backup is deployed on RBD again, it could also be a NFS Primary Storage. + Another key feature of RBD format 2 is cloning and with this release templates will be + copied to Primary Storage once and using the cloning mechanism new disks will be cloned + from this parent template. This saves space and decreases deployment time for Instances + dramatically. + Before this release a NFS Primary Storage was still required for running the System + VMs from. The reason behind this was a so called 'patch disk' which was generated by the + hypervisor which contained metadata for the System VM. The scripts generating this disk + didn't support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of + the patch disk a VirtIO serial console is used to pass meta information to System VMs. + This enabled the deployment of System VMs on RBD Primary Storage. +
Issues Fixed in 4.2.0 @@ -386,6 +408,14 @@ under the License. + + CLOUDSTACK-2709 + + VM Migration across VMware clusters which are added with different switches + (Standard Swith,Vmware DVS, Cisco Nexus 1000v) is not supported.. + + CLOUDSTACK-4207 @@ -1268,7 +1298,6 @@ service cloudstack-agent start # apt-get update # apt-get upgrade cloud-* - Edit /etc/cloudstack/agent/agent.properties to change the resource parameter from diff --git a/docs/en-US/change-network-offering-on-guest-network.xml b/docs/en-US/change-network-offering-on-guest-network.xml index 2c7db3e9176..be99835a774 100644 --- a/docs/en-US/change-network-offering-on-guest-network.xml +++ b/docs/en-US/change-network-offering-on-guest-network.xml @@ -20,34 +20,49 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---> +-->
- Changing the Network Offering on a Guest Network - A user or administrator can change the network offering that is associated with an existing guest network. - - Log in to the &PRODUCT; UI as an administrator or end user. - If you are changing from a network offering that uses the &PRODUCT; virtual router to one - that uses external devices as network service providers, you must first stop all the - VMs on the network. - See "Stopping and Starting Virtual Machines" in the Administrator's Guide. - See . - In the left navigation, choose Network. - Click the name of the network you want to modify. - In the Details tab, click Edit. - - - - - EditButton.png: button to edit a network - - - In Network Offering, choose the new network offering, then click Apply. - A prompt is displayed asking whether you want to keep the existing CIDR. This is to let you - know that if you change the network offering, the CIDR will be affected. Choose No - to proceed with the change. - Wait for the update to complete. Don’t try to restart VMs until the network change is - complete. - If you stopped any VMs, restart them. - -
- + Changing the Network Offering on a Guest Network + A user or administrator can change the network offering that is associated with an existing + guest network. + + + Log in to the &PRODUCT; UI as an administrator or end user. + + + If you are changing from a network offering that uses the &PRODUCT; virtual router to + one that uses external devices as network service providers, you must first stop all the VMs + on the network. See . + + + In the left navigation, choose Network. + + + Click the name of the network you want to modify. + + + In the Details tab, click Edit. + + + + + EditButton.png: button to edit a network + + + + + In Network Offering, choose the new network offering, then click Apply. + A prompt is displayed asking whether you want to keep the existing CIDR. This is to let + you know that if you change the network offering, the CIDR will be affected. + If you upgrade between virtual router as a provider and an external network device as + provider, acknowledge the change of CIDR to continue, so choose Yes. + + + Wait for the update to complete. Don’t try to restart VMs until the network change is + complete. + + + If you stopped any VMs, restart them. + + +
diff --git a/docs/en-US/guest-traffic.xml b/docs/en-US/guest-traffic.xml index bca635582a8..c55c7e1b97d 100644 --- a/docs/en-US/guest-traffic.xml +++ b/docs/en-US/guest-traffic.xml @@ -23,15 +23,20 @@ -->
Guest Traffic - A network can carry guest traffic only between VMs within one zone. Virtual machines in different zones cannot communicate with each other using their IP addresses; they must communicate with each other by routing through a public IP address. - This figure illustrates a typical guest traffic setup: - - - - - Depicts a guest traffic setup. - - The Management Server automatically creates a virtual router for each network. A virtual router is a special virtual machine that runs on the hosts. Each virtual router has three network interfaces. Its eth0 interface serves as the gateway for the guest traffic and has the IP address of 10.1.1.1. Its eth1 interface is used by the system to configure the virtual router. Its eth2 interface is assigned a public IP address for public traffic. + A network can carry guest traffic only between VMs within one zone. Virtual machines in different zones cannot communicate with each other using their IP addresses; they must communicate with each other by routing through a public IP address. + See a typical guest traffic setup given below: + + + + + guest-traffic-setup.png: Depicts a guest traffic setup + + The Management Server automatically creates a virtual router for each network. A virtual + router is a special virtual machine that runs on the hosts. Each virtual router in an isolated + network has three network interfaces. If multiple public VLAN is used, the router will have + multiple public interfaces. Its eth0 interface serves as the gateway for the guest traffic and + has the IP address of 10.1.1.1. Its eth1 interface is used by the system to configure the + virtual router. Its eth2 interface is assigned a public IP address for public traffic. The virtual router provides DHCP and will automatically assign an IP address for each guest VM within the IP range assigned for the network. The user can manually reconfigure guest VMs to assume different IP addresses. Source NAT is automatically configured in the virtual router to forward outbound traffic for all guest VMs
diff --git a/docs/en-US/pvlan.xml b/docs/en-US/pvlan.xml index eb4c1d85c13..38b25319faf 100644 --- a/docs/en-US/pvlan.xml +++ b/docs/en-US/pvlan.xml @@ -223,15 +223,15 @@ IP Range: A range of IP addresses that are accessible from the Internet and are assigned to the guest VMs. - If one NIC is used, these IPs should be in the same CIDR in the case of - IPv6. + - + Network Domain: A custom DNS suffix at the level of a network. If you want to assign a special domain name to the guest VM network, diff --git a/docs/en-US/site-to-site-vpn.xml b/docs/en-US/site-to-site-vpn.xml index a5899eac4f1..9a41a0adf82 100644 --- a/docs/en-US/site-to-site-vpn.xml +++ b/docs/en-US/site-to-site-vpn.xml @@ -3,6 +3,7 @@ %BOOK_ENTITIES; ]> + + If one NIC is used, these IPs should be in the same CIDR in the case of + IPv6. - + Network Domain: A custom DNS suffix at the level of a network. If you want to assign a special domain name to the guest VM network, From 1ba591516957c21b95abb7eea0a9e4e23f0fd18e Mon Sep 17 00:00:00 2001 From: radhikap Date: Fri, 30 Aug 2013 11:46:19 +0530 Subject: [PATCH 090/251] build error fixed --- docs/en-US/change-network-offering-on-guest-network.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/change-network-offering-on-guest-network.xml b/docs/en-US/change-network-offering-on-guest-network.xml index be99835a774..de3a80ecddc 100644 --- a/docs/en-US/change-network-offering-on-guest-network.xml +++ b/docs/en-US/change-network-offering-on-guest-network.xml @@ -32,7 +32,7 @@ If you are changing from a network offering that uses the &PRODUCT; virtual router to one that uses external devices as network service providers, you must first stop all the VMs - on the network. See . + on the network. In the left navigation, choose Network. From 948014dee6af67d4bdd27301e23f4cdee695d9f1 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 30 Aug 2013 08:51:01 +0200 Subject: [PATCH 091/251] CLOUDSTACK-4566: fix incorrect values in resource_count table for resource limitation There are three issues in resource_count table (1) expunge a vm, the public_ip decreases and becomes -1 in basic zone. (2) recover a vm, the volume increase. (3) restore a vm, the volume decrease. --- server/src/com/cloud/network/NetworkManagerImpl.java | 2 +- server/src/com/cloud/vm/UserVmManagerImpl.java | 6 ++++-- server/test/com/cloud/vm/UserVmManagerTest.java | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 472336c8f1a..fb1dcb10fdd 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -508,7 +508,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } addr.setState(assign ? IpAddress.State.Allocated : IpAddress.State.Allocating); - if (vlanUse != VlanType.DirectAttached || zone.getNetworkType() == NetworkType.Basic) { + if (vlanUse != VlanType.DirectAttached) { addr.setAssociatedWithNetworkId(guestNetworkId); addr.setVpcId(vpcId); } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 251c203f13b..a970478727a 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1479,8 +1479,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } //Update Resource Count for the given account - _resourceLimitMgr.incrementResourceCount(account.getId(), - ResourceType.volume, new Long(volumes.size())); resourceCountIncrement(account.getId(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); txn.commit(); @@ -4878,6 +4876,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } else { newVol = volumeMgr.allocateDuplicateVolume(root, null); } + // Save usage event and update resource count for user vm volumes + if (vm instanceof UserVm) { + _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume); + } _volsDao.attachVolume(newVol.getId(), vmId, newVol.getDeviceId()); diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java index 973b8bc9f5c..b113f5dbd50 100755 --- a/server/test/com/cloud/vm/UserVmManagerTest.java +++ b/server/test/com/cloud/vm/UserVmManagerTest.java @@ -68,6 +68,7 @@ import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountService; import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; import com.cloud.user.UserContext; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; @@ -104,6 +105,8 @@ public class UserVmManagerTest { @Mock List _rootVols; @Mock Account _accountMock2; @Mock ServiceOfferingDao _offeringDao; + @Mock ResourceLimitService _resourceLimitMgr; + @Before public void setup(){ MockitoAnnotations.initMocks(this); @@ -121,6 +124,7 @@ public class UserVmManagerTest { _userVmMgr._configMgr = _configMgr; _userVmMgr._offeringDao= _offeringDao; _userVmMgr._capacityMgr = _capacityMgr; + _userVmMgr._resourceLimitMgr = _resourceLimitMgr; _userVmMgr._scaleRetry = 2; doReturn(3L).when(_account).getId(); @@ -488,4 +492,4 @@ public class UserVmManagerTest { _userVmMgr.moveVMToUser(cmd); } -} \ No newline at end of file +} From 72a026cea28a7390fc61be9fc0ed92bfb496e5e3 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 30 Aug 2013 12:40:42 +0530 Subject: [PATCH 092/251] Fix pep8 formatting to fix cloudstack-marvin build (cherry picked from commit 5326833971da14df17565b61c7f15f11af6a61b5) --- tools/marvin/marvin/deployDataCenter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index 4c34a5099cf..8cc9cd4fa6f 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -109,7 +109,6 @@ specify a valid config file" % cfgFile) sleep(timeout) retry = retry - 1 - def createPrimaryStorages(self, primaryStorages, zoneId, podId, clusterId): if primaryStorages is None: return From 420b654eaf2fe23c28d0da8dbddd7c58d0ce868b Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 30 Aug 2013 14:36:29 +0530 Subject: [PATCH 093/251] CLOUDSTACK-4567: Correcting URLs that pointed to the incubator resources The location on the downloads section points to the incubator pages and the KEYS file used for GPG verify of the source points to the incubator resource. Corrected both links Signed-off-by: Prasanna Santhanam --- docs/en-US/getting-release.xml | 2 +- docs/en-US/verifying-source.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en-US/getting-release.xml b/docs/en-US/getting-release.xml index ee08a941b96..33c246f08c5 100644 --- a/docs/en-US/getting-release.xml +++ b/docs/en-US/getting-release.xml @@ -26,7 +26,7 @@ Getting the release You can download the latest &PRODUCT; release from the - + Apache CloudStack project download page. Prior releases are available via archive.apache.org as well. See the downloads page for more information on archived releases. diff --git a/docs/en-US/verifying-source.xml b/docs/en-US/verifying-source.xml index b20b9bbacf9..668ea84f266 100644 --- a/docs/en-US/verifying-source.xml +++ b/docs/en-US/verifying-source.xml @@ -32,7 +32,7 @@ Getting the KEYS To enable you to verify the GPG signature, you will need to download the - KEYS + KEYS file. From d9ba234d6c032aeb2ba04d4e6be0502de8a4efd9 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 30 Aug 2013 11:13:59 +0200 Subject: [PATCH 094/251] CLOUDSTACK-2319: fix incorrect account_id in event table for Revoke SecurityGroupRule commands --- .../securitygroup/RevokeSecurityGroupEgressCmd.java | 10 +++++++--- .../securitygroup/RevokeSecurityGroupIngressCmd.java | 10 +++++++--- .../api/response/SecurityGroupRuleResponse.java | 4 ++-- .../security/dao/SecurityGroupRulesDaoImpl.java | 8 -------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java index c03d3e4ba5b..b30ba1c8d84 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupEgressCmd.java @@ -29,6 +29,7 @@ import org.apache.log4j.Logger; import com.cloud.async.AsyncJob; import com.cloud.event.EventTypes; import com.cloud.network.security.SecurityGroup; +import com.cloud.network.security.SecurityRule; import com.cloud.user.Account; @APICommand(name = "revokeSecurityGroupEgress", responseObject = SuccessResponse.class, description = "Deletes a particular egress rule from this security group", since="3.0.0") @@ -67,9 +68,12 @@ public class RevokeSecurityGroupEgressCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - SecurityGroup group = _entityMgr.findById(SecurityGroup.class, getId()); - if (group != null) { - return group.getAccountId(); + SecurityRule rule = _entityMgr.findById(SecurityRule.class, getId()); + if (rule != null) { + SecurityGroup group = _entityMgr.findById(SecurityGroup.class, rule.getSecurityGroupId()); + if (group != null) { + return group.getAccountId(); + } } return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java index c2fdb8b000f..a547fb02898 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/RevokeSecurityGroupIngressCmd.java @@ -29,6 +29,7 @@ import org.apache.log4j.Logger; import com.cloud.async.AsyncJob; import com.cloud.event.EventTypes; import com.cloud.network.security.SecurityGroup; +import com.cloud.network.security.SecurityRule; import com.cloud.user.Account; @APICommand(name = "revokeSecurityGroupIngress", responseObject = SuccessResponse.class, description = "Deletes a particular ingress rule from this security group") @@ -67,9 +68,12 @@ public class RevokeSecurityGroupIngressCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - SecurityGroup group = _entityMgr.findById(SecurityGroup.class, getId()); - if (group != null) { - return group.getAccountId(); + SecurityRule rule = _entityMgr.findById(SecurityRule.class, getId()); + if (rule != null) { + SecurityGroup group = _entityMgr.findById(SecurityGroup.class, rule.getSecurityGroupId()); + if (group != null) { + return group.getAccountId(); + } } return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked diff --git a/api/src/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java b/api/src/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java index 5aeee6f0611..798b1237c94 100644 --- a/api/src/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java +++ b/api/src/org/apache/cloudstack/api/response/SecurityGroupRuleResponse.java @@ -20,11 +20,11 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import com.cloud.network.security.SecurityGroupRules; +import com.cloud.network.security.SecurityRule; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; -@EntityReference(value = SecurityGroupRules.class) +@EntityReference(value = SecurityRule.class) public class SecurityGroupRuleResponse extends BaseResponse { @SerializedName("ruleid") @Param(description="the id of the security group rule") private String ruleId; diff --git a/engine/schema/src/com/cloud/network/security/dao/SecurityGroupRulesDaoImpl.java b/engine/schema/src/com/cloud/network/security/dao/SecurityGroupRulesDaoImpl.java index 18ef57fbcd8..cb3baac3350 100644 --- a/engine/schema/src/com/cloud/network/security/dao/SecurityGroupRulesDaoImpl.java +++ b/engine/schema/src/com/cloud/network/security/dao/SecurityGroupRulesDaoImpl.java @@ -85,12 +85,4 @@ public class SecurityGroupRulesDaoImpl extends GenericDaoBase sc = createSearchCriteria(); - sc.addAnd("ruleUuid", SearchCriteria.Op.EQ, uuid); - SecurityGroupRulesVO rule = findOneIncludingRemovedBy(sc); - SecurityGroupRulesVO newRule = new SecurityGroupRulesVO(rule.getRuleId()); - return newRule; - } } From a011b450b6a76fa7f116efdc1286c9e00ed9caf5 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 30 Aug 2013 15:18:17 +0530 Subject: [PATCH 095/251] docs: correcting the links to the wiki - pointing to the new wiki page on 'How to build CloudStack'. Although we shouldnt be using wiki sections on our docs. - Also formatting the rpms built by cloudstack into a paramlisting (cherry picked from commit c2afa0f03ce3c9a0f04dcc108039ba5ef2d9edf9) --- docs/en-US/build-nonoss.xml | 4 ++-- docs/en-US/build-rpm.xml | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/en-US/build-nonoss.xml b/docs/en-US/build-nonoss.xml index fceca6071c2..dbcab99e9bb 100644 --- a/docs/en-US/build-nonoss.xml +++ b/docs/en-US/build-nonoss.xml @@ -31,9 +31,9 @@ under the License. To build the Non-OSS plugins, you'll need to have the requisite JARs installed under the deps directory. - Because these modules require dependencies that can't be distributed with &PRODUCT; you'll need to download them yourself. Links to the most recent dependencies are listed on the How to build on master branch page on the wiki. + Because these modules require dependencies that can't be distributed with &PRODUCT; you'll need to download them yourself. Links to the most recent dependencies are listed on the How to build CloudStack page on the wiki. - You may also need to download vhd-util, which was removed due to licensing issues. You'll copy vhd-util to the scripts/vm/hypervisor/xenserver/ directory. + You may also need to download vhd-util when using XenServer hypervisors, which was removed due to licensing issues. You'll copy vhd-util to the scripts/vm/hypervisor/xenserver/ directory. Once you have all the dependencies copied over, you'll be able to build &PRODUCT; with the nonoss option: diff --git a/docs/en-US/build-rpm.xml b/docs/en-US/build-rpm.xml index 100a06f486e..c15074293a6 100644 --- a/docs/en-US/build-rpm.xml +++ b/docs/en-US/build-rpm.xml @@ -53,7 +53,16 @@ under the License. $./package.sh That will run for a bit and then place the finished packages in dist/rpmbuild/RPMS/x86_64/. - You should see seven RPMs in that directory: cloudstack-agent-4.1.0-SNAPSHOT.el6.x86_64.rpm, cloudstack-awsapi-4.1.0-SNAPSHOT.el6.x86_64.rpm, cloudstack-cli-4.1.0-SNAPSHOT.el6.x86_64.rpm, cloudstack-common-4.1.0-SNAPSHOT.el6.x86_64.rpm, cloudstack-docs-4.1.0-SNAPSHOT.el6.x86_64.rpm, cloudstack-management-4.1.0-SNAPSHOT.el6.x86_64.rpm, and cloudstack-usage-4.1.0-SNAPSHOT.el6.x86_64.rpm. + You should see seven RPMs in that directory: + + cloudstack-agent-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-awsapi-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-cli-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-common-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-docs-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-management-4.1.0-SNAPSHOT.el6.x86_64.rpm + cloudstack-usage-4.1.0-SNAPSHOT.el6.x86_64.rpm +
Creating a yum repo From f37e0b0b6bf473884d4f7dfc21e3559f4ebeb1b3 Mon Sep 17 00:00:00 2001 From: radhikap Date: Fri, 30 Aug 2013 15:44:18 +0530 Subject: [PATCH 096/251] CLOUDSTACK-4569 review comments on egress firewall and multiple ip per nic --- docs/en-US/egress-firewall-rule.xml | 12 ++++++------ docs/en-US/multiple-ip-nic.xml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/en-US/egress-firewall-rule.xml b/docs/en-US/egress-firewall-rule.xml index 17bf15eb18c..93d5a814547 100644 --- a/docs/en-US/egress-firewall-rule.xml +++ b/docs/en-US/egress-firewall-rule.xml @@ -108,12 +108,12 @@
- Changing the Default Egress Policy - You can configure the default policy of egress firewall rules in Isolated Advanced - networks. Use the create network offering option to determine whether the default policy - should be block or allow all the traffic to the public network from a guest network. If no - policy is specified, by default all the traffic is allowed from the guest network that you - create by using this network offering. + Configuring the Default Egress Policy + The default egress policy for Isolated guest network is configured by using Network + offering. Use the create network offering option to determine whether the default policy + should be block or allow all the traffic to the public network from a guest network. Use this + network offering to create the network. If no policy is specified, by default all the traffic + is allowed from the guest network that you create by using this network offering. You have two options: Allow and Deny. Allow diff --git a/docs/en-US/multiple-ip-nic.xml b/docs/en-US/multiple-ip-nic.xml index 790befcc081..344dc8df16f 100644 --- a/docs/en-US/multiple-ip-nic.xml +++ b/docs/en-US/multiple-ip-nic.xml @@ -75,9 +75,9 @@ Click Acquire New Secondary IP, and click Yes in the confirmation dialog. - You need to specify the secondary IP address on the guest VM. &PRODUCT; will not - automatically configure the acquired IP address on the VM. Ensure that you assign IPs to - NIC each time the VM reboots. + You need to configure the IP on the guest VM NIC manually. &PRODUCT; will not + automatically configure the acquired IP address on the VM. Ensure that the IP address + configuration persist on VM reboot. 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. From 404474751466bd1ca84b7e6abfbc65dbb1014e64 Mon Sep 17 00:00:00 2001 From: Marty Sweet Date: Sun, 25 Aug 2013 13:17:51 +0100 Subject: [PATCH 097/251] Added Accessing System VM via SSH --- docs/en-US/accessing-system-vms.xml | 66 ++++++++++++++++++++ docs/en-US/images/view-systemvm-details.png | Bin 0 -> 25483 bytes docs/en-US/working-with-system-vm.xml | 1 + 3 files changed, 67 insertions(+) create mode 100755 docs/en-US/accessing-system-vms.xml create mode 100755 docs/en-US/images/view-systemvm-details.png diff --git a/docs/en-US/accessing-system-vms.xml b/docs/en-US/accessing-system-vms.xml new file mode 100755 index 00000000000..e1b6090d7af --- /dev/null +++ b/docs/en-US/accessing-system-vms.xml @@ -0,0 +1,66 @@ + + +%BOOK_ENTITIES; +]> + + + +
+ Accessing System VMs + It may sometimes be necessary to access System VMs for diagnostics of certain issues, for example if you are experiencing SSVM (Secondary Storage VM) connection issues. Use the steps below in order to connect to the SSH console of a running System VM. + + Accessing System VMs over the network requires the use of private keys and connecting to System VMs SSH Daemon on port 3922. + XenServer/KVM Hypervisors store this key at /root/.ssh/id_rsa.cloud on each &PRODUCT; agent. + To access System VMs running on ESXi, the key is stored on the management server at /var/lib/cloudstack/management/.ssh/id_rsa. + + + + Find the details of the System VM + + Log in with admin privileges to the &PRODUCT; UI. + Click Infrastructure, then System VMs, and then click the name of a running VM. + Take a note of the 'Host', 'Private IP Address' and 'Link Local IP Address' of the System VM you wish to access. + + + + + XenServer/KVM Hypervisors + + Connect to the Host of which the System VM is running. + SSH the 'Link Local IP Address' of the System VM from the Host on which the VM is running. + Format: ssh -i <path-to-private-key> <link-local-ip> -p 3922 + Example: root@faith:~# ssh -i /root/.ssh/id_rsa.cloud 169.254.3.93 -p 3922 + + + + ESXi Hypervisors + + Connect to your &PRODUCT; Management Server. + ESXi users should SSH to the private IP address of the System VM. + Format: ssh -i <path-to-private-key> <vm-private-ip> -p 3922 + Example: root@management:~# ssh -i /var/lib/cloudstack/management/.ssh/id_rsa 172.16.0.250 -p 3922 + + + + + + + +
diff --git a/docs/en-US/images/view-systemvm-details.png b/docs/en-US/images/view-systemvm-details.png new file mode 100755 index 0000000000000000000000000000000000000000..bce270bf25850fd718439bcd9db6f4a5c56469a2 GIT binary patch literal 25483 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 F{XeoVEx!N& literal 0 HcmV?d00001 diff --git a/docs/en-US/working-with-system-vm.xml b/docs/en-US/working-with-system-vm.xml index 70f7dd1aa4e..073d0772561 100644 --- a/docs/en-US/working-with-system-vm.xml +++ b/docs/en-US/working-with-system-vm.xml @@ -32,6 +32,7 @@ parameter on the &PRODUCT; UI or by calling the listConfigurations API. + From 62c3356d78156f93f600c694dbc181550614f8e0 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Fri, 30 Aug 2013 17:04:54 +0200 Subject: [PATCH 098/251] docs: Add Release Notes about Disk I/O Polling and Throttling --- docs/en-US/Release_Notes.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index facd144cf40..15de58f22a3 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -243,6 +243,15 @@ under the License. the patch disk a VirtIO serial console is used to pass meta information to System VMs. This enabled the deployment of System VMs on RBD Primary Storage.
+
+ Disk I/O polling and throttling + CLOUDSTACK-1192: + On KVM hypervisors polling and throttling of disk I/Os is supported. Per disk disk attached to + an Instance the usage server will record the amount of IOps. + Per disk offering you are able to specify the number of Read and Write I/Os. Trottling is + done by Qemu/KVM. + Both polling and throttling only works with KVM and with all types of Primary Storage. +
Issues Fixed in 4.2.0 From 8d386a9f6cd9c35a8b13afd6530d89971340bc82 Mon Sep 17 00:00:00 2001 From: David Nalley Date: Fri, 30 Aug 2013 11:07:55 -0400 Subject: [PATCH 099/251] adding publican.cfg fot qig no idea why I missed this earlier --- docs/qig/publican.cfg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/qig/publican.cfg diff --git a/docs/qig/publican.cfg b/docs/qig/publican.cfg new file mode 100644 index 00000000000..52d434c3775 --- /dev/null +++ b/docs/qig/publican.cfg @@ -0,0 +1,22 @@ +# Config::Simple 4.59 +# Fri May 25 12:50:59 2012 +# 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. + +xml_lang: "en-US" +type: Book +brand: cloudstack +docname: qig From 0929011991906da557497778be86a65713e89110 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Fri, 30 Aug 2013 17:46:43 +0200 Subject: [PATCH 100/251] docs: Add RBD issue link to Release Notes --- docs/en-US/Release_Notes.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 15de58f22a3..d64d4909f85 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -227,7 +227,8 @@ under the License. These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt 0.9.14 on the KVM hypervisors. - With this release &PRODUCT; will leverage the features of RBD format 2. This allows + CLOUDSTACK-1191: + With this release &PRODUCT; will leverage the features of RBD format 2. This allows snapshotting and backing up those snapshots. Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they are not RBD diffs. This because when restoring a backup of a snapshot it is not mandatory From b5713ecad2eddec9ba57619c0f9c046ca557b4d5 Mon Sep 17 00:00:00 2001 From: David Nalley Date: Fri, 30 Aug 2013 12:10:57 -0400 Subject: [PATCH 101/251] updating the version number --- docs/qig/en-US/Book_Info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/qig/en-US/Book_Info.xml b/docs/qig/en-US/Book_Info.xml index e356de4415a..98cbcb49327 100644 --- a/docs/qig/en-US/Book_Info.xml +++ b/docs/qig/en-US/Book_Info.xml @@ -27,7 +27,7 @@ Quick Install Guide Prescriptive instructions for deploying Apache CloudStack Apache CloudStack - 4.0.2 + 4.2.0 0 0 From 2c2ebee3f7395a6541088eefe91ceaa1b02c70d5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 30 Aug 2013 11:03:11 -0700 Subject: [PATCH 102/251] CLOUDSTACK-4089: UI > zone wizard > hypervisor VMware > multiple physical networks > edit Public/Guest traffic type > fix a bug that vSwitch Type dropdown selection didn't remain after Public/Guest traffic type is dragged to another physical network. --- ui/scripts/ui-custom/zoneWizard.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index b8f77a479d1..254ea630acc 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -324,10 +324,10 @@ }, { id: 'vmwaredvs', description: 'VMware vNetwork Distributed Virtual Switch' - }], - defaultValue: trafficData.vSwitchType + }] }); - } + }, + defaultValue: trafficData.vSwitchType } }); } From e362f51f37b718466f2d80d9193e58e1fafcb8fb Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 30 Aug 2013 11:10:56 -0700 Subject: [PATCH 103/251] CLOUDSTACK-4362: always honor vCenter on-disk meta data to work with live migration better --- .../vmware/resource/VmwareResource.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 96ee201bac1..de6885c749c 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -3031,27 +3031,27 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception("Primary datastore " + primaryStore.getUuid() + " is not mounted on host."); DatastoreMO dsMo = volumeDsDetails.second(); + // we will honor vCenter's meta if it exists + if(diskInfoBuilder != null && diskInfoBuilder.getDiskCount() > 0) { + // we will always on-disk info from vCenter in this case + VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); + if(diskInfo != null) { + if(s_logger.isInfoEnabled()) + s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore. use on-disk chain: " + + _gson.toJson(diskInfo)); + + return diskInfo.getDiskChain(); + } else { + s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore. on-disk may be out of sync as well. disk device info: " + deviceBusName); + } + } + String datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( - dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); - + dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); if(!dsMo.fileExists(datastoreDiskPath)) { if(s_logger.isInfoEnabled()) s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath); - if(diskInfoBuilder != null && diskInfoBuilder.getDiskCount() > 0) { - // we will always on-disk info from vCenter in this case - VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); - if(diskInfo != null) { - if(s_logger.isInfoEnabled()) - s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore. use on-disk chain: " + - _gson.toJson(diskInfo)); - - return diskInfo.getDiskChain(); - } else { - s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore. on-disk may be out of sync as well. disk device info: " + deviceBusName); - } - } - // last resort, try chain info stored in DB if(volumeTO.getChainInfo() != null) { VirtualMachineDiskInfo diskInfo = _gson.fromJson(volumeTO.getChainInfo(), VirtualMachineDiskInfo.class); @@ -3061,7 +3061,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } throw new Exception("Volume " + volumeTO.getId() + " does not seem to exist on datastore. Broken disk chain"); - } + } } return new String[] { datastoreDiskPath }; From 6354604eedff0c5f4ddef4940ce02df80adb656c Mon Sep 17 00:00:00 2001 From: Saksham Srivastava Date: Fri, 30 Aug 2013 12:30:41 -0700 Subject: [PATCH 104/251] CLOUDSTACK-4572: findHostsForMigration API does not return correct host list Changes: Expected behavior: The api should return the list of suitable/unsuitable hosts Added fix that creates a deep copy of the the variable allHosts and prevents faulty host list return. --- server/src/com/cloud/server/ManagementServerImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 3341c05da97..8437c9924f3 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1161,8 +1161,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe null, null, null); } - Pair, Integer> otherHosts = new Pair, Integer>(allHosts, - new Integer(allHosts.size())); + //'otherHosts' must use the current value of allHosts as allHosts may get modified later in the allocator + List allHostsCpy = new ArrayList(allHosts); + Pair, Integer> otherHosts = new Pair, Integer>(allHostsCpy, + new Integer(allHostsCpy.size())); List suitableHosts = new ArrayList(); ExcludeList excludes = new ExcludeList(); excludes.addHost(srcHostId); From a98eb12549a900c7f88acc68457957a4a955fecd Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 30 Aug 2013 14:27:40 -0700 Subject: [PATCH 105/251] CLOUDSTACK-4575: Portable IP: disassociating a transferred public IP fails The code is excessively complicated and convoluted. DisassociateIP -> Revoke Rule -> {FW, PF{incl SNAT}, LB, RA VPN} -> -> Send IpAssoc (false) to VR Send all config to VR again -> Send IpAssoc(false) to VR again <---- fails here since it cannot find the VLAN for the IP since it is already gone -> Mark Ip as released The workaround fix would be to not throw an exception in CitrixResourceBase if it is disassociate and the VLAN does not exist on the XS host. Signed-off-by: Chiradeep Vittal --- .../com/cloud/hypervisor/xen/resource/CitrixResourceBase.java | 3 +++ 1 file changed, 3 insertions(+) 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 815299b361c..9f254c35a65 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 @@ -2299,6 +2299,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (add && correctVif == null) { addVif = true; } + if (!add && correctVif == null) { + return; // it is a disassociateIp and it has already happened + } if (addVif) { // Add a new VIF to DomR From 845e280d71f041afa7c6880dd7ae2a4a6386a83b Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 30 Aug 2013 14:50:11 -0700 Subject: [PATCH 106/251] UI > Infrastructure > clusters > Add cluster dialog > change variable name for Nexus DVS fields to be more intuitive. --- ui/scripts/system.js | 70 +++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 78f02428e93..8b092600f27 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10088,21 +10088,21 @@ preFilter: function(args) { var $form = args.$form; $form.click(function() { - var $vsmFields = $form.find('.form-item').filter(function() { - var vsmFields = [ + var $nexusDvsOptFields = $form.find('.form-item').filter(function() { + var nexusDvsOptFields = [ 'vsmipaddress', 'vsmusername', 'vsmpassword' ]; - return $.inArray($(this).attr('rel'), vsmFields) > -1; + return $.inArray($(this).attr('rel'), nexusDvsOptFields) > -1; }); - var $vsmReqFields = $form.find('.form-item').filter(function() { - var vsmReqFields = [ + var $nexusDvsReqFields = $form.find('.form-item').filter(function() { + var nexusDvsReqFields = [ 'vsmipaddress_req', 'vsmusername_req', 'vsmpassword_req' ]; - return $.inArray($(this).attr('rel'), vsmReqFields) > -1; + return $.inArray($(this).attr('rel'), nexusDvsReqFields) > -1; }); if ($form.find('.form-item[rel=hypervisor] select').val() == 'VMware' ) { @@ -10116,8 +10116,8 @@ var $overrideGuestTraffic = $form.find('.form-item[rel=overrideguesttraffic] input[type=checkbox]'); var $vSwitchGuestType = $form.find('.form-item[rel=vSwitchGuestType] select'); - //***** 'vmware.use.dvswitch' : whether to show override traffic checkbox (begin) ***** - var dvSwitchEnabled = false; + + var useDvs = false; $.ajax({ url: createURL('listConfigurations'), data: { @@ -10126,16 +10126,15 @@ async: false, success: function(json) { if (json.listconfigurationsresponse.configuration[0].value == 'true') { - dvSwitchEnabled = true; + useDvs = true; } } }); - if (dvSwitchEnabled == true) { + if (useDvs == true) { //If using Distributed vswitch, there is OverrideTraffic option. $form.find('.form-item[rel=overridepublictraffic]').css('display', 'inline-block'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'inline-block'); - - //'vmware.use.nexus.vswitch': whether to show VSM fields (begin) - var vSwitchEnabled = false; + + var useNexusDvs = false; $.ajax({ url: createURL('listConfigurations'), data: { @@ -10144,27 +10143,26 @@ async: false, success: function(json) { if (json.listconfigurationsresponse.configuration[0].value == 'true') { - vSwitchEnabled = true; + useNexusDvs = true; } } }); - if (vSwitchEnabled == true) { + if (useNexusDvs == true) { //If using Nexus Distributed vswitch, show Nexus Distributed vswitch fields (either required ones or optional ones). if (($overridePublicTraffic.is(':checked') && $vSwitchPublicType.val() == 'nexusdvs') || ($overrideGuestTraffic.is(':checked') && $vSwitchGuestType.val() == 'nexusdvs' )) { - $vsmReqFields.css('display', 'inline-block'); - $vsmFields.hide(); + $nexusDvsReqFields.css('display', 'inline-block'); + $nexusDvsOptFields.hide(); } else { - $vsmFields.css('display', 'inline-block'); - $vsmReqFields.hide(); + $nexusDvsOptFields.css('display', 'inline-block'); + $nexusDvsReqFields.hide(); } - } else { //vSwitchEnabled == false - $vsmFields.hide(); - $vsmReqFields.hide(); - } - //***** 'vmware.use.dvswitch' : whether to show override traffic checkbox (end) ***** + } else { //If not using Nexus Distributed vswitch, hide Nexus Distributed vswitch fields. + $nexusDvsOptFields.hide(); + $nexusDvsReqFields.hide(); + } - } else { //dvSwitchEnabled == false + } else { //useDvs == false $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); $form.find('.form-item[rel=vSwitchPublicType]').css('display', 'none'); $form.find('.form-item[rel=vSwitchPublicName]').css('display', 'none'); @@ -10173,10 +10171,10 @@ $form.find('.form-item[rel=vSwitchGuestType]').css('display', 'none'); $form.find('.form-item[rel=vSwitchGuestName]').css('display', 'none'); - $vsmFields.hide(); - $vsmReqFields.hide(); + $nexusDvsOptFields.hide(); + $nexusDvsReqFields.hide(); } - //***** 'vmware.use.dvswitch' (end) ***** + } else { //XenServer, KVM, etc (non-VMware) $form.find('.form-item[rel=vCenterHost]').css('display', 'none'); @@ -10187,8 +10185,8 @@ $form.find('.form-item[rel=overridepublictraffic]').css('display', 'none'); $form.find('.form-item[rel=overrideguesttraffic]').css('display', 'none'); - $vsmFields.hide(); - $vsmReqFields.hide(); + $nexusDvsOptFields.hide(); + $nexusDvsReqFields.hide(); } if ($form.find('.form-item[rel=overridepublictraffic]').css('display') != 'none' && $overridePublicTraffic.is(':checked')) { @@ -10396,7 +10394,7 @@ vSwitchPublicType: { label: 'Public Traffic vSwitch Type', select: function(args) { - var vSwitchEnabled = false; + var useNexusDvs = false; var items = [] $.ajax({ url: createURL('listConfigurations'), @@ -10406,12 +10404,12 @@ async: false, success: function(json) { if (json.listconfigurationsresponse.configuration[0].value == 'true') { - vSwitchEnabled = true; + useNexusDvs = true; } } }); - if (vSwitchEnabled) { + if (useNexusDvs) { items.push({ id: "nexusdvs", description: "Cisco Nexus 1000v Distributed Virtual Switch" @@ -10465,7 +10463,7 @@ select: function(args) { var items = [] - var vSwitchEnabled = false; + var useNexusDvs = false; $.ajax({ url: createURL('listConfigurations'), data: { @@ -10474,13 +10472,13 @@ async: false, success: function(json) { if (json.listconfigurationsresponse.configuration[0].value == 'true') { - vSwitchEnabled = true; + useNexusDvs = true; } } }); - if (vSwitchEnabled) { + if (useNexusDvs) { items.push({ id: "nexusdvs", description: "Cisco Nexus 1000v Distributed Virtual Switch" From 8d60e4436bbc71f62c52b2d3861ecad1cd82596b Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 30 Aug 2013 14:52:44 -0700 Subject: [PATCH 107/251] CLOUDSTACK-4089: UI > zone wizard > VMware hypervisor > physical network > edit Public/Guest traffic type > vSwitchType dropdown > set default option upon configuration 'vmware.use.dvswtich' and 'vmware.use.nexus.vswitch'. --- ui/scripts/ui-custom/zoneWizard.js | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index 254ea630acc..a1c58031a51 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -309,7 +309,45 @@ } }; - if($trafficType.hasClass('guest') || $trafficType.hasClass('public')) { + if($trafficType.hasClass('guest') || $trafficType.hasClass('public')) { + if(trafficData.vSwitchType == null) { + var useDvs = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.dvswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + useDvs = true; + } + } + }); + if (useDvs == true) { + var useNexusDvs = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.nexus.vswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + useNexusDvs = true; + } + } + }); + if (useNexusDvs == true) { + trafficData.vSwitchType = 'nexusdvs'; + } else { + trafficData.vSwitchType = 'vmwaredvs'; + } + } else { //useDvs == false + trafficData.vSwitchType = 'vmwaresvs'; + } + } + $.extend(fields, { vSwitchType: { label: 'vSwitch Type', From ff4f931cd8b29983b5ad82675ccc65b651839a62 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 30 Aug 2013 15:18:32 -0700 Subject: [PATCH 108/251] CLOUDSTACK-4089: UI > zone wizard > VMware hypervisor > physical network > edit traffic type > set default value for vSwitchName field upon selected vSwitchType. --- ui/scripts/ui-custom/zoneWizard.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index a1c58031a51..695534ffe0a 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -340,11 +340,14 @@ }); if (useNexusDvs == true) { trafficData.vSwitchType = 'nexusdvs'; + fields.vSwitchName.defaultValue = 'epp0'; } else { trafficData.vSwitchType = 'vmwaredvs'; + fields.vSwitchName.defaultValue = 'dvSwitch0'; } } else { //useDvs == false trafficData.vSwitchType = 'vmwaresvs'; + fields.vSwitchName.defaultValue = 'vSwitch0'; } } From ffc53e81e0de998f15a91b90eb05104402c4538c Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 30 Aug 2013 18:06:08 -0700 Subject: [PATCH 109/251] kvm upgrade issue from 2.2.14: 1. the uuid passed by mgt server is malformat, libvirt can't start vm. 2. the template path on primary storage is incorrect, which contains absolute path --- .../resource/LibvirtComputingResource.java | 20 ++++++++++++++++++- .../kvm/storage/KVMStorageProcessor.java | 4 ++++ .../LibvirtComputingResourceTest.java | 15 ++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 67819bcfad3..3ee811ff835 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3374,10 +3374,28 @@ ServerResource { } } + protected String getUuid(String uuid) { + if (uuid == null) { + uuid = UUID.randomUUID().toString(); + } else { + try { + UUID uuid2 = UUID.fromString(uuid); + String uuid3 = uuid2.toString(); + if (!uuid3.equals(uuid)) { + uuid = UUID.randomUUID().toString(); + } + } catch (IllegalArgumentException e) { + uuid = UUID.randomUUID().toString(); + } + } + return uuid; + } protected LibvirtVMDef createVMFromSpec(VirtualMachineTO vmTO) { LibvirtVMDef vm = new LibvirtVMDef(); vm.setDomainName(vmTO.getName()); - vm.setDomUUID(vmTO.getUuid()); + String uuid = vmTO.getUuid(); + uuid = getUuid(uuid); + vm.setDomUUID(uuid); vm.setDomDescription(vmTO.getOs()); GuestDef guest = new GuestDef(); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 8482a8d25dc..99ea04fc1d7 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -289,6 +289,10 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { vol = templateToPrimaryDownload(templatePath, primaryPool); } else { + if (templatePath.contains("/mnt")) { + //upgrade issue, if the path contains path, need to extract the volume uuid from path + templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1); + } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); } diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 3640030ad8c..d6e8dc2fcc2 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -24,11 +24,13 @@ import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; +import junit.framework.Assert; import org.apache.commons.lang.SystemUtils; import org.junit.Assume; import org.junit.Test; import java.util.Random; +import java.util.UUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -195,4 +197,17 @@ public class LibvirtComputingResourceTest { Pair stats = LibvirtComputingResource.getNicStats("lo"); assertNotNull(stats); } + + @Test + public void testUUID() { + String uuid = "1"; + LibvirtComputingResource lcr = new LibvirtComputingResource(); + uuid =lcr.getUuid(uuid); + Assert.assertTrue(!uuid.equals("1")); + + String oldUuid = UUID.randomUUID().toString(); + uuid = oldUuid; + uuid = lcr.getUuid(uuid); + Assert.assertTrue(uuid.equals(oldUuid)); + } } From 21a44e3ed10953b47559f1d8482b0c4aec3f6906 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 30 Aug 2013 18:15:20 -0700 Subject: [PATCH 110/251] disk resize NPE, if the new disk offering doesn't have tags, then NPE --- server/src/com/cloud/storage/VolumeManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 85a19ca63b3..1d6b44fd7db 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -1144,7 +1144,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } if (diskOffering.getTags() != null) { - if (!newDiskOffering.getTags().equals(diskOffering.getTags())) { + if (newDiskOffering.getTags() == null || !newDiskOffering.getTags().equals(diskOffering.getTags())) { throw new InvalidParameterValueException( "Tags on new and old disk offerings must match"); } From 2575ded3f32e7c27315c5701bbc4fdbc44615080 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sat, 31 Aug 2013 12:50:00 +0530 Subject: [PATCH 111/251] get host credentials from marvin configuration for ssvm test --- test/integration/smoke/test_ssvm.py | 63 ++++++++++++++++------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/test/integration/smoke/test_ssvm.py b/test/integration/smoke/test_ssvm.py index 6893472ebe1..9fa59a94d3f 100644 --- a/test/integration/smoke/test_ssvm.py +++ b/test/integration/smoke/test_ssvm.py @@ -37,11 +37,6 @@ class Services: def __init__(self): self.services = { - "host": { - "username": 'root', # Credentials for SSH - "password": 'password', - "publicport": 22, - }, "sleep": 60, "timeout": 10, } @@ -346,14 +341,18 @@ class TestSSVMs(cloudstackTestCase): hypervisor=self.apiclient.hypervisor ) else: - result = get_process_status( - host.ipaddress, - self.services['host']["publicport"], - self.services['host']["username"], - self.services['host']["password"], - ssvm.linklocalip, - "/usr/local/cloud/systemvm/ssvm-check.sh |grep -e ERROR -e WARNING -e FAIL" + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + ssvm.linklocalip, + "/usr/local/cloud/systemvm/ssvm-check.sh |grep -e ERROR -e WARNING -e FAIL" ) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") res = str(result) self.debug("SSVM script output: %s" % res) @@ -382,14 +381,18 @@ class TestSSVMs(cloudstackTestCase): hypervisor=self.apiclient.hypervisor ) else: - result = get_process_status( - host.ipaddress, - self.services['host']["publicport"], - self.services['host']["username"], - self.services['host']["password"], - ssvm.linklocalip, - "service cloud status" - ) + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + ssvm.linklocalip, + "service cloud status" + ) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") res = str(result) self.debug("Cloud Process status: %s" % res) # cloud.com service (type=secstorage) is running: process id: 2346 @@ -462,14 +465,18 @@ class TestSSVMs(cloudstackTestCase): hypervisor=self.apiclient.hypervisor ) else: - result = get_process_status( - host.ipaddress, - self.services['host']["publicport"], - self.services['host']["username"], - self.services['host']["password"], - cpvm.linklocalip, - "service cloud status" - ) + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + cpvm.linklocalip, + "service cloud status" + ) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") res = str(result) self.debug("Cloud Process status: %s" % res) self.assertEqual( From 8ccff0472269a7f06e1751ea1640532e52ba55fe Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Sun, 1 Sep 2013 07:14:27 -0700 Subject: [PATCH 112/251] CLOUDSTACK-4585: make run-time datastore folder migration, VM snapshot, bug in root disk controller type carried from previous version work under upgrade situation --- .../cloud/upgrade/dao/Upgrade410to420.java | 2 +- .../vmware/resource/VmwareResource.java | 146 +++++++++++------- .../resource/VmwareStorageLayoutHelper.java | 4 +- .../vmware/mo/VirtualMachineMO.java | 20 ++- .../hypervisor/vmware/util/VmwareHelper.java | 48 +++--- 5 files changed, 135 insertions(+), 85 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 839ab86479e..8ff07dfa9dd 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -772,7 +772,7 @@ public class Upgrade410to420 implements DbUpgrade { pstmt.close(); } else { if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())){ - throw new CloudRuntimeException("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. Cannot upgrade system Vms"); + // throw new CloudRuntimeException("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. Cannot upgrade system Vms"); } else { s_logger.warn("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. " + hypervisorAndTemplateName.getKey() + " hypervisor is not used, so not failing upgrade"); // Update the latest template URLs for corresponding hypervisor diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index de6885c749c..0990b3afb9f 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -2849,7 +2849,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (vol.getType() == Volume.Type.ISO) continue; - controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); + VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(diskInfoBuilder, vol); + controllerKey = getDiskController(matchingExistingDisk, vol, vmSpec, ideControllerKey, scsiControllerKey); VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); @@ -2858,19 +2859,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VirtualDevice device; String[] diskChain = syncDiskChain(dcMo, vmMo, vmSpec, - vol, diskInfoBuilder, - dataStoresDetails, - (controllerKey == ideControllerKey) ? true : false, - 0, // currently only support bus 0 - (controllerKey == ideControllerKey) ? ideUnitNumber : scsiUnitNumber); + vol, matchingExistingDisk, + dataStoresDetails); - device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, + device = VmwareHelper.prepareDiskDevice(vmMo, null, controllerKey, diskChain, volumeDsDetails.first(), (controllerKey == ideControllerKey) ? ideUnitNumber++ : scsiUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(device); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); if(s_logger.isDebugEnabled()) s_logger.debug("Prepare volume at new device " + _gson.toJson(device)); @@ -3013,55 +3011,37 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // return the finalized disk chain for startup, from top to bottom private String[] syncDiskChain(DatacenterMO dcMo, VirtualMachineMO vmMo, VirtualMachineTO vmSpec, - DiskTO vol, VirtualMachineDiskInfoBuilder diskInfoBuilder, - HashMap> dataStoresDetails, - boolean ideController, int deviceBusNumber, int deviceUnitNumber) throws Exception { + DiskTO vol, VirtualMachineDiskInfo diskInfo, + HashMap> dataStoresDetails + ) throws Exception { VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); - String deviceBusName; - if(ideController) - deviceBusName = String.format("ide%d:%d", deviceBusNumber, deviceUnitNumber); - else - deviceBusName = String.format("scsi%d:%d", deviceBusNumber, deviceUnitNumber); - Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); if(volumeDsDetails == null) throw new Exception("Primary datastore " + primaryStore.getUuid() + " is not mounted on host."); DatastoreMO dsMo = volumeDsDetails.second(); // we will honor vCenter's meta if it exists - if(diskInfoBuilder != null && diskInfoBuilder.getDiskCount() > 0) { - // we will always on-disk info from vCenter in this case - VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); - if(diskInfo != null) { - if(s_logger.isInfoEnabled()) - s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore. use on-disk chain: " + - _gson.toJson(diskInfo)); - - return diskInfo.getDiskChain(); - } else { - s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore. on-disk may be out of sync as well. disk device info: " + deviceBusName); - } - } + if(diskInfo != null) { + // to deal with run-time upgrade to maintain the new datastore folder structure + String disks[] = diskInfo.getDiskChain(); + for(int i = 0; i < disks.length; i++) { + DatastoreFile file = new DatastoreFile(disks[i]); + if(file.getDir() != null && file.getDir().isEmpty()) { + s_logger.info("Perform run-time datastore folder upgrade. sync " + disks[i] + " to VM folder"); + disks[i] = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( + dcMo, vmMo.getName(), dsMo, file.getFileBaseName()); + } + } + return disks; + } String datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); if(!dsMo.fileExists(datastoreDiskPath)) { - if(s_logger.isInfoEnabled()) - s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath); - - // last resort, try chain info stored in DB - if(volumeTO.getChainInfo() != null) { - VirtualMachineDiskInfo diskInfo = _gson.fromJson(volumeTO.getChainInfo(), VirtualMachineDiskInfo.class); - if(diskInfo != null) { - s_logger.info("Use chain info from DB: " + volumeTO.getChainInfo()); - return diskInfo.getDiskChain(); - } - - throw new Exception("Volume " + volumeTO.getId() + " does not seem to exist on datastore. Broken disk chain"); - } + s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath); } return new String[] { datastoreDiskPath }; @@ -3271,21 +3251,80 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - private static int getDiskController(DiskTO vol, VirtualMachineTO vmSpec, int ideControllerKey, int scsiControllerKey) { + private VirtualMachineDiskInfo getMatchingExistingDisk(VirtualMachineDiskInfoBuilder diskInfoBuilder, DiskTO vol) { + if(diskInfoBuilder != null) { + VolumeObjectTO volume = (VolumeObjectTO)vol.getData(); + + VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(volume.getPath()); + if(diskInfo != null) { + s_logger.info("Found existing disk info from volume path: " + volume.getPath()); + return diskInfo; + } else { + String chainInfo = volume.getChainInfo(); + if(chainInfo != null) { + VirtualMachineDiskInfo infoInChain = _gson.fromJson(chainInfo, VirtualMachineDiskInfo.class); + if(infoInChain != null) { + String[] disks = infoInChain.getDiskChain(); + if(disks.length > 0) { + for(String diskPath : disks) { + DatastoreFile file = new DatastoreFile(diskPath); + diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(file.getFileBaseName()); + if(diskInfo != null) { + s_logger.info("Found existing disk from chain info: " + diskPath); + return diskInfo; + } + } + } + + if(diskInfo == null) { + diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(infoInChain.getDiskDeviceBusName()); + if(diskInfo != null) { + s_logger.info("Found existing disk from from chain device bus information: " + infoInChain.getDiskDeviceBusName()); + return diskInfo; + } + } + } + } + } + } + + return null; + } + + private int getDiskController(VirtualMachineDiskInfo matchingExistingDisk, DiskTO vol, + VirtualMachineTO vmSpec, int ideControllerKey, int scsiControllerKey) { + int controllerKey; + if(matchingExistingDisk != null) { + s_logger.info("Chose disk controller based on existing information: " + matchingExistingDisk.getDiskDeviceBusName()); + if(matchingExistingDisk.getDiskDeviceBusName().startsWith("ide")) + return ideControllerKey; + else + return scsiControllerKey; + } if(vol.getType() == Volume.Type.ROOT) { - if(vmSpec.getDetails() != null && vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null) + Map vmDetails = vmSpec.getDetails(); + if(vmDetails != null && vmDetails.get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null) { - if(vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi")) + if(vmDetails.get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi")) { + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> scsi, based on root disk controller settings: " + + vmDetails.get(VmDetailConstants.ROOK_DISK_CONTROLLER)); controllerKey = scsiControllerKey; - else + } + else { + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> ide, based on root disk controller settings: " + + vmDetails.get(VmDetailConstants.ROOK_DISK_CONTROLLER)); controllerKey = ideControllerKey; + } } else { + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> scsi. due to null root disk controller setting"); controllerKey = scsiControllerKey; } + } else { // DATA volume always use SCSI device + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> scsi"); controllerKey = scsiControllerKey; } @@ -3296,9 +3335,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa int ideControllerKey, int scsiControllerKey) throws Exception { VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); - int controllerKey; - int ideUnitNumber = 1; // we always count in IDE device first - int scsiUnitNumber = 0; for(DiskTO vol: sortedDisks) { if (vol.getType() == Volume.Type.ISO) @@ -3306,15 +3342,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); - - String deviceBusName; - if(controllerKey == ideControllerKey) - deviceBusName = String.format("ide%d:%d", 0, ideUnitNumber++); - else - deviceBusName = String.format("scsi%d:%d", 0, scsiUnitNumber++); - - VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName); + VirtualMachineDiskInfo diskInfo = getMatchingExistingDisk(diskInfoBuilder, vol); assert(diskInfo != null); String[] diskChain = diskInfo.getDiskChain(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java index 20544df3d06..24b13c8b2ca 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageLayoutHelper.java @@ -109,10 +109,10 @@ public class VmwareStorageLayoutHelper { String[] vmdkFullCloneModePair = getVmdkFilePairDatastorePath(ds, vmName, vmdkName, VmwareStorageLayoutType.VMWARE, false); - if(!ds.fileExists(vmdkLinkedCloneModeLegacyPair[0])) { + if(!ds.fileExists(vmdkLinkedCloneModeLegacyPair[0]) && !ds.fileExists(vmdkLinkedCloneModePair[0])) { // To protect against inconsistency caused by non-atomic datastore file management, detached disk may // be left over in its previous owner VM. We will do a fixup synchronization here by moving it to root - // again + // again. // syncVolumeToRootFolder(dcMo, ds, vmdkName); } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 6007bf0f1aa..f7118597cb2 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -988,7 +988,7 @@ public class VirtualMachineMO extends BaseMO { s_logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + new Gson().toJson(vmdkDatastorePathChain) + ", datastore: " + morDs.getValue()); - VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, getScsiDeviceControllerKey(), + VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, getScsiDeviceControllerKey(), vmdkDatastorePathChain, morDs, -1, 1); VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); @@ -1573,7 +1573,7 @@ public class VirtualMachineMO extends BaseMO { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); - VirtualDevice device = VmwareHelper.prepareDiskDevice(clonedVmMo, -1, disks, morDs, -1, 1); + VirtualDevice device = VmwareHelper.prepareDiskDevice(clonedVmMo, null, -1, disks, morDs, -1, 1); deviceConfigSpec.setDevice(device); deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); @@ -1841,6 +1841,22 @@ public class VirtualMachineMO extends BaseMO { return null; } + public VirtualDisk getDiskDeviceByDeviceBusName(String deviceBusName) throws Exception { + List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualDisk) { + String deviceNumbering = getDeviceBusName(devices, device); + if(deviceNumbering.equals(deviceBusName)) + return (VirtualDisk)device; + } + } + } + + return null; + } + public VirtualMachineDiskInfoBuilder getDiskInfoBuilder() throws Exception { VirtualMachineDiskInfoBuilder builder = new VirtualMachineDiskInfoBuilder(); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java index e545c8e3fdd..bcf9f1419b3 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java @@ -264,18 +264,40 @@ public class VmwareHelper { } // vmdkDatastorePath: [datastore name] vmdkFilePath - public static VirtualDevice prepareDiskDevice(VirtualMachineMO vmMo, int controllerKey, String vmdkDatastorePathChain[], + public static VirtualDevice prepareDiskDevice(VirtualMachineMO vmMo, VirtualDisk device, int controllerKey, String vmdkDatastorePathChain[], ManagedObjectReference morDs, int deviceNumber, int contextNumber) throws Exception { assert(vmdkDatastorePathChain != null); assert(vmdkDatastorePathChain.length >= 1); - VirtualDisk disk = new VirtualDisk(); + VirtualDisk disk; + VirtualDiskFlatVer2BackingInfo backingInfo; + if(device != null) { + disk = device; + backingInfo = (VirtualDiskFlatVer2BackingInfo)disk.getBacking(); + } else { + disk = new VirtualDisk(); + backingInfo = new VirtualDiskFlatVer2BackingInfo(); + backingInfo.setDatastore(morDs); + backingInfo.setDiskMode(VirtualDiskMode.PERSISTENT.value()); + disk.setBacking(backingInfo); + + if(controllerKey < 0) + controllerKey = vmMo.getIDEDeviceControllerKey(); + if(deviceNumber < 0) + deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo(); - backingInfo.setDatastore(morDs); + disk.setControllerKey(controllerKey); + disk.setKey(-contextNumber); + disk.setUnitNumber(deviceNumber); + + VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo(); + connectInfo.setConnected(true); + connectInfo.setStartConnected(true); + disk.setConnectable(connectInfo); + } + backingInfo.setFileName(vmdkDatastorePathChain[0]); - backingInfo.setDiskMode(VirtualDiskMode.PERSISTENT.value()); if(vmdkDatastorePathChain.length > 1) { String[] parentDisks = new String[vmdkDatastorePathChain.length - 1]; for(int i = 0; i < vmdkDatastorePathChain.length - 1; i++) @@ -284,22 +306,6 @@ public class VmwareHelper { setParentBackingInfo(backingInfo, morDs, parentDisks); } - disk.setBacking(backingInfo); - - if(controllerKey < 0) - controllerKey = vmMo.getIDEDeviceControllerKey(); - if(deviceNumber < 0) - deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - - disk.setControllerKey(controllerKey); - disk.setKey(-contextNumber); - disk.setUnitNumber(deviceNumber); - - VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo(); - connectInfo.setConnected(true); - connectInfo.setStartConnected(true); - disk.setConnectable(connectInfo); - return disk; } From 034a67a7b437e40105237587914916e790b35b0e Mon Sep 17 00:00:00 2001 From: radhikap Date: Mon, 2 Sep 2013 13:58:52 +0530 Subject: [PATCH 113/251] review comments on API dev guide is incorporated --- docs/en-US/added-API-commands-4.2.xml | 19 +++++++++++++++++-- docs/en-US/changed-API-commands-4.2.xml | 14 +++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/en-US/added-API-commands-4.2.xml b/docs/en-US/added-API-commands-4.2.xml index bf9594f852a..14a5f64b8ee 100644 --- a/docs/en-US/added-API-commands-4.2.xml +++ b/docs/en-US/added-API-commands-4.2.xml @@ -21,6 +21,22 @@
Added API Commands in 4.2 + + addImageStore + Adds all types of secondary storage providers, S3/Swift/NFS. + + + createSecondaryStagingStore + Adds a staging secondary storage in each zone. + + + listImageStores + Lists all secondary storages, S3/Swift/NFS. + + + listSecondaryStagingStores + Lists all staging secondary storages. + addIpToNic Adds an IP address to the NIC from the guest subnet. The request parameters are: nicid, @@ -286,8 +302,7 @@ scaleSystemVm - Scales the service offering for a systemVM, console proxy, or secondary storage. The - system VM must be in Stopped state for this command to take effect. + Scales the service offering for a systemVM, console proxy, or secondary storage. listDeploymentPlanners diff --git a/docs/en-US/changed-API-commands-4.2.xml b/docs/en-US/changed-API-commands-4.2.xml index 0401f7211c4..8fda9cc13bd 100644 --- a/docs/en-US/changed-API-commands-4.2.xml +++ b/docs/en-US/changed-API-commands-4.2.xml @@ -217,9 +217,9 @@ updateCluster - The following new request parameters are added: cpuovercommitratio - (optional), memoryovercommitratio (optional) - The following new response parameters are added: cpuovercommitratio, + The following new request parameters are removed: cpuovercommitratio, + memoryovercommitratio + The following new response parameters are removed: cpuovercommitratio, memoryovercommitratio @@ -402,7 +402,8 @@ restoreVirtualMachine The following request parameter is added: templateID (optional). This is used to point to the - new template ID when the base image is updated. + new template ID when the base image is updated. The parameter templateID can be an ISO + ID in case of restore vm deployed using ISO. The following response parameters are added: diskioread, diskiowrite, diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup @@ -558,10 +559,9 @@ addCluster - The following request parameters are added: cpuovercommitratio (optional), - guestvswitchtype (optional), guestvswitchtype (optional), memoryovercommitratio + The following request parameters are added: guestvswitchtype (optional), guestvswitchtype (optional), publicvswitchtype (optional), publicvswitchtype (optional) - The following request parameters are added: cpuovercommitratio, + The following request parameters are removed: cpuovercommitratio, memoryovercommitratio From 6e463f236f8c6a18bc1ca6e66cadfe5a9f9bf2ca Mon Sep 17 00:00:00 2001 From: radhikap Date: Mon, 2 Sep 2013 14:20:07 +0530 Subject: [PATCH 114/251] CLOUDSTACK-4565 sheng's comments --- docs/en-US/shared-networks.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/shared-networks.xml b/docs/en-US/shared-networks.xml index 6cb0d79f1ac..b8a86f1f5f9 100644 --- a/docs/en-US/shared-networks.xml +++ b/docs/en-US/shared-networks.xml @@ -38,7 +38,7 @@ designated by the administrator - Shared Networks are isolated by security groups + Shared Networks can be isolated by security groups Public Network is a shared network that is not shown to the end users From 569594f9626397771b7975d6b6fc705c15343e7c Mon Sep 17 00:00:00 2001 From: radhikap Date: Mon, 2 Sep 2013 14:27:30 +0530 Subject: [PATCH 115/251] CLOUDSTACK-4565 sheng's comments --- docs/en-US/guest-traffic.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/en-US/guest-traffic.xml b/docs/en-US/guest-traffic.xml index c55c7e1b97d..943073ebc97 100644 --- a/docs/en-US/guest-traffic.xml +++ b/docs/en-US/guest-traffic.xml @@ -31,12 +31,13 @@ guest-traffic-setup.png: Depicts a guest traffic setup - The Management Server automatically creates a virtual router for each network. A virtual - router is a special virtual machine that runs on the hosts. Each virtual router in an isolated - network has three network interfaces. If multiple public VLAN is used, the router will have - multiple public interfaces. Its eth0 interface serves as the gateway for the guest traffic and - has the IP address of 10.1.1.1. Its eth1 interface is used by the system to configure the - virtual router. Its eth2 interface is assigned a public IP address for public traffic. + Typically, the Management Server automatically creates a virtual router for each network. A + virtual router is a special virtual machine that runs on the hosts. Each virtual router in an + isolated network has three network interfaces. If multiple public VLAN is used, the router will + have multiple public interfaces. Its eth0 interface serves as the gateway for the guest traffic + and has the IP address of 10.1.1.1. Its eth1 interface is used by the system to configure the + virtual router. Its eth2 interface is assigned a public IP address for public traffic. If + multiple public VLAN is used, the router will have multiple public interfaces. The virtual router provides DHCP and will automatically assign an IP address for each guest VM within the IP range assigned for the network. The user can manually reconfigure guest VMs to assume different IP addresses. Source NAT is automatically configured in the virtual router to forward outbound traffic for all guest VMs
From 8c3986df81f1ba87cf9a2a36e8d4dfb168a619d9 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 2 Sep 2013 22:03:25 +0200 Subject: [PATCH 116/251] docs: Add more information about RBD cloning to the Release Notes --- docs/en-US/Release_Notes.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index d64d4909f85..9ee70119676 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -237,6 +237,10 @@ under the License. copied to Primary Storage once and using the cloning mechanism new disks will be cloned from this parent template. This saves space and decreases deployment time for Instances dramatically.
+ Cloning will however only work with new templates and when they are freshly downloaded + to primary storage. Templates currently stored on RBD Primary Storage are in RBD format 1 + which does not support cloning. Loglevel debug on the Agent will show if cloning is used + when deploying a template or not. Before this release a NFS Primary Storage was still required for running the System VMs from. The reason behind this was a so called 'patch disk' which was generated by the hypervisor which contained metadata for the System VM. The scripts generating this disk From b99962d27f3ba24f31d5b99bae64c20ea48f13ae Mon Sep 17 00:00:00 2001 From: Jayapal Date: Tue, 3 Sep 2013 14:17:16 +0530 Subject: [PATCH 117/251] CLOUDSTACK-4586 Added CIDR validation for SG Egress rules --- .../securitygroup/AuthorizeSecurityGroupIngressCmd.java | 7 ------- .../cloud/network/security/SecurityGroupManagerImpl.java | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java index f8cd8c5d955..13d21338ae7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java @@ -207,13 +207,6 @@ public class AuthorizeSecurityGroupIngressCmd extends BaseAsyncCmd { @Override public void execute() { - if(cidrList != null){ - for(String cidr : cidrList ){ - if (!NetUtils.isValidCIDR(cidr)){ - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, cidr + " is an Invalid CIDR "); - } - } - } List ingressRules = _securityGroupService.authorizeSecurityGroupIngress(this); if (ingressRules != null && !ingressRules.isEmpty()) { SecurityGroupResponse response = _responseGenerator.createSecurityGroupResponseFromSecurityGroupRule(ingressRules); diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index 1c189c44688..fc95f21b6aa 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -600,6 +600,14 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro protocol = NetUtils.ALL_PROTO; } + if(cidrList != null){ + for(String cidr : cidrList ){ + if (!NetUtils.isValidCIDR(cidr)){ + throw new InvalidParameterValueException("Invalid cidr " + cidr); + } + } + } + if (!NetUtils.isValidSecurityGroupProto(protocol)) { throw new InvalidParameterValueException("Invalid protocol " + protocol); } From d0ffc9e33985a4a19277b94df6d13a94b53c317c Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 3 Sep 2013 11:45:34 +0200 Subject: [PATCH 118/251] CLOUDSTACK-1192: fix collectVmDiskStatistics issue when stopping a vm (Cherry-picked from commit 65c1c986da2cd14b9138a229ad6843ffbf46f3f4) --- server/src/com/cloud/vm/UserVmManagerImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a970478727a..812f8384aba 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3486,8 +3486,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use public void collectVmDiskStatistics (UserVmVO userVm) { // support KVM only util 2013.06.25 if (!userVm.getHypervisorType().equals(HypervisorType.KVM)) - return; - // Collect vm disk statistics from host before stopping Vm + return; + s_logger.debug("Collect vm disk statistics from host before stopping Vm"); long hostId = userVm.getHostId(); List vmNames = new ArrayList(); vmNames.add(userVm.getInstanceName()); @@ -3509,8 +3509,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use try { txn.start(); HashMap> vmDiskStatsByName = diskStatsAnswer.getVmDiskStatsMap(); + if (vmDiskStatsByName == null) + return; List vmDiskStats = vmDiskStatsByName.get(userVm.getInstanceName()); - if (vmDiskStats == null) return; @@ -4982,7 +4983,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Override public void prepareStop(VirtualMachineProfile profile) { UserVmVO vm = _vmDao.findById(profile.getId()); - if (vm.getState() == State.Running) + if (vm != null && vm.getState() == State.Stopping) collectVmDiskStatistics(vm); } From 3dc8b8863a02772e92bb10376d3ee5e4382c0944 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 3 Sep 2013 16:31:40 -0700 Subject: [PATCH 119/251] CLOUDSTACK-4600:Registered Cross-zone template does not populate template_zone_ref for later added zones. --- .../subsystem/api/storage/TemplateService.java | 2 ++ .../storage/image/TemplateServiceImpl.java | 17 +++++++++++++++++ .../storage/download/DownloadListener.java | 2 ++ 3 files changed, 21 insertions(+) diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java index 085fbbdb232..4950597963d 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java @@ -61,4 +61,6 @@ public interface TemplateService { void downloadBootstrapSysTemplate(DataStore store); void addSystemVMTemplatesToSecondary(DataStore store); + + void associateCrosszoneTemplatesToZone(long dcId); } diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index a77270f7c97..0113bc0cfcd 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -487,6 +487,23 @@ public class TemplateServiceImpl implements TemplateService { } } + // update template_zone_ref for cross-zone template for newly added zone + @Override + public void associateCrosszoneTemplatesToZone(long dcId){ + VMTemplateZoneVO tmpltZone; + + List allTemplates = _templateDao.listAll(); + for (VMTemplateVO vt: allTemplates){ + if (vt.isCrossZones()) { + tmpltZone = _vmTemplateZoneDao.findByZoneTemplate(dcId, vt.getId()); + if (tmpltZone == null) { + VMTemplateZoneVO vmTemplateZone = new VMTemplateZoneVO(dcId, vt.getId(), new Date()); + _vmTemplateZoneDao.persist(vmTemplateZone); + } + } + } + } + private Map listTemplate(DataStore ssStore) { ListTemplateCommand cmd = new ListTemplateCommand(ssStore.getTO()); EndPoint ep = _epSelector.select(ssStore); diff --git a/server/src/com/cloud/storage/download/DownloadListener.java b/server/src/com/cloud/storage/download/DownloadListener.java index 3b6c0dd687f..e5efcb2bbd7 100755 --- a/server/src/com/cloud/storage/download/DownloadListener.java +++ b/server/src/com/cloud/storage/download/DownloadListener.java @@ -289,6 +289,8 @@ public class DownloadListener implements Listener { return; } _imageSrv.handleSysTemplateDownload(hostHyper, agent.getDataCenterId()); + // update template_zone_ref for cross-zone templates + _imageSrv.associateCrosszoneTemplatesToZone(agent.getDataCenterId()); } /* This can be removed else if ( cmd instanceof StartupStorageCommand) { From 8451d353241ccc03ecfb234bd5b38f9625fc80dd Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 3 Sep 2013 16:51:13 -0700 Subject: [PATCH 120/251] CLOUDSTACK-4601: Add PATH for cron job of check_heartbeat.sh Also fix a typo in the script template. --- patches/systemvm/debian/config/etc/init.d/cloud-early-config | 2 +- .../config/root/redundant_router/check_heartbeat.sh.templ | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 d847c2470a6..88ecc119b61 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -848,7 +848,7 @@ setup_redundant_router() { crontab -l|grep "check_heartbeat.sh" if [ $? -ne 0 ] then - (crontab -l; echo "*/1 * * * * $rrouter_bin_path/check_heartbeat.sh 2>&1 > /dev/null") | crontab + (crontab -l; echo -e "SHELL=/bin/bash\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n*/1 * * * * $rrouter_bin_path/check_heartbeat.sh 2>&1 > /dev/null") | crontab fi } diff --git a/patches/systemvm/debian/config/root/redundant_router/check_heartbeat.sh.templ b/patches/systemvm/debian/config/root/redundant_router/check_heartbeat.sh.templ index 7a980bdfb8c..1a390e69eea 100755 --- a/patches/systemvm/debian/config/root/redundant_router/check_heartbeat.sh.templ +++ b/patches/systemvm/debian/config/root/redundant_router/check_heartbeat.sh.templ @@ -22,7 +22,7 @@ then lasttime=$(cat [RROUTER_BIN_PATH]/keepalived.ts2) thistime=$(cat [RROUTER_BIN_PATH]/keepalived.ts) diff=$(($thistime - $lasttime)) - if [ $diff -lt 30] + if [ $diff -lt 30 ] then echo Keepalived process is dead! >> [RROUTER_LOG] service keepalived stop >> [RROUTER_LOG] 2>&1 From 9f870eee0e7e3fe69c97241c25eaa0400ee0c562 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 4 Sep 2013 15:02:28 -0700 Subject: [PATCH 121/251] CLOUDSTACK-4430: Add retry logic back in case of template reload needed for vmware. --- .../com/cloud/storage/VolumeManagerImpl.java | 109 ++++++++++-------- 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 1d6b44fd7db..4569202d812 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -681,30 +681,39 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { s_logger.debug("Trying to create " + volume + " on " + pool); } DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); - AsyncCallFuture future = null; - boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false; - if (isNotCreatedFromTemplate) { - future = volService.createVolumeAsync(volume, store); - } else { - TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image); - future = volService.createVolumeFromTemplateAsync(volume, store.getId(), templ); - } - try { - VolumeApiResult result = future.get(); - if (result.isFailed()) { - s_logger.debug("create volume failed: " + result.getResult()); - throw new CloudRuntimeException("create volume failed:" + result.getResult()); + for (int i = 0; i < 2; i++) { + // retry one more time in case of template reload is required for Vmware case + AsyncCallFuture future = null; + boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false; + if (isNotCreatedFromTemplate) { + future = volService.createVolumeAsync(volume, store); + } else { + TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image); + future = volService.createVolumeFromTemplateAsync(volume, store.getId(), templ); } + try { + VolumeApiResult result = future.get(); + if (result.isFailed()) { + if (result.getResult().contains("request template reload") && (i == 0)) { + s_logger.debug("Retry template re-deploy for vmware"); + continue; + } else { + s_logger.debug("create volume failed: " + result.getResult()); + throw new CloudRuntimeException("create volume failed:" + result.getResult()); + } + } - return result.getVolume(); - } catch (InterruptedException e) { - s_logger.error("create volume failed", e); - throw new CloudRuntimeException("create volume failed", e); - } catch (ExecutionException e) { - s_logger.error("create volume failed", e); - throw new CloudRuntimeException("create volume failed", e); + return result.getVolume(); + } catch (InterruptedException e) { + s_logger.error("create volume failed", e); + throw new CloudRuntimeException("create volume failed", e); + } catch (ExecutionException e) { + s_logger.error("create volume failed", e); + throw new CloudRuntimeException("create volume failed", e); + } } - + + throw new CloudRuntimeException("create volume failed even after template re-deploy"); } public String getRandomVolumeName() { @@ -2528,31 +2537,41 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } VolumeInfo volume = volFactory.getVolume(newVol.getId(), destPool); Long templateId = newVol.getTemplateId(); - AsyncCallFuture future = null; - if (templateId == null) { - future = volService.createVolumeAsync(volume, destPool); - } else { - TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image); - future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); - } - VolumeApiResult result = null; - try { - result = future.get(); - if (result.isFailed()) { - s_logger.debug("Unable to create " - + newVol + ":" + result.getResult()); - throw new StorageUnavailableException("Unable to create " - + newVol + ":" + result.getResult(), destPool.getId()); + for (int i = 0; i < 2; i++) { + // retry one more time in case of template reload is required for Vmware case + AsyncCallFuture future = null; + if (templateId == null) { + future = volService.createVolumeAsync(volume, destPool); + } else { + TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image); + future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); + } + VolumeApiResult result = null; + try { + result = future.get(); + if (result.isFailed()) { + if (result.getResult().contains("request template reload") && (i == 0)) { + s_logger.debug("Retry template re-deploy for vmware"); + continue; + } + else { + s_logger.debug("Unable to create " + + newVol + ":" + result.getResult()); + throw new StorageUnavailableException("Unable to create " + + newVol + ":" + result.getResult(), destPool.getId()); + } + } + newVol = _volsDao.findById(newVol.getId()); + break; //break out of template-redeploy retry loop + } catch (InterruptedException e) { + s_logger.error("Unable to create " + newVol, e); + throw new StorageUnavailableException("Unable to create " + + newVol + ":" + e.toString(), destPool.getId()); + } catch (ExecutionException e) { + s_logger.error("Unable to create " + newVol, e); + throw new StorageUnavailableException("Unable to create " + + newVol + ":" + e.toString(), destPool.getId()); } - newVol = _volsDao.findById(newVol.getId()); - } catch (InterruptedException e) { - s_logger.error("Unable to create " + newVol, e); - throw new StorageUnavailableException("Unable to create " - + newVol + ":" + e.toString(), destPool.getId()); - } catch (ExecutionException e) { - s_logger.error("Unable to create " + newVol, e); - throw new StorageUnavailableException("Unable to create " - + newVol + ":" + e.toString(), destPool.getId()); } return new Pair(newVol, destPool); From 4f3b411d4cf9a6986337dea98cd902b25efefb95 Mon Sep 17 00:00:00 2001 From: Girish Shilamkar Date: Tue, 27 Aug 2013 23:56:28 -0400 Subject: [PATCH 122/251] CLOUDSTACK-4531: Resolved ssh error for basic zone. Public ip should be used for ssh instead of ipaddress of nic Signed-off-by: venkataswamybabu budumuru --- tools/marvin/marvin/integration/lib/base.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 782ad6b868a..f3d57d861bd 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -410,9 +410,16 @@ class VirtualMachine: #program ssh access over NAT via PF if mode.lower() == 'advanced': cls.access_ssh_over_nat(apiclient, services, virtual_machine, allow_egress=allow_egress) + elif mode.lower() == 'basic': - virtual_machine.ssh_ip = virtual_machine.nic[0].ipaddress - virtual_machine.public_ip = virtual_machine.nic[0].ipaddress + + if virtual_machine.publicip is not None: + vm_ssh_ip = virtual_machine.publicip #EIP/ELB (netscaler) enabled zone + else: + vm_ssh_ip = virtual_machine.nic[0].ipaddress #regular basic zone with security group + + virtual_machine.ssh_ip = vm_ssh_ip + virtual_machine.public_ip = vm_ssh_ip return VirtualMachine(virtual_machine.__dict__, services) def start(self, apiclient): From e1f6fde414690086ac4e39f616ee035ecdf9629b Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 5 Sep 2013 11:43:36 +0530 Subject: [PATCH 123/251] CLOUDSTACK-4565 review comments on VPN --- docs/en-US/configure-vpn.xml | 2 +- docs/en-US/using-vpn-with-mac.xml | 2 +- docs/en-US/using-vpn-with-windows.xml | 2 +- docs/en-US/vpn.xml | 57 +++++++++++++++++---------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/docs/en-US/configure-vpn.xml b/docs/en-US/configure-vpn.xml index 5d25620b3eb..f389f30efc3 100644 --- a/docs/en-US/configure-vpn.xml +++ b/docs/en-US/configure-vpn.xml @@ -22,7 +22,7 @@ under the License. -->
- Configuring VPN + Configuring Remote Access VPN To set up VPN for the cloud: Log in to the &PRODUCT; UI as an administrator or end user. diff --git a/docs/en-US/using-vpn-with-mac.xml b/docs/en-US/using-vpn-with-mac.xml index a41dcab5e02..769682445e4 100644 --- a/docs/en-US/using-vpn-with-mac.xml +++ b/docs/en-US/using-vpn-with-mac.xml @@ -23,7 +23,7 @@ -->
- Using VPN with Mac OS X + Using Remote Access VPN with Mac OS X First, be sure you've configured the VPN settings in your &PRODUCT; install. This section is only concerned with connecting via Mac OS X to your VPN. Note, these instructions were written on Mac OS X 10.7.5. They may differ slightly in older or newer releases of Mac OS X. diff --git a/docs/en-US/using-vpn-with-windows.xml b/docs/en-US/using-vpn-with-windows.xml index c5d95ddd3e0..82e556c58a4 100644 --- a/docs/en-US/using-vpn-with-windows.xml +++ b/docs/en-US/using-vpn-with-windows.xml @@ -23,7 +23,7 @@ -->
- Using VPN with Windows + Using Remote Access VPN with Windows The procedure to use VPN varies by Windows version. Generally, the user must edit the VPN properties and make sure that the default route is not the VPN. The following steps are for Windows L2TP clients on Windows Vista. The commands should be similar for other Windows versions. Log in to the &PRODUCT; UI and click on the source NAT IP for the account. The VPN tab should display the IPsec preshared key. Make a note of this and the source NAT IP. The UI also lists one or more users and their passwords. Choose one of these users, or, if none exists, add a user and password. diff --git a/docs/en-US/vpn.xml b/docs/en-US/vpn.xml index ccb3e861310..1f8098ca962 100644 --- a/docs/en-US/vpn.xml +++ b/docs/en-US/vpn.xml @@ -22,24 +22,41 @@ under the License. -->
- VPN - &PRODUCT; account owners can create virtual private networks (VPN) to access their virtual machines. If the guest network is instantiated from a network offering that offers the Remote Access VPN service, the virtual router (based on the System VM) is used to provide the service. &PRODUCT; provides a L2TP-over-IPsec-based remote access VPN service to guest virtual networks. Since each network gets its own virtual router, VPNs are not shared across the networks. VPN clients native to Windows, Mac OS X and iOS can be used to connect to the guest networks. The account owner can create and manage users for their VPN. &PRODUCT; does not use its account database for this purpose but uses a separate table. The VPN user database is shared across all the VPNs created by the account owner. All VPN users get access to all VPNs created by the account owner. - Make sure that not all traffic goes through the VPN. That is, the route installed by the VPN should be only for the guest network and not for all traffic. - - - Road Warrior / Remote Access. Users want to be able to - connect securely from a home or office to a private network in the cloud. Typically, - the IP address of the connecting client is dynamic and cannot be preconfigured on - the VPN server. - Site to Site. In this scenario, two private subnets are - connected over the public Internet with a secure VPN tunnel. The cloud user’s subnet - (for example, an office network) is connected through a gateway to the network in - the cloud. The address of the user’s gateway must be preconfigured on the VPN server - in the cloud. Note that although L2TP-over-IPsec can be used to set up Site-to-Site - VPNs, this is not the primary intent of this feature. For more information, see - - - - - + Remote Access VPN + &PRODUCT; account owners can create virtual private networks (VPN) to access their virtual + machines. If the guest network is instantiated from a network offering that offers the Remote + Access VPN service, the virtual router (based on the System VM) is used to provide the service. + &PRODUCT; provides a L2TP-over-IPsec-based remote access VPN service to guest virtual networks. + Since each network gets its own virtual router, VPNs are not shared across the networks. VPN + clients native to Windows, Mac OS X and iOS can be used to connect to the guest networks. The + account owner can create and manage users for their VPN. &PRODUCT; does not use its account + database for this purpose but uses a separate table. The VPN user database is shared across all + the VPNs created by the account owner. All VPN users get access to all VPNs created by the + account owner. + + Make sure that not all traffic goes through the VPN. That is, the route installed by the + VPN should be only for the guest network and not for all traffic. + + + + + Road Warrior / Remote Access. Users want to be able to + connect securely from a home or office to a private network in the cloud. Typically, the IP + address of the connecting client is dynamic and cannot be preconfigured on the VPN + server. + + + Site to Site. In this scenario, two private subnets are + connected over the public Internet with a secure VPN tunnel. The cloud user’s subnet (for + example, an office network) is connected through a gateway to the network in the cloud. The + address of the user’s gateway must be preconfigured on the VPN server in the cloud. Note + that although L2TP-over-IPsec can be used to set up Site-to-Site VPNs, this is not the + primary intent of this feature. For more information, see + + + + + +
From 65e85962db462cc9728e204abeaa13bb0e4a9a8f Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Thu, 5 Sep 2013 12:41:05 +0530 Subject: [PATCH 124/251] CLOUDSTACK-4327: Check for the all the transition states for Maintenance. Also corrected the isMaintenance function for StoragePoolVo Signed off by : nitin mehta --- .../cloudstack/storage/datastore/db/StoragePoolVO.java | 3 +-- .../cloudstack/storage/datastore/PrimaryDataStoreImpl.java | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java b/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java index 941b952baaa..557c96456ab 100644 --- a/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java +++ b/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java @@ -340,7 +340,6 @@ public class StoragePoolVO implements StoragePool { @Override public boolean isInMaintenance() { - // TODO Auto-generated method stub - return false; + return status == StoragePoolStatus.PrepareForMaintenance || status == StoragePoolStatus.Maintenance || status == StoragePoolStatus.ErrorInMaintenance || removed != null; } } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index bbccfcdafb3..22ff2209b19 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -364,9 +364,13 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return this.pdsv.getPodId(); } + public Date getRemoved() { + return this.pdsv.getRemoved(); + } + @Override public boolean isInMaintenance() { - return this.getStatus() == StoragePoolStatus.Maintenance ? true : false; + return this.getStatus() == StoragePoolStatus.PrepareForMaintenance || this.getStatus() == StoragePoolStatus.Maintenance || this.getStatus() == StoragePoolStatus.ErrorInMaintenance || this.getRemoved() != null; } @Override From c1ea61a0e736ce626c0d1f21e90cf9aaf7f89964 Mon Sep 17 00:00:00 2001 From: Sowmya Krishnan Date: Wed, 28 Aug 2013 23:19:23 +0530 Subject: [PATCH 125/251] CLOUDSTACK-4487 Fix adding Netscaler service provider if not already done Signed-off-by: venkataswamybabu budumuru --- tools/marvin/marvin/integration/lib/common.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index d71c749d35f..6ffe951e977 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -68,12 +68,6 @@ def add_netscaler(apiclient, zoneid, NSservice): if isinstance(physical_networks, list): physical_network = physical_networks[0] - netscaler = NetScaler.add( - apiclient, - NSservice, - physicalnetworkid=physical_network.id - ) - cmd = listNetworkServiceProviders.listNetworkServiceProvidersCmd() cmd.name = 'Netscaler' cmd.physicalnetworkid=physical_network.id @@ -81,6 +75,17 @@ def add_netscaler(apiclient, zoneid, NSservice): if isinstance(nw_service_providers, list): netscaler_provider = nw_service_providers[0] + else: + cmd1 = addNetworkServiceProvider.addNetworkServiceProviderCmd() + cmd1.name = 'Netscaler' + cmd1.physicalnetworkid = physical_network.id + netscaler_provider = apiclient.addNetworkServiceProvider(cmd1) + + netscaler = NetScaler.add( + apiclient, + NSservice, + physicalnetworkid=physical_network.id + ) if netscaler_provider.state != 'Enabled': cmd = updateNetworkServiceProvider.updateNetworkServiceProviderCmd() cmd.id = netscaler_provider.id From dfee47e3b6c35d08cba7f28433b9e1bce12b4078 Mon Sep 17 00:00:00 2001 From: sanjeevneelarapu Date: Wed, 4 Sep 2013 22:37:15 +0530 Subject: [PATCH 126/251] CLOUDSTACK-4203: Adding a test for migrating volumes of stopped vms to test_stopped_vm.py Signed-off-by: sanjeevneelarapu Signed-off-by: venkataswamybabu budumuru s --- test/integration/component/test_stopped_vm.py | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/test/integration/component/test_stopped_vm.py b/test/integration/component/test_stopped_vm.py index 41eeb46a95c..7903f0e8cbf 100644 --- a/test/integration/component/test_stopped_vm.py +++ b/test/integration/component/test_stopped_vm.py @@ -809,6 +809,154 @@ class TestDeployVM(cloudstackTestCase): ) return + @attr(tags = ["advanced", "eip", "advancedns", "basic", "sg"]) + def test_09_stop_vm_migrate_vol(self): + """Test Stopped Virtual Machine's ROOT volume migration + """ + + # Validate the following: + # 1. deploy Vm with startvm=true + # 2. Should not be able to login to the VM. + # 3. listVM command should return the deployed VM.State of this VM + # should be "Running". + # 4. Stop the vm + # 5.list primary storages in the cluster , should be more than one + # 6.Migrate voluem to another available primary storage + clusters = Cluster.list( + self.apiclient, + zoneid = self.zone.id + ) + self.assertEqual( + isinstance(clusters, list), + True, + "Check list response returns a valid list" + ) + i = 0 + for cluster in clusters : + storage_pools = StoragePool.list( + self.apiclient, + clusterid = cluster.id + ) + if len(storage_pools) > 1 : + self.cluster_id = cluster.id + i += 1 + break + if i == 0 : + self.skipTest("No cluster with more than one primary storage pool to perform migrate volume test") + + hosts = Host.list( + self.apiclient, + clusterid = self.cluster_id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + self.debug("Deploying instance on host: %s" % host.id) + self.debug("Deploying instance in the account: %s" % + self.account.name) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + diskofferingid=self.disk_offering.id, + hostid=host.id, + mode=self.zone.networktype + ) + + self.debug("Deployed instance in account: %s" % + self.account.name) + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.state, + "Running", + "VM should be in Running state after deployment" + ) + self.debug("Stopping instance: %s" % self.virtual_machine.name) + self.virtual_machine.stop(self.apiclient) + self.debug("Instance is stopped!") + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.state, + "Stopped", + "VM should be in Stopped state after stoping vm" + ) + volumes = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check volume list response returns a valid list" + ) + vol_response = volumes[0] + #get the storage name in which volume is stored + storage_name = vol_response.storage + storage_pools = StoragePool.list( + self.apiclient, + clusterid = self.cluster_id + ) + #Get storage pool to migrate volume + for spool in storage_pools: + if spool.name == storage_name: + continue + else: + self.storage_id = spool.id + self.storage_name = spool.name + break + self.debug("Migrating volume to storage pool: %s" % self.storage_name) + Volume.migrate( + self.apiclient, + storageid = self.storage_id, + volumeid = vol_response.id + ) + volume = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + volume[0].storage, + self.storage_name, + "Check volume migration response") + + return class TestDeployHaEnabledVM(cloudstackTestCase): From fb97e8e617393ac86924304f2765e933cfa30a6a Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 5 Sep 2013 23:47:10 +0200 Subject: [PATCH 127/251] CLOUDSTACK-4533: fix two usage issues (db.properties and log4j-cloud.xml) (1) Replacing db.properties with management server db.properties (2) Rename log4j-cloud_usage.xml to log4j-cloud.xml --- debian/cloudstack-usage.postinst | 12 +++++++++--- debian/rules | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/debian/cloudstack-usage.postinst b/debian/cloudstack-usage.postinst index 2e15d5d3a43..fa8650c53e6 100644 --- a/debian/cloudstack-usage.postinst +++ b/debian/cloudstack-usage.postinst @@ -27,11 +27,17 @@ case "$1" in cp -a /etc/cloud/management/db.properties /etc/cloudstack/usage/db.properties fi + # Replacing db.properties with management server db.properties + if [ -f "/etc/cloudstack/management/db.properties" ]; then + rm -rf /etc/cloudstack/usage/db.properties + ln -s /etc/cloudstack/management/db.properties /etc/cloudstack/usage/db.properties + fi + # We also retain the log4j configuration - if [ -f "/etc/cloud/usage/log4j-cloud_usage.xml" ]; then - cp -a /etc/cloud/usage/log4j-cloud_usage.xml /etc/cloudstack/usage/log4j-cloud_usage.xml + if [ -f "/etc/cloud/usage/log4j-cloud.xml" ]; then + cp -a /etc/cloud/usage/log4j-cloud.xml /etc/cloudstack/usage/log4j-cloud.xml fi ;; esac -exit 0 \ No newline at end of file +exit 0 diff --git a/debian/rules b/debian/rules index e5ff5484fe5..5e3d58c4da3 100755 --- a/debian/rules +++ b/debian/rules @@ -145,7 +145,8 @@ install: mkdir $(DESTDIR)/usr/share/$(PACKAGE)-usage/plugins install -D usage/target/cloud-usage-$(VERSION)-SNAPSHOT.jar $(DESTDIR)/usr/share/$(PACKAGE)-usage/lib/$(PACKAGE)-usage.jar install -D usage/target/dependencies/* $(DESTDIR)/usr/share/$(PACKAGE)-usage/lib/ - cp usage/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/usage/ + cp usage/target/transformed/db.properties $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/usage/ + cp usage/target/transformed/log4j-cloud_usage.xml $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/usage/log4j-cloud.xml install -D packaging/debian/init/cloud-usage $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-usage # cloudstack-awsapi From e1e6f93306959dc8799eca00df11587237f1b38d Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Thu, 5 Sep 2013 14:26:52 -0700 Subject: [PATCH 128/251] Revert "CLOUDSTACK-2792: Send "saved_password" to BACKUP router when reset password for user VM" This reverts commit 5a8a2a259ea6e049b3e5810ff3a432d6ca7767e1. We would fix it in another way, since mgmt server may get state updated in time. Conflicts: server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java --- .../network/router/VirtualNetworkApplianceManagerImpl.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index a143c0c2b23..816924fcce9 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -3378,12 +3378,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V // password should be set only on default network element if (password != null && nic.isDefaultNic()) { - String encodedPassword = PasswordGenerator.rot13(password); - // We would unset password for BACKUP router in the RvR, to prevent user from accidently reset the - // password again after BACKUP become MASTER - if (router.getIsRedundantRouter() && router.getRedundantState() != RedundantState.MASTER) { - encodedPassword = PasswordGenerator.rot13("saved_password"); - } + final String encodedPassword = PasswordGenerator.rot13(password); SavePasswordCommand cmd = new SavePasswordCommand(encodedPassword, nic.getIp4Address(), profile.getVirtualMachine().getHostName(), _networkModel.getExecuteInSeqNtwkElmtCmd()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, getRouterIpInNetwork(nic.getNetworkId(), router.getId())); From eb9ab676f6463df50e22460af2a3376f3206b0ee Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 5 Sep 2013 15:26:33 -0700 Subject: [PATCH 129/251] CLOUDSTACK-4089: zone wizard > hypervisor VMware > if zoneType is Basic, not show vSwitchType dropdown in Edit Traffic Type for Guest. --- ui/scripts/ui-custom/zoneWizard.js | 130 +++++++++++++++-------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index 695534ffe0a..bb33929f5ba 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -294,7 +294,7 @@ var trafficData = $trafficType.data('traffic-type-data') ? $trafficType.data('traffic-type-data') : {}; var hypervisor = getData($trafficType.closest('.zone-wizard')).zone.hypervisor; - + var zoneType = getData($trafficType.closest('.zone-wizard')).zone.networkType; var fields; if (hypervisor == 'VMware') { @@ -309,69 +309,71 @@ } }; - if($trafficType.hasClass('guest') || $trafficType.hasClass('public')) { - if(trafficData.vSwitchType == null) { - var useDvs = false; - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'vmware.use.dvswitch' - }, - async: false, - success: function(json) { - if (json.listconfigurationsresponse.configuration[0].value == 'true') { - useDvs = true; - } - } - }); - if (useDvs == true) { - var useNexusDvs = false; - $.ajax({ - url: createURL('listConfigurations'), - data: { - name: 'vmware.use.nexus.vswitch' - }, - async: false, - success: function(json) { - if (json.listconfigurationsresponse.configuration[0].value == 'true') { - useNexusDvs = true; - } - } - }); - if (useNexusDvs == true) { - trafficData.vSwitchType = 'nexusdvs'; - fields.vSwitchName.defaultValue = 'epp0'; - } else { - trafficData.vSwitchType = 'vmwaredvs'; - fields.vSwitchName.defaultValue = 'dvSwitch0'; - } - } else { //useDvs == false - trafficData.vSwitchType = 'vmwaresvs'; - fields.vSwitchName.defaultValue = 'vSwitch0'; - } - } - - $.extend(fields, { - vSwitchType: { - label: 'vSwitch Type', - select: function (args) { - args.response.success({ - data: [{ - id: 'nexusdvs', - description: 'Cisco Nexus 1000v Distributed Virtual Switch' - }, { - id: 'vmwaresvs', - description: 'VMware vNetwork Standard Virtual Switch' - }, { - id: 'vmwaredvs', - description: 'VMware vNetwork Distributed Virtual Switch' - }] - }); - }, - defaultValue: trafficData.vSwitchType - } - }); - } + if(zoneType == 'Advanced') { + if($trafficType.hasClass('guest') || $trafficType.hasClass('public')) { + if(trafficData.vSwitchType == null) { + var useDvs = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.dvswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + useDvs = true; + } + } + }); + if (useDvs == true) { + var useNexusDvs = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'vmware.use.nexus.vswitch' + }, + async: false, + success: function(json) { + if (json.listconfigurationsresponse.configuration[0].value == 'true') { + useNexusDvs = true; + } + } + }); + if (useNexusDvs == true) { + trafficData.vSwitchType = 'nexusdvs'; + fields.vSwitchName.defaultValue = 'epp0'; + } else { + trafficData.vSwitchType = 'vmwaredvs'; + fields.vSwitchName.defaultValue = 'dvSwitch0'; + } + } else { //useDvs == false + trafficData.vSwitchType = 'vmwaresvs'; + fields.vSwitchName.defaultValue = 'vSwitch0'; + } + } + + $.extend(fields, { + vSwitchType: { + label: 'vSwitch Type', + select: function (args) { + args.response.success({ + data: [{ + id: 'nexusdvs', + description: 'Cisco Nexus 1000v Distributed Virtual Switch' + }, { + id: 'vmwaresvs', + description: 'VMware vNetwork Standard Virtual Switch' + }, { + id: 'vmwaredvs', + description: 'VMware vNetwork Distributed Virtual Switch' + }] + }); + }, + defaultValue: trafficData.vSwitchType + } + }); + } + } } else { fields = { label: { From daaec56d400cc37e0e43459d237ae13f4f7d0044 Mon Sep 17 00:00:00 2001 From: sanjeevneelarapu Date: Wed, 24 Jul 2013 22:12:22 +0530 Subject: [PATCH 130/251] CLOUDSTACK-702: Tests for Multiple IP Ranges 1. Removed advanced tag from all the tests. At the movement scripts support only basic zone 2. Added a test to deploy guest vm after adding ip range in new CIDR CLOUDSTACK-702: Removed test for deploying vm after adding new cidr 1.This test would block parallel execution of remianing tests 2.Removed list_Routers proc and unused code 3.Added few more debug statements Signed-off-by: sanjeevneelarapu Signed-off-by: venkataswamybabu budumuru --- .../component/test_multiple_ip_ranges.py | 75 ++++++++++++------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/test/integration/component/test_multiple_ip_ranges.py b/test/integration/component/test_multiple_ip_ranges.py index 18409c55cff..aae90c4cd45 100644 --- a/test/integration/component/test_multiple_ip_ranges.py +++ b/test/integration/component/test_multiple_ip_ranges.py @@ -58,14 +58,17 @@ class Services: "ostype": "CentOS 5.3 (64-bit)", "templatefilter": 'self', }, - "vlan_ip_range": { + "vlan_ip_range": { "startip": "", "endip": "", "netmask": "", "gateway": "", "forvirtualnetwork": "false", "vlan": "untagged", - } + }, + "ostype": "CentOS 5.3 (64-bit)", + "sleep": 60, + "timeout": 10, } class TestMultipleIpRanges(cloudstackTestCase): @@ -90,6 +93,21 @@ class TestMultipleIpRanges(cloudstackTestCase): domainid=cls.domain.id ) cls.services["account"] = cls.account.name + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["templates"]["ostypeid"] = cls.template.ostypeid + cls.services["diskoffering"] = cls.disk_offering.id cls._cleanup = [ cls.account, ] @@ -166,7 +184,7 @@ class TestMultipleIpRanges(cloudstackTestCase): ) return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_01_add_ip_same_cidr(self): """Test add guest ip range in the existing cidr """ @@ -188,6 +206,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["zoneid"] = self.zone.id self.services["vlan_ip_range"]["podid"] = self.pod.id #create new vlan ip range + self.debug("Creating new ip range with new cidr in the same vlan") new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) self.cleanup.append(new_vlan) @@ -197,6 +216,7 @@ class TestMultipleIpRanges(cloudstackTestCase): #Add few more ips in the same CIDR self.services["vlan_ip_range"]["startip"] = test_startIp2 self.services["vlan_ip_range"]["endip"] = test_endIp2 + self.debug("Creating new ip range in the existing CIDR") new_vlan2 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp2,test_endIp2)) self.cleanup.append(new_vlan2) @@ -206,7 +226,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.verify_vlan_range(new_vlan2_res,self.services["vlan_ip_range"]) return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_02_add_ip_diff_cidr(self): """Test add ip range in a new cidr @@ -230,6 +250,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["zoneid"] = self.zone.id self.services["vlan_ip_range"]["podid"] = self.pod.id #create new vlan ip range + self.debug("Adding new ip range in different CIDR in same vlan") new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) self.cleanup.append(new_vlan) @@ -238,7 +259,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.verify_vlan_range(new_vlan_res,self.services["vlan_ip_range"]) return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_03_del_ip_range(self): """Test delete ip range @@ -263,12 +284,14 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["zoneid"] = self.zone.id self.services["vlan_ip_range"]["podid"] = self.pod.id #create new vlan ip range + self.debug("Creating new ip range in the new cidr") new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) new_vlan_res = new_vlan.list(self.apiclient,id=new_vlan.vlan.id) #Compare list output with configured values self.verify_vlan_range(new_vlan_res,self.services["vlan_ip_range"]) #Delete the above IP range + self.debug("Deleting new ip range added in new cidr") new_vlan.delete(self.apiclient) #listing vlan ip ranges with the id should through exception , if not mark the test case as failed try: @@ -278,7 +301,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.assertTrue(cs.errorMsg.find("entity does not exist")>0, msg="Failed to delete IP range") return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_04_add_noncontiguous_ip_range(self): """Test adding non-contiguous ip range in existing cidr @@ -315,6 +338,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["startip"] = test_startIp2 self.services["vlan_ip_range"]["endip"] = test_endIp2 #create new vlan ip range + self.debug("Adding non contiguous ip range") new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) self.cleanup.append(new_vlan) @@ -323,7 +347,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.verify_vlan_range(new_vlan_res,self.services["vlan_ip_range"]) return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_05_add_overlapped_ip_range(self): """Test adding overlapped ip range in existing cidr @@ -337,9 +361,9 @@ class TestMultipleIpRanges(cloudstackTestCase): #Add IP range in the new CIDR test_gateway = ip.__add__(1) test_startIp = ip.__add__(10) - test_endIp = ip.__add__(100) - test_startIp2 = ip.__add__(90) - test_endIp2 = ip.__add__(150) + test_endIp = ip.__add__(30) + test_startIp2 = ip.__add__(20) + test_endIp2 = ip.__add__(40) #Populating services with new IP range self.services["vlan_ip_range"]["startip"] = test_startIp self.services["vlan_ip_range"]["endip"] = test_endIp @@ -348,6 +372,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["zoneid"] = self.zone.id self.services["vlan_ip_range"]["podid"] = self.pod.id #create new vlan ip range + self.debug("Creating new ip range with startip:%s and endip: %s".format(test_startIp,test_endIp)) new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) self.cleanup.append(new_vlan) @@ -359,6 +384,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["startip"] = test_startIp2 self.services["vlan_ip_range"]["endip"] = test_endIp2 #Try to create ip range overlapped with exiting ip range + self.debug("Adding overlapped ip range") try: new_vlan2 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) except cloudstackAPIException as cs: @@ -370,9 +396,9 @@ class TestMultipleIpRanges(cloudstackTestCase): self.fail("CS should not accept overlapped ip ranges in guest traffic, but it allowed") return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_06_add_ip_range_overlapped_with_two_ranges(self): - """Test adding overlapped ip range in existing cidr + """Test adding overlapped ip range with two existing cidr 1.Add ip range in new cidr e.g:10.147.40.2-10.147.40.10 2.Add another ip range in the same cidr e.g:10.147.40.20-10.147.40.30 @@ -385,11 +411,11 @@ class TestMultipleIpRanges(cloudstackTestCase): #Add IP range in the new CIDR test_gateway = ip.__add__(1) test_startIp = ip.__add__(2) - test_endIp = ip.__add__(10) - test_startIp2 = ip.__add__(20) - test_endIp2 = ip.__add__(30) - test_startIp3 = ip.__add__(10) - test_endIp3 = ip.__add__(20) + test_endIp = ip.__add__(5) + test_startIp2 = ip.__add__(7) + test_endIp2 = ip.__add__(10) + test_startIp3 = ip.__add__(5) + test_endIp3 = ip.__add__(7) #Populating services with new IP range self.services["vlan_ip_range"]["startip"] = test_startIp self.services["vlan_ip_range"]["endip"] = test_endIp @@ -410,14 +436,11 @@ class TestMultipleIpRanges(cloudstackTestCase): new_vlan2 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp2,test_endIp2)) self.cleanup.append(new_vlan2) - new_vlan_res = new_vlan.list(self.apiclient,id=new_vlan.vlan.id) - #Compare list output with configured values - self.verify_vlan_range(new_vlan_res,self.services["vlan_ip_range"]) - #Add ip range which will overlap with two existing ip ranges in the same CIDR #Populating services with new IP range self.services["vlan_ip_range"]["startip"] = test_startIp3 self.services["vlan_ip_range"]["endip"] = test_endIp3 #Try to create ip range overlapped with exiting ip range + self.debug("Adding ip range overlapped with two cidrs") try: new_vlan3 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) except cloudstackAPIException as cs: @@ -429,7 +452,7 @@ class TestMultipleIpRanges(cloudstackTestCase): self.fail("CS should not accept overlapped ip ranges in guest traffic, but it allowed") return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_07_add_iprange_superset(self): """Test adding ip range superset to existing CIDR @@ -470,18 +493,19 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["netmask"] = superset self.services["vlan_ip_range"]["startip"] = test_startIp2 self.services["vlan_ip_range"]["endip"] = test_endIp2 + self.debug("Adding IP range super set to existing CIDR") try: new_vlan2 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) except cloudstackAPIException as cs: self.debug(cs.errorMsg) - self.assertTrue(cs.errorMsg.find("new subnet is a super set of the existing subnet")>0, msg="Fail: CS allowed adding ip range superset to existing CIDR") + self.assertTrue(cs.errorMsg.find("superset")>0, msg="Fail: CS allowed adding ip range superset to existing CIDR") return #Test will reach here if there is a bug in allowing superset ip range self.cleanup.append(new_vlan2) self.fail("CS should not allow adding ip range superset to existing CIDR") return - @attr(tags=["advanced_sg", "sg"]) + @attr(tags=["sg"]) def test_08_add_iprange_subset(self): """Test adding ip range subset to existing CIDR @@ -522,11 +546,12 @@ class TestMultipleIpRanges(cloudstackTestCase): self.services["vlan_ip_range"]["netmask"] = subset self.services["vlan_ip_range"]["startip"] = test_startIp2 self.services["vlan_ip_range"]["endip"] = test_endIp2 + self.debug("Adding ip range subset to existing cidr") try: new_vlan2 = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) except cloudstackAPIException as cs: self.debug(cs.errorMsg) - self.assertTrue(cs.errorMsg.find("new subnet is a subset of the existing subnet")>0, msg="Fail: CS allowed adding ip range subset to existing CIDR") + self.assertTrue(cs.errorMsg.find("subset")>0, msg="Fail: CS allowed adding ip range subset to existing CIDR") return #Test will reach here if there is a bug in allowing superset ip range self.cleanup.append(new_vlan2) From bd652416608cc63555f6cecff5a610ebacdab9d3 Mon Sep 17 00:00:00 2001 From: Jayapal Date: Fri, 6 Sep 2013 15:28:37 +0530 Subject: [PATCH 131/251] CLOUDSTACK-4613 correcting anti spoofing security group rules --- scripts/vm/hypervisor/xenserver/vmops | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index f53a9674acd..83efc67b002 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -1072,12 +1072,12 @@ def network_rules_for_rebooted_vm(session, vmName): #change antispoof rule in vmchain try: - delcmd = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-in | sed 's/-A/-D/'" - delcmd2 = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-out | sed 's/-A/-D/'" - inscmd = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-in | grep vif | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/-A/-I/'" - inscmd2 = "iptables-save| grep '\-A " + vmchain_default + "' | grep physdev-in | grep tap | sed -r 's/tap[0-9]+.0/" + tap + "/' | sed 's/-A/-I/'" - inscmd3 = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-out | grep vif | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/-A/-I/'" - inscmd4 = "iptables-save| grep '\-A " + vmchain_default + "' | grep physdev-out | grep tap | sed -r 's/tap[0-9]+.0/" + tap + "/' | sed 's/-A/-I/'" + delcmd = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-in | sed 's/!--set/! --set/' | sed 's/-A/-D/'" + delcmd2 = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-out | sed 's/!--set/! --set/'| sed 's/-A/-D/'" + inscmd = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-in | grep vif | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/!--set/! --set/'" + inscmd2 = "iptables-save| grep '\-A " + vmchain_default + "' | grep physdev-in | grep tap | sed -r 's/tap[0-9]+.0/" + tap + "/' | sed 's/!--set/! --set/'" + inscmd3 = "iptables-save | grep '\-A " + vmchain_default + "' | grep physdev-out | grep vif | sed -r 's/vif[0-9]+.0/" + vif + "/' | sed 's/!--set/! --set/'" + inscmd4 = "iptables-save| grep '\-A " + vmchain_default + "' | grep physdev-out | grep tap | sed -r 's/tap[0-9]+.0/" + tap + "/' | sed 's/!--set/! --set/'" ipts = [] for cmd in [delcmd, delcmd2, inscmd, inscmd2, inscmd3, inscmd4]: From add65051826eb0311a9028ecf3785617ea191733 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 6 Sep 2013 14:20:18 +0200 Subject: [PATCH 132/251] change display name in /etc/init.d/cloudstack-agent from cloud-agent to cloudstack-agent --- packaging/debian/init/cloud-agent | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debian/init/cloud-agent b/packaging/debian/init/cloud-agent index c87a5c09f81..29f64881626 100755 --- a/packaging/debian/init/cloud-agent +++ b/packaging/debian/init/cloud-agent @@ -33,7 +33,7 @@ . /lib/lsb/init-functions -SHORTNAME="cloud-agent" +SHORTNAME="cloudstack-agent" PIDFILE=/var/run/"$SHORTNAME".pid LOCKFILE=/var/lock/subsys/"$SHORTNAME" PROGNAME="CloudStack Agent" From 751fe8e339750c79d9a30c28f7ef8b0b87bad10a Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Thu, 5 Sep 2013 19:27:56 -0400 Subject: [PATCH 133/251] CLOUDSTACK-4531: Fixed indent problem with the patch. Signed-off-by: venkataswamybabu budumuru --- tools/marvin/marvin/integration/lib/base.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index f3d57d861bd..fa4cc8202d2 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -410,16 +410,14 @@ class VirtualMachine: #program ssh access over NAT via PF if mode.lower() == 'advanced': cls.access_ssh_over_nat(apiclient, services, virtual_machine, allow_egress=allow_egress) - elif mode.lower() == 'basic': - - if virtual_machine.publicip is not None: - vm_ssh_ip = virtual_machine.publicip #EIP/ELB (netscaler) enabled zone - else: - vm_ssh_ip = virtual_machine.nic[0].ipaddress #regular basic zone with security group - + if virtual_machine.publicip is not None: + vm_ssh_ip = virtual_machine.publicip #EIP/ELB (netscaler) enabled zone + else: + vm_ssh_ip = virtual_machine.nic[0].ipaddress #regular basic zone with security group virtual_machine.ssh_ip = vm_ssh_ip virtual_machine.public_ip = vm_ssh_ip + return VirtualMachine(virtual_machine.__dict__, services) def start(self, apiclient): From 3f492ca2ea24c74e9628d015ddc2b8b8387ce6b4 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 6 Sep 2013 10:27:52 -0700 Subject: [PATCH 134/251] CLOUDSTACK-4619: UI > zone detail page > check if the zone has any cluster whose hypervisor is VMware. If not, skip calling listVmwareDcs API. --- ui/scripts/system.js | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8b092600f27..32d0e56eb64 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -6096,22 +6096,39 @@ }); $.ajax({ - url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild - data: { + url: createURL('listClusters'), + data: { zoneid: args.context.physicalResources[0].id }, async: false, - success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } - var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; - if (vmwaredcs != null) { - selectedZoneObj.vmwaredcName = vmwaredcs[0].name; - selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; - selectedZoneObj.vmwaredcId = vmwaredcs[0].id; - } - }, - error: function(XMLHttpResponse) {} //override default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); - }); - + success: function(json) { + var clusters = json.listclustersresponse.cluster; + if (clusters != null) { + for (var i = 0; i < clusters.length; i++) { + if (clusters[i].hypervisortype == 'VMware') { + $.ajax({ + url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild + data: { + zoneid: args.context.physicalResources[0].id + }, + async: false, + success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } + var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; + if (vmwaredcs != null) { + selectedZoneObj.vmwaredcName = vmwaredcs[0].name; + selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; + selectedZoneObj.vmwaredcId = vmwaredcs[0].id; + } + } + //, error: function(XMLHttpResponse) {} //override default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); + }); + + break; + } + } + } + } + }); // for testing only (begin) /* selectedZoneObj.vmwaredcName = "datacenter"; From f05d6a5419eb725615059b95bec5b01bcb277a19 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Fri, 6 Sep 2013 14:33:34 -0700 Subject: [PATCH 135/251] CLOUDSTACK-4190:[Object_store_refactor] volume should be deleted from staging storage after successfule volume migration, specially handle DeleteVolume command for Vmware volume structure in secondary storage. --- .../resource/NfsSecondaryStorageResource.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 cf4369c9e44..3e19b641256 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 @@ -1775,7 +1775,15 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S parent += File.separator; } String absoluteVolumePath = parent + relativeVolumePath; - File tmpltParent = new File(absoluteVolumePath).getParentFile(); + File volPath = new File(absoluteVolumePath); + File tmpltParent = null; + if (volPath.exists() && volPath.isDirectory()) { + // for vmware, absoluteVolumePath represents a directory where volume files are located. + tmpltParent = volPath; + } else{ + // for other hypervisors, the volume .vhd or .qcow2 file path is passed + tmpltParent = new File(absoluteVolumePath).getParentFile(); + } String details = null; if (!tmpltParent.exists()) { details = "volume parent directory " + tmpltParent.getName() + " doesn't exist"; @@ -1806,7 +1814,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S if (!f.delete()) { return new Answer(cmd, false, "Unable to delete file " + f.getName() + " under Volume path " - + relativeVolumePath); + + tmpltParent.getPath()); } } if (!found) { @@ -1816,7 +1824,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S } if (!tmpltParent.delete()) { details = "Unable to delete directory " + tmpltParent.getName() + " under Volume path " - + relativeVolumePath; + + tmpltParent.getPath(); s_logger.debug(details); return new Answer(cmd, false, details); } From 80aa0d36086fce90353fce91301496f2a44e137d Mon Sep 17 00:00:00 2001 From: Min Chen Date: Fri, 6 Sep 2013 17:27:30 -0700 Subject: [PATCH 136/251] CLOUDSTACK-4625:Snapshots and templates should be deleted from staging storage after create template from snapshot on S3. --- .../motion/AncientDataMotionStrategy.java | 5 +++++ .../VmwareStorageSubsystemCommandHandler.java | 10 +++++++++- .../resource/NfsSecondaryStorageResource.java | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 4aa01476527..1d579219e19 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -439,6 +439,11 @@ public class CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromsnapshotwait, _mgmtServer.getExecuteInSequence()); EndPoint ep = selector.select(srcData, destData); Answer answer = ep.sendMessage(cmd); + + // clean up snapshot copied to staging + if (srcData != null) { + cacheMgr.deleteCacheObject(srcData); + } return answer; } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java index f2ba492c7b9..ba72e8ff167 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java @@ -102,7 +102,15 @@ public class VmwareStorageSubsystemCommandHandler extends StorageSubsystemComman TemplateObjectTO template = (TemplateObjectTO)answer.getNewData(); template.setDataStore(srcDataStore); CopyCommand newCmd = new CopyCommand(template, destData, cmd.getWait(), cmd.executeInSequence()); - return storageResource.defaultAction(newCmd); + Answer result = storageResource.defaultAction(newCmd); + //clean up template data on staging area + try { + DeleteCommand deleteCommand = new DeleteCommand(template); + storageResource.defaultAction(deleteCommand); + } catch (Exception e) { + s_logger.debug("Failed to clean up staging area:", e); + } + return result; } needDelegation = true; } 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 3e19b641256..3a563cb6655 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 @@ -526,6 +526,14 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S SwiftUtil.putObject(swift, properties, containterName, _tmpltpp); } + //clean up template data on staging area + try { + DeleteCommand deleteCommand = new DeleteCommand(newTemplate); + execute(deleteCommand); + } catch (Exception e) { + s_logger.debug("Failed to clean up staging area:", e); + } + TemplateObjectTO template = new TemplateObjectTO(); template.setPath(swiftPath); template.setSize(templateFile.length()); @@ -543,7 +551,15 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S TemplateObjectTO newTemplate = (TemplateObjectTO)answer.getNewData(); newTemplate.setDataStore(srcDataStore); CopyCommand newCpyCmd = new CopyCommand(newTemplate, destData, cmd.getWait(), cmd.executeInSequence()); - return copyFromNfsToS3(newCpyCmd); + Answer result = copyFromNfsToS3(newCpyCmd); + //clean up template data on staging area + try { + DeleteCommand deleteCommand = new DeleteCommand(newTemplate); + execute(deleteCommand); + } catch (Exception e) { + s_logger.debug("Failed to clean up staging area:", e); + } + return result; } } s_logger.debug("Failed to create templat from snapshot"); From f2c5b5fbfe45196dfad2821fca513ddd6efa25c9 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 6 Sep 2013 17:55:11 -0700 Subject: [PATCH 137/251] CLOUDSTACK-4618: fix CLVM --- .../kvm/storage/KVMStorageProcessor.java | 31 +++++++--- .../CloudStackPrimaryDataStoreDriverImpl.java | 57 ++++++++++++++++++- scripts/storage/qcow2/managesnapshot.sh | 4 +- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 99ea04fc1d7..c69f9b03963 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -147,8 +147,7 @@ public class KVMStorageProcessor implements StorageProcessor { DataTO destData = cmd.getDestTO(); TemplateObjectTO template = (TemplateObjectTO) srcData; DataStoreTO imageStore = template.getDataStore(); - TemplateObjectTO volume = (TemplateObjectTO) destData; - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) volume.getDataStore(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) destData.getDataStore(); if (!(imageStore instanceof NfsTO)) { return new CopyCmdAnswer("unsupported protocol"); @@ -197,19 +196,32 @@ public class KVMStorageProcessor implements StorageProcessor { KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, UUID.randomUUID().toString(), primaryPool); - TemplateObjectTO newTemplate = new TemplateObjectTO(); - newTemplate.setPath(primaryVol.getName()); + DataTO data = null; /** * Force the ImageFormat for RBD templates to RAW * */ - if (primaryPool.getType() == StoragePoolType.RBD) { - newTemplate.setFormat(ImageFormat.RAW); - } else { - newTemplate.setFormat(ImageFormat.QCOW2); + if (destData.getObjectType() == DataObjectType.TEMPLATE) { + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(primaryVol.getName()); + if (primaryPool.getType() == StoragePoolType.RBD) { + newTemplate.setFormat(ImageFormat.RAW); + } else { + newTemplate.setFormat(ImageFormat.QCOW2); + } + data = newTemplate; + } else if (destData.getObjectType() == DataObjectType.VOLUME) { + VolumeObjectTO volumeObjectTO = new VolumeObjectTO(); + volumeObjectTO.setPath(primaryVol.getName()); + if (primaryVol.getFormat() == PhysicalDiskFormat.RAW) + volumeObjectTO.setFormat(ImageFormat.RAW); + else if (primaryVol.getFormat() == PhysicalDiskFormat.QCOW2) { + volumeObjectTO.setFormat(ImageFormat.QCOW2); + } + data = volumeObjectTO; } - return new CopyCmdAnswer(newTemplate); + return new CopyCmdAnswer(data); } catch (CloudRuntimeException e) { return new CopyCmdAnswer(e.toString()); } finally { @@ -287,6 +299,7 @@ public class KVMStorageProcessor implements StorageProcessor { String templatePath = template.getPath(); if (primaryPool.getType() == StoragePoolType.CLVM) { + templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; vol = templateToPrimaryDownload(templatePath, primaryPool); } else { if (templatePath.contains("/mnt")) { diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 47cd1a83ef9..683239c1d1c 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -25,9 +25,13 @@ import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.dao.HostDao; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; +import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeManager; @@ -36,17 +40,24 @@ import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.template.TemplateManager; +import com.cloud.utils.NumbersUtil; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; import javax.inject.Inject; +import java.util.UUID; public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { private static final Logger s_logger = Logger.getLogger(CloudStackPrimaryDataStoreDriverImpl.class); @@ -72,7 +83,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri SnapshotManager snapshotMgr; @Inject EndPointSelector epSelector; - + @Inject + ConfigurationDao configDao; + @Inject + TemplateManager templateManager; + @Inject + TemplateDataFactory templateDataFactory; @Override public DataTO getTO(DataObject data) { return null; @@ -163,10 +179,49 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback) { + DataStore store = destData.getDataStore(); + if (store.getRole() == DataStoreRole.Primary) { + if ((srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE)) { + //For CLVM, we need to copy template to primary storage at all, just fake the copy result. + TemplateObjectTO templateObjectTO = new TemplateObjectTO(); + templateObjectTO.setPath(UUID.randomUUID().toString()); + templateObjectTO.setSize(srcdata.getSize()); + templateObjectTO.setPhysicalSize(srcdata.getSize()); + templateObjectTO.setFormat(Storage.ImageFormat.RAW); + CopyCmdAnswer answer = new CopyCmdAnswer(templateObjectTO); + CopyCommandResult result = new CopyCommandResult("", answer); + callback.complete(result); + } else if (srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME) { + //For CLVM, we need to pass template on secondary storage to hypervisor + String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); + int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, + Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue())); + StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); + DataStore imageStore = templateManager.getImageStore(storagePoolVO.getDataCenterId(), srcdata.getId()); + DataObject srcData = templateDataFactory.getTemplate(srcdata.getId(), imageStore); + + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _primaryStorageDownloadWait, true); + EndPoint ep = epSelector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); + CopyCommandResult result = new CopyCommandResult("", answer); + callback.complete(result); + } + } } @Override public boolean canCopy(DataObject srcData, DataObject destData) { + //BUG fix for CLOUDSTACK-4618 + DataStore store = destData.getDataStore(); + if (store.getRole() == DataStoreRole.Primary) { + if ((srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE) || + (srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME)) { + StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); + if (storagePoolVO != null && storagePoolVO.getPoolType() == Storage.StoragePoolType.CLVM) { + return true; + } + } + } return false; } diff --git a/scripts/storage/qcow2/managesnapshot.sh b/scripts/storage/qcow2/managesnapshot.sh index 368ff549ee6..42bd1eb2613 100755 --- a/scripts/storage/qcow2/managesnapshot.sh +++ b/scripts/storage/qcow2/managesnapshot.sh @@ -42,11 +42,11 @@ fi is_lv() { # Must be a block device - if [ -b "${1}" ]; then + if [ -b "${1}" -o -L "{1}" ]; then # But not a volume group or physical volume lvm vgs "${1}" > /dev/null 2>&1 && return 1 # And a logical volume - lvm lvs "${1}" > /dev/null 2>&1 && return 0 + lvm lvs "${1}" > /dev/null 2>&1 && return 1 fi return 0 } From b2c4529a8a5f1a62a607477ebceb9c19579b65c9 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Thu, 5 Sep 2013 22:45:40 -0600 Subject: [PATCH 138/251] Updating devcloud-kvm marvin config to enable InternalLbVM upon zone deployment. --- tools/devcloud-kvm/devcloud-kvm-advanced.cfg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/devcloud-kvm/devcloud-kvm-advanced.cfg b/tools/devcloud-kvm/devcloud-kvm-advanced.cfg index 45bdfceb0ef..8e778098c01 100644 --- a/tools/devcloud-kvm/devcloud-kvm-advanced.cfg +++ b/tools/devcloud-kvm/devcloud-kvm-advanced.cfg @@ -47,6 +47,10 @@ { "broadcastdomainrange": "ZONE", "name": "VpcVirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "InternalLbVm" } ] }, From 0a0ff45b16d340cf78924d39b6ce73b759e0a624 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 9 Sep 2013 11:41:13 +0530 Subject: [PATCH 139/251] CLOUDSTACK-4608: Resolve the hostname to fqdn from marvin config In case the marvin configuration does not contain fqdn information fetch and compare the fqdns of hostip from `cloud`.`host` with the fqdn available on the client. Signed-off-by: Prasanna Santhanam (cherry picked from commit 433d5839a196dd4a8ecc49abc192c580d79c175d) --- tools/marvin/marvin/integration/lib/utils.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index c2403d4fb0a..a6abe0665eb 100644 --- a/tools/marvin/marvin/integration/lib/utils.py +++ b/tools/marvin/marvin/integration/lib/utils.py @@ -25,6 +25,8 @@ import string import random import imaplib import email +import socket +import urlparse import datetime from marvin.cloudstackAPI import * from marvin.remoteSSHClient import remoteSSHClient @@ -154,16 +156,23 @@ def fetch_api_client(config_file='datacenterCfg'): ) ) -def get_host_credentials(config, hostname): - """Get login information for a host `hostname` from marvin's `config` +def get_host_credentials(config, hostip): + """Get login information for a host `hostip` (ipv4) from marvin's `config` @return the tuple username, password for the host else raise keyerror""" for zone in config.zones: for pod in zone.pods: for cluster in pod.clusters: for host in cluster.hosts: - if str(host.url).find(str(hostname)) > 0: - return host.username, host.password + if str(host.url).startswith('http'): + hostname = urlparse.urlsplit(str(host.url)).netloc + else: + hostname = str(host.url) + try: + if socket.getfqdn(hostip) == socket.getfqdn(hostname): + return host.username, host.password + except socket.error, e: + raise Exception("Unresolvable host %s error is %s" % (hostip, e)) raise KeyError("Please provide the marvin configuration file with credentials to your hosts") From 2e3104ac1e1592140b046e748017e60a0fdd167d Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 9 Sep 2013 13:46:25 -0700 Subject: [PATCH 140/251] CLOUDSTACK-4629: UI > Infrastructure > zone > Add UCS Manager > change label from URL to IP Address. --- ui/scripts/system.js | 57 +++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 32d0e56eb64..d677a49d879 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -6096,39 +6096,30 @@ }); $.ajax({ - url: createURL('listClusters'), + url: createURL('listApis'), data: { - zoneid: args.context.physicalResources[0].id - }, - async: false, - success: function(json) { - var clusters = json.listclustersresponse.cluster; - if (clusters != null) { - for (var i = 0; i < clusters.length; i++) { - if (clusters[i].hypervisortype == 'VMware') { - $.ajax({ - url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild - data: { - zoneid: args.context.physicalResources[0].id - }, - async: false, - success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } - var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; - if (vmwaredcs != null) { - selectedZoneObj.vmwaredcName = vmwaredcs[0].name; - selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; - selectedZoneObj.vmwaredcId = vmwaredcs[0].id; - } - } - //, error: function(XMLHttpResponse) {} //override default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); - }); - - break; - } - } - } - } - }); + name: 'listVmwareDcs' + }, + async: false, + success: function(json) { + $.ajax({ + url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild + data: { + zoneid: args.context.physicalResources[0].id + }, + async: false, + success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } + var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; + if (vmwaredcs != null) { + selectedZoneObj.vmwaredcName = vmwaredcs[0].name; + selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; + selectedZoneObj.vmwaredcId = vmwaredcs[0].id; + } + } + }); + }, + error: function(XMLHttpResponse) {} //suppress error dialog by overriding default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); + }); // for testing only (begin) /* selectedZoneObj.vmwaredcName = "datacenter"; @@ -13638,7 +13629,7 @@ } }, url: { - label: 'label.url', + label: 'label.ip', //CLOUDSTACK-4629 validation: { required: true } From 37c87a84fa49985c6bd2aade8a96f47daf6244c8 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 9 Sep 2013 14:00:26 -0700 Subject: [PATCH 141/251] CLOUDSTACK-4619: UI > Infrastructure > zone > Using listApis to check existence of listVmwareDcs API command will show an error in the log when listVmwareDcs API command doesn't exist (in oss build). So, still use listClusters to determine the existence of listVmwareDcs API command. --- ui/scripts/system.js | 55 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index d677a49d879..3f5a5d4f8e6 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -6096,30 +6096,39 @@ }); $.ajax({ - url: createURL('listApis'), + url: createURL('listClusters'), data: { - name: 'listVmwareDcs' - }, - async: false, - success: function(json) { - $.ajax({ - url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild - data: { - zoneid: args.context.physicalResources[0].id - }, - async: false, - success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } - var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; - if (vmwaredcs != null) { - selectedZoneObj.vmwaredcName = vmwaredcs[0].name; - selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; - selectedZoneObj.vmwaredcId = vmwaredcs[0].id; - } - } - }); - }, - error: function(XMLHttpResponse) {} //suppress error dialog by overriding default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); - }); + zoneid: args.context.physicalResources[0].id + }, + async: false, + success: function(json) { + var clusters = json.listclustersresponse.cluster; + if (clusters != null) { + for (var i = 0; i < clusters.length; i++) { + if (clusters[i].hypervisortype == 'VMware') { + $.ajax({ + url: createURL('listVmwareDcs'), //listVmwareDcs API exists in only non-oss bild + data: { + zoneid: args.context.physicalResources[0].id + }, + async: false, + success: function(json) { //e.g. json == { "listvmwaredcsresponse" { "count":1 ,"VMwareDC" [ {"id":"c3c2562d-65e9-4fc7-92e2-773c2efe8f37","zoneid":1,"name":"datacenter","vcenter":"10.10.20.20"} ] } } + var vmwaredcs = json.listvmwaredcsresponse.VMwareDC; + if (vmwaredcs != null) { + selectedZoneObj.vmwaredcName = vmwaredcs[0].name; + selectedZoneObj.vmwaredcVcenter = vmwaredcs[0].vcenter; + selectedZoneObj.vmwaredcId = vmwaredcs[0].id; + } + } + //, error: function(XMLHttpResponse) {} //override default error handling: cloudStack.dialog.notice({ message: parseXMLHttpResponse(XMLHttpResponse)}); + }); + + break; + } + } + } + } + }); // for testing only (begin) /* selectedZoneObj.vmwaredcName = "datacenter"; From cb170d6afdab20bd63f17728b8f04a05f14ebcec Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 9 Sep 2013 15:58:22 -0700 Subject: [PATCH 142/251] CLOUDSTACK-4627: fix NPE in vm migration --- server/src/com/cloud/storage/VolumeManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 4569202d812..232b640afd6 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -2657,7 +2657,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { public boolean canVmRestartOnAnotherServer(long vmId) { List vols = _volsDao.findCreatedByInstance(vmId); for (VolumeVO vol : vols) { - if (!vol.isRecreatable() && !vol.getPoolType().isShared()) { + StoragePoolVO storagePoolVO = _storagePoolDao.findById(vol.getPoolId()); + if (!vol.isRecreatable() && storagePoolVO != null && storagePoolVO.getPoolType() != null && !(storagePoolVO.getPoolType().isShared())) { return false; } } From e607998afaf091194906a5cae4463adbf9787c4c Mon Sep 17 00:00:00 2001 From: Min Chen Date: Mon, 9 Sep 2013 16:07:46 -0700 Subject: [PATCH 143/251] CLOUDSTACK-4628:[Automation] Failed to create template from snapshot with same name; after deleting the first one. --- .../cloudstack/storage/motion/AncientDataMotionStrategy.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 1d579219e19..dce8c4e6294 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -431,7 +431,9 @@ public class int _createprivatetemplatefromsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromSnapshotWait.getDefaultValue())); + boolean needCache = false; if (needCacheStorage(srcData, destData)) { + needCache = true; SnapshotInfo snapshot = (SnapshotInfo) srcData; srcData = cacheSnapshotChain(snapshot); } @@ -441,7 +443,7 @@ public class Answer answer = ep.sendMessage(cmd); // clean up snapshot copied to staging - if (srcData != null) { + if (needCache && srcData != null) { cacheMgr.deleteCacheObject(srcData); } return answer; From ab60e9eae9cd7b195f316fd5028ce33945b0d2fc Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Tue, 10 Sep 2013 11:54:32 +0530 Subject: [PATCH 144/251] CLOUDSTACK-4636: In a scaled up setup all Vm's in a cluster were stopped and/or started after management server restart Issue happens as there are more than one thread processing connect for a host simultaneously. The VM full sync. is not designed to work in this scenario and as a result user VMs may get stopped incorrectly. Direct agent scan task runs at regular intervals (direct.agent.scan.interval defaulted to 90 secs) and identifies hosts that needs to be processed for connect. In a normal scenario hosts mostly get connected within that interval and there are no issues. But if due to some reason the connect process takes more time and is not completed by the time next agent scan runs. In this case, based on the db. state same hosts may get picked up again. And then there will be situations where more than one thread is processing connect for the same host. The fix is to check if there is a thread already processing connect for a host and in this case all subsequent threads for that host will simply bail out. Also there may be a scenario where one thread already completed processing connect but another thread already got scheduled before that and will again repeat the same. This is also prevented by putting appropriate checks. --- .../cloud/agent/manager/AgentManagerImpl.java | 5 ++++- .../cloud/resource/ResourceManagerImpl.java | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index 572f78b86b6..a7073f47c0d 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -1376,7 +1376,10 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl public boolean tapLoadingAgents(Long hostId, TapAgentsAction action) { synchronized (_loadingAgents) { if (action == TapAgentsAction.Add) { - _loadingAgents.add(hostId); + if (_loadingAgents.contains(hostId)) + return false; + else + _loadingAgents.add(hostId); } else if (action == TapAgentsAction.Del) { _loadingAgents.remove(hostId); } else if (action == TapAgentsAction.Contains) { diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 9da8c28f26e..b8f6a5acd3a 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -1920,9 +1920,23 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, @Override public Host createHostAndAgent(Long hostId, ServerResource resource, Map details, boolean old, List hostTags, boolean forRebalance) { - _agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Add); - Host host = createHostAndAgent(resource, details, old, hostTags, forRebalance); - _agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Del); + Host host = null; + if (_agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Add)) { + try { + AgentAttache agentattache = _agentMgr.findAttache(hostId); + if (agentattache == null) { + s_logger.debug("Creating agent for host " + hostId); + host = createHostAndAgent(resource, details, old, hostTags, forRebalance); + s_logger.debug("Completed creating agent for host " + hostId); + } else { + s_logger.debug("Agent already created in another thread for host " + hostId + ", ignore this"); + } + } finally { + _agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Del); + } + } else { + s_logger.debug("Agent creation already getting processed in another thread for host " + hostId + ", ignore this"); + } return host; } From 13a0619732a34e0fc54b6078d789bde22e8cb0e8 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 5 Sep 2013 11:16:12 +0200 Subject: [PATCH 145/251] CLOUDSTACK-4610: fix incorrect value available_bytes of storage pool (cherry picked from commit 5c141a46fc3f684fd633d8b1b18f708ccccbcff4) --- .../cloud/hypervisor/kvm/resource/LibvirtComputingResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 3ee811ff835..6d2f10617be 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -2663,7 +2663,7 @@ ServerResource { Map tInfo = new HashMap(); ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(cmd, - storagepool.getCapacity(), storagepool.getUsed(), tInfo); + storagepool.getCapacity(), storagepool.getAvailable(), tInfo); return answer; } From 523cac4bceca33aab456baec89e1b138589245b1 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 10 Sep 2013 15:52:08 -0700 Subject: [PATCH 146/251] CLOUDSTACK-4642: UI > get complete timezone list from Java class TimeZone --- ui/scripts/sharedFunctions.js | 678 +++++++++++++++++++++++++++++++--- 1 file changed, 619 insertions(+), 59 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 3d45ad01c6a..0ae8644970e 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -1259,65 +1259,625 @@ var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { } var timezoneMap = new Object(); -timezoneMap['Etc/GMT+12'] = '[UTC-12:00] GMT-12:00'; -timezoneMap['Etc/GMT+11'] = '[UTC-11:00] GMT-11:00'; -timezoneMap['Pacific/Samoa'] = '[UTC-11:00] Samoa Standard Time'; -timezoneMap['Pacific/Honolulu'] = '[UTC-10:00] Hawaii Standard Time'; -timezoneMap['US/Alaska'] = '[UTC-09:00] Alaska Standard Time'; -timezoneMap['America/Los_Angeles'] = '[UTC-08:00] Pacific Standard Time'; -timezoneMap['Mexico/BajaNorte'] = '[UTC-08:00] Baja California'; -timezoneMap['US/Arizona'] = '[UTC-07:00] Arizona'; -timezoneMap['US/Mountain'] = '[UTC-07:00] Mountain Standard Time'; -timezoneMap['America/Chihuahua'] = '[UTC-07:00] Chihuahua, La Paz'; -timezoneMap['America/Chicago'] = '[UTC-06:00] Central Standard Time'; -timezoneMap['America/Costa_Rica'] = '[UTC-06:00] Central America'; -timezoneMap['America/Mexico_City'] = '[UTC-06:00] Mexico City, Monterrey'; -timezoneMap['Canada/Saskatchewan'] = '[UTC-06:00] Saskatchewan'; -timezoneMap['America/Bogota'] = '[UTC-05:00] Bogota, Lima'; -timezoneMap['America/New_York'] = '[UTC-05:00] Eastern Standard Time'; -timezoneMap['America/Caracas'] = '[UTC-04:00] Venezuela Time'; -timezoneMap['America/Asuncion'] = '[UTC-04:00] Paraguay Time'; -timezoneMap['America/Cuiaba'] = '[UTC-04:00] Amazon Time'; -timezoneMap['America/Halifax'] = '[UTC-04:00] Atlantic Standard Time'; -timezoneMap['America/La_Paz'] = '[UTC-04:00] Bolivia Time'; -timezoneMap['America/Santiago'] = '[UTC-04:00] Chile Time'; -timezoneMap['America/St_Johns'] = '[UTC-03:30] Newfoundland Standard Time'; -timezoneMap['America/Araguaina'] = '[UTC-03:00] Brasilia Time'; -timezoneMap['America/Argentina/Buenos_Aires'] = '[UTC-03:00] Argentine Time'; -timezoneMap['America/Cayenne'] = '[UTC-03:00] French Guiana Time'; -timezoneMap['America/Godthab'] = '[UTC-03:00] Greenland Time'; -timezoneMap['America/Montevideo'] = '[UTC-03:00] Uruguay Time]'; -timezoneMap['Etc/GMT+2'] = '[UTC-02:00] GMT-02:00'; -timezoneMap['Atlantic/Azores'] = '[UTC-01:00] Azores Time'; -timezoneMap['Atlantic/Cape_Verde'] = '[UTC-01:00] Cape Verde Time'; -timezoneMap['Africa/Casablanca'] = '[UTC] Casablanca'; -timezoneMap['Etc/UTC'] = '[UTC] Coordinated Universal Time'; -timezoneMap['Atlantic/Reykjavik'] = '[UTC] Reykjavik'; -timezoneMap['Europe/London'] = '[UTC] Western European Time'; -timezoneMap['CET'] = '[UTC+01:00] Central European Time'; -timezoneMap['Europe/Bucharest'] = '[UTC+02:00] Eastern European Time'; -timezoneMap['Africa/Johannesburg'] = '[UTC+02:00] South Africa Standard Time'; -timezoneMap['Asia/Beirut'] = '[UTC+02:00] Beirut'; -timezoneMap['Africa/Cairo'] = '[UTC+02:00] Cairo'; -timezoneMap['Asia/Jerusalem'] = '[UTC+02:00] Israel Standard Time'; -timezoneMap['Europe/Minsk'] = '[UTC+02:00] Minsk'; -timezoneMap['Europe/Moscow'] = '[UTC+03:00] Moscow Standard Time'; -timezoneMap['Africa/Nairobi'] = '[UTC+03:00] Eastern African Time'; -timezoneMap['Asia/Karachi'] = '[UTC+05:00] Pakistan Time'; -timezoneMap['Asia/Kolkata'] = '[UTC+05:30] India Standard Time'; -timezoneMap['Asia/Bangkok'] = '[UTC+05:30] Indochina Time'; -timezoneMap['Asia/Shanghai'] = '[UTC+08:00] China Standard Time'; -timezoneMap['Asia/Kuala_Lumpur'] = '[UTC+08:00] Malaysia Time'; -timezoneMap['Australia/Perth'] = '[UTC+08:00] Western Standard Time (Australia)'; -timezoneMap['Asia/Taipei'] = '[UTC+08:00] Taiwan'; -timezoneMap['Asia/Tokyo'] = '[UTC+09:00] Japan Standard Time'; -timezoneMap['Asia/Seoul'] = '[UTC+09:00] Korea Standard Time'; -timezoneMap['Australia/Adelaide'] = '[UTC+09:30] Central Standard Time (South Australia)'; -timezoneMap['Australia/Darwin'] = '[UTC+09:30] Central Standard Time (Northern Territory)'; -timezoneMap['Australia/Brisbane'] = '[UTC+10:00] Eastern Standard Time (Queensland)'; -timezoneMap['Australia/Canberra'] = '[UTC+10:00] Eastern Standard Time (New South Wales)'; -timezoneMap['Pacific/Guam'] = '[UTC+10:00] Chamorro Standard Time'; -timezoneMap['Pacific/Auckland'] = '[UTC+12:00] New Zealand Standard Time'; +timezoneMap["Etc/GMT+12"] = "Etc/GMT+12 [GMT-12:00]"; +timezoneMap["Etc/GMT+11"] = "Etc/GMT+11 [GMT-11:00]"; +timezoneMap["Pacific/Midway"] = "Pacific/Midway [Samoa Standard Time]"; +timezoneMap["Pacific/Niue"] = "Pacific/Niue [Niue Time]"; +timezoneMap["Pacific/Pago_Pago"] = "Pacific/Pago_Pago [Samoa Standard Time]"; +timezoneMap["Pacific/Samoa"] = "Pacific/Samoa [Samoa Standard Time]"; +timezoneMap["US/Samoa"] = "US/Samoa [Samoa Standard Time]"; +timezoneMap["America/Adak"] = "America/Adak [Hawaii-Aleutian Standard Time]"; +timezoneMap["America/Atka"] = "America/Atka [Hawaii-Aleutian Standard Time]"; +timezoneMap["Etc/GMT+10"] = "Etc/GMT+10 [GMT-10:00]"; +timezoneMap["HST"] = "HST [Hawaii Standard Time]"; +timezoneMap["Pacific/Honolulu"] = "Pacific/Honolulu [Hawaii Standard Time]"; +timezoneMap["Pacific/Johnston"] = "Pacific/Johnston [Hawaii Standard Time]"; +timezoneMap["Pacific/Rarotonga"] = "Pacific/Rarotonga [Cook Is. Time]"; +timezoneMap["Pacific/Tahiti"] = "Pacific/Tahiti [Tahiti Time]"; +timezoneMap["SystemV/HST10"] = "SystemV/HST10 [Hawaii Standard Time]"; +timezoneMap["US/Aleutian"] = "US/Aleutian [Hawaii-Aleutian Standard Time]"; +timezoneMap["US/Hawaii"] = "US/Hawaii [Hawaii Standard Time]"; +timezoneMap["Pacific/Marquesas"] = "Pacific/Marquesas [Marquesas Time]"; +timezoneMap["AST"] = "AST [Alaska Standard Time]"; +timezoneMap["America/Anchorage"] = "America/Anchorage [Alaska Standard Time]"; +timezoneMap["America/Juneau"] = "America/Juneau [Alaska Standard Time]"; +timezoneMap["America/Nome"] = "America/Nome [Alaska Standard Time]"; +timezoneMap["America/Sitka"] = "America/Sitka [GMT-09:00]"; +timezoneMap["America/Yakutat"] = "America/Yakutat [Alaska Standard Time]"; +timezoneMap["Etc/GMT+9"] = "Etc/GMT+9 [GMT-09:00]"; +timezoneMap["Pacific/Gambier"] = "Pacific/Gambier [Gambier Time]"; +timezoneMap["SystemV/YST9"] = "SystemV/YST9 [Alaska Standard Time]"; +timezoneMap["SystemV/YST9YDT"] = "SystemV/YST9YDT [Alaska Standard Time]"; +timezoneMap["US/Alaska"] = "US/Alaska [Alaska Standard Time]"; +timezoneMap["America/Dawson"] = "America/Dawson [Pacific Standard Time]"; +timezoneMap["America/Ensenada"] = "America/Ensenada [Pacific Standard Time]"; +timezoneMap["America/Los_Angeles"] = "America/Los_Angeles [Pacific Standard Time]"; +timezoneMap["America/Metlakatla"] = "America/Metlakatla [GMT-08:00]"; +timezoneMap["America/Santa_Isabel"] = "America/Santa_Isabel [Pacific Standard Time]"; +timezoneMap["America/Tijuana"] = "America/Tijuana [Pacific Standard Time]"; +timezoneMap["America/Vancouver"] = "America/Vancouver [Pacific Standard Time]"; +timezoneMap["America/Whitehorse"] = "America/Whitehorse [Pacific Standard Time]"; +timezoneMap["Canada/Pacific"] = "Canada/Pacific [Pacific Standard Time]"; +timezoneMap["Canada/Yukon"] = "Canada/Yukon [Pacific Standard Time]"; +timezoneMap["Etc/GMT+8"] = "Etc/GMT+8 [GMT-08:00]"; +timezoneMap["Mexico/BajaNorte"] = "Mexico/BajaNorte [Pacific Standard Time]"; +timezoneMap["PST"] = "PST [Pacific Standard Time]"; +timezoneMap["PST8PDT"] = "PST8PDT [Pacific Standard Time]"; +timezoneMap["Pacific/Pitcairn"] = "Pacific/Pitcairn [Pitcairn Standard Time]"; +timezoneMap["SystemV/PST8"] = "SystemV/PST8 [Pacific Standard Time]"; +timezoneMap["SystemV/PST8PDT"] = "SystemV/PST8PDT [Pacific Standard Time]"; +timezoneMap["US/Pacific"] = "US/Pacific [Pacific Standard Time]"; +timezoneMap["US/Pacific-New"] = "US/Pacific-New [Pacific Standard Time]"; +timezoneMap["America/Boise"] = "America/Boise [Mountain Standard Time]"; +timezoneMap["America/Cambridge_Bay"] = "America/Cambridge_Bay [Mountain Standard Time]"; +timezoneMap["America/Chihuahua"] = "America/Chihuahua [Mountain Standard Time]"; +timezoneMap["America/Creston"] = "America/Creston [GMT-07:00]"; +timezoneMap["America/Dawson_Creek"] = "America/Dawson_Creek [Mountain Standard Time]"; +timezoneMap["America/Denver"] = "America/Denver [Mountain Standard Time]"; +timezoneMap["America/Edmonton"] = "America/Edmonton [Mountain Standard Time]"; +timezoneMap["America/Hermosillo"] = "America/Hermosillo [Mountain Standard Time]"; +timezoneMap["America/Inuvik"] = "America/Inuvik [Mountain Standard Time]"; +timezoneMap["America/Mazatlan"] = "America/Mazatlan [Mountain Standard Time]"; +timezoneMap["America/Ojinaga"] = "America/Ojinaga [Mountain Standard Time]"; +timezoneMap["America/Phoenix"] = "America/Phoenix [Mountain Standard Time]"; +timezoneMap["America/Shiprock"] = "America/Shiprock [Mountain Standard Time]"; +timezoneMap["America/Yellowknife"] = "America/Yellowknife [Mountain Standard Time]"; +timezoneMap["Canada/Mountain"] = "Canada/Mountain [Mountain Standard Time]"; +timezoneMap["Etc/GMT+7"] = "Etc/GMT+7 [GMT-07:00]"; +timezoneMap["MST"] = "MST [Mountain Standard Time]"; +timezoneMap["MST7MDT"] = "MST7MDT [Mountain Standard Time]"; +timezoneMap["Mexico/BajaSur"] = "Mexico/BajaSur [Mountain Standard Time]"; +timezoneMap["Navajo"] = "Navajo [Mountain Standard Time]"; +timezoneMap["PNT"] = "PNT [Mountain Standard Time]"; +timezoneMap["SystemV/MST7"] = "SystemV/MST7 [Mountain Standard Time]"; +timezoneMap["SystemV/MST7MDT"] = "SystemV/MST7MDT [Mountain Standard Time]"; +timezoneMap["US/Arizona"] = "US/Arizona [Mountain Standard Time]"; +timezoneMap["US/Mountain"] = "US/Mountain [Mountain Standard Time]"; +timezoneMap["America/Bahia_Banderas"] = "America/Bahia_Banderas [GMT-06:00]"; +timezoneMap["America/Belize"] = "America/Belize [Central Standard Time]"; +timezoneMap["America/Cancun"] = "America/Cancun [Central Standard Time]"; +timezoneMap["America/Chicago"] = "America/Chicago [Central Standard Time]"; +timezoneMap["America/Costa_Rica"] = "America/Costa_Rica [Central Standard Time]"; +timezoneMap["America/El_Salvador"] = "America/El_Salvador [Central Standard Time]"; +timezoneMap["America/Guatemala"] = "America/Guatemala [Central Standard Time]"; +timezoneMap["America/Indiana/Knox"] = "America/Indiana/Knox [Central Standard Time]"; +timezoneMap["America/Indiana/Tell_City"] = "America/Indiana/Tell_City [Central Standard Time]"; +timezoneMap["America/Knox_IN"] = "America/Knox_IN [Central Standard Time]"; +timezoneMap["America/Managua"] = "America/Managua [Central Standard Time]"; +timezoneMap["America/Matamoros"] = "America/Matamoros [Central Standard Time]"; +timezoneMap["America/Menominee"] = "America/Menominee [Central Standard Time]"; +timezoneMap["America/Merida"] = "America/Merida [Central Standard Time]"; +timezoneMap["America/Mexico_City"] = "America/Mexico_City [Central Standard Time]"; +timezoneMap["America/Monterrey"] = "America/Monterrey [Central Standard Time]"; +timezoneMap["America/North_Dakota/Beulah"] = "America/North_Dakota/Beulah [GMT-06:00]"; +timezoneMap["America/North_Dakota/Center"] = "America/North_Dakota/Center [Central Standard Time]"; +timezoneMap["America/North_Dakota/New_Salem"] = "America/North_Dakota/New_Salem [Central Standard Time]"; +timezoneMap["America/Rainy_River"] = "America/Rainy_River [Central Standard Time]"; +timezoneMap["America/Rankin_Inlet"] = "America/Rankin_Inlet [Central Standard Time]"; +timezoneMap["America/Regina"] = "America/Regina [Central Standard Time]"; +timezoneMap["America/Resolute"] = "America/Resolute [Eastern Standard Time]"; +timezoneMap["America/Swift_Current"] = "America/Swift_Current [Central Standard Time]"; +timezoneMap["America/Tegucigalpa"] = "America/Tegucigalpa [Central Standard Time]"; +timezoneMap["America/Winnipeg"] = "America/Winnipeg [Central Standard Time]"; +timezoneMap["CST"] = "CST [Central Standard Time]"; +timezoneMap["CST6CDT"] = "CST6CDT [Central Standard Time]"; +timezoneMap["Canada/Central"] = "Canada/Central [Central Standard Time]"; +timezoneMap["Canada/East-Saskatchewan"] = "Canada/East-Saskatchewan [Central Standard Time]"; +timezoneMap["Canada/Saskatchewan"] = "Canada/Saskatchewan [Central Standard Time]"; +timezoneMap["Chile/EasterIsland"] = "Chile/EasterIsland [Easter Is. Time]"; +timezoneMap["Etc/GMT+6"] = "Etc/GMT+6 [GMT-06:00]"; +timezoneMap["Mexico/General"] = "Mexico/General [Central Standard Time]"; +timezoneMap["Pacific/Easter"] = "Pacific/Easter [Easter Is. Time]"; +timezoneMap["Pacific/Galapagos"] = "Pacific/Galapagos [Galapagos Time]"; +timezoneMap["SystemV/CST6"] = "SystemV/CST6 [Central Standard Time]"; +timezoneMap["SystemV/CST6CDT"] = "SystemV/CST6CDT [Central Standard Time]"; +timezoneMap["US/Central"] = "US/Central [Central Standard Time]"; +timezoneMap["US/Indiana-Starke"] = "US/Indiana-Starke [Central Standard Time]"; +timezoneMap["America/Atikokan"] = "America/Atikokan [Eastern Standard Time]"; +timezoneMap["America/Bogota"] = "America/Bogota [Colombia Time]"; +timezoneMap["America/Cayman"] = "America/Cayman [Eastern Standard Time]"; +timezoneMap["America/Coral_Harbour"] = "America/Coral_Harbour [Eastern Standard Time]"; +timezoneMap["America/Detroit"] = "America/Detroit [Eastern Standard Time]"; +timezoneMap["America/Fort_Wayne"] = "America/Fort_Wayne [Eastern Standard Time]"; +timezoneMap["America/Grand_Turk"] = "America/Grand_Turk [Eastern Standard Time]"; +timezoneMap["America/Guayaquil"] = "America/Guayaquil [Ecuador Time]"; +timezoneMap["America/Havana"] = "America/Havana [Cuba Standard Time]"; +timezoneMap["America/Indiana/Indianapolis"] = "America/Indiana/Indianapolis [Eastern Standard Time]"; +timezoneMap["America/Indiana/Marengo"] = "America/Indiana/Marengo [Eastern Standard Time]"; +timezoneMap["America/Indiana/Petersburg"] = "America/Indiana/Petersburg [Eastern Standard Time]"; +timezoneMap["America/Indiana/Vevay"] = "America/Indiana/Vevay [Eastern Standard Time]"; +timezoneMap["America/Indiana/Vincennes"] = "America/Indiana/Vincennes [Eastern Standard Time]"; +timezoneMap["America/Indiana/Winamac"] = "America/Indiana/Winamac [Eastern Standard Time]"; +timezoneMap["America/Indianapolis"] = "America/Indianapolis [Eastern Standard Time]"; +timezoneMap["America/Iqaluit"] = "America/Iqaluit [Eastern Standard Time]"; +timezoneMap["America/Jamaica"] = "America/Jamaica [Eastern Standard Time]"; +timezoneMap["America/Kentucky/Louisville"] = "America/Kentucky/Louisville [Eastern Standard Time]"; +timezoneMap["America/Kentucky/Monticello"] = "America/Kentucky/Monticello [Eastern Standard Time]"; +timezoneMap["America/Lima"] = "America/Lima [Peru Time]"; +timezoneMap["America/Louisville"] = "America/Louisville [Eastern Standard Time]"; +timezoneMap["America/Montreal"] = "America/Montreal [Eastern Standard Time]"; +timezoneMap["America/Nassau"] = "America/Nassau [Eastern Standard Time]"; +timezoneMap["America/New_York"] = "America/New_York [Eastern Standard Time]"; +timezoneMap["America/Nipigon"] = "America/Nipigon [Eastern Standard Time]"; +timezoneMap["America/Panama"] = "America/Panama [Eastern Standard Time]"; +timezoneMap["America/Pangnirtung"] = "America/Pangnirtung [Eastern Standard Time]"; +timezoneMap["America/Port-au-Prince"] = "America/Port-au-Prince [Eastern Standard Time]"; +timezoneMap["America/Thunder_Bay"] = "America/Thunder_Bay [Eastern Standard Time]"; +timezoneMap["America/Toronto"] = "America/Toronto [Eastern Standard Time]"; +timezoneMap["Canada/Eastern"] = "Canada/Eastern [Eastern Standard Time]"; +timezoneMap["Cuba"] = "Cuba [Cuba Standard Time]"; +timezoneMap["EST"] = "EST [Eastern Standard Time]"; +timezoneMap["EST5EDT"] = "EST5EDT [Eastern Standard Time]"; +timezoneMap["Etc/GMT+5"] = "Etc/GMT+5 [GMT-05:00]"; +timezoneMap["IET"] = "IET [Eastern Standard Time]"; +timezoneMap["Jamaica"] = "Jamaica [Eastern Standard Time]"; +timezoneMap["SystemV/EST5"] = "SystemV/EST5 [Eastern Standard Time]"; +timezoneMap["SystemV/EST5EDT"] = "SystemV/EST5EDT [Eastern Standard Time]"; +timezoneMap["US/East-Indiana"] = "US/East-Indiana [Eastern Standard Time]"; +timezoneMap["US/Eastern"] = "US/Eastern [Eastern Standard Time]"; +timezoneMap["US/Michigan"] = "US/Michigan [Eastern Standard Time]"; +timezoneMap["America/Caracas"] = "America/Caracas [Venezuela Time]"; +timezoneMap["America/Anguilla"] = "America/Anguilla [Atlantic Standard Time]"; +timezoneMap["America/Antigua"] = "America/Antigua [Atlantic Standard Time]"; +timezoneMap["America/Argentina/San_Luis"] = "America/Argentina/San_Luis [Western Argentine Time]"; +timezoneMap["America/Aruba"] = "America/Aruba [Atlantic Standard Time]"; +timezoneMap["America/Asuncion"] = "America/Asuncion [Paraguay Time]"; +timezoneMap["America/Barbados"] = "America/Barbados [Atlantic Standard Time]"; +timezoneMap["America/Blanc-Sablon"] = "America/Blanc-Sablon [Atlantic Standard Time]"; +timezoneMap["America/Boa_Vista"] = "America/Boa_Vista [Amazon Time]"; +timezoneMap["America/Campo_Grande"] = "America/Campo_Grande [Amazon Time]"; +timezoneMap["America/Cuiaba"] = "America/Cuiaba [Amazon Time]"; +timezoneMap["America/Curacao"] = "America/Curacao [Atlantic Standard Time]"; +timezoneMap["America/Dominica"] = "America/Dominica [Atlantic Standard Time]"; +timezoneMap["America/Eirunepe"] = "America/Eirunepe [Amazon Time]"; +timezoneMap["America/Glace_Bay"] = "America/Glace_Bay [Atlantic Standard Time]"; +timezoneMap["America/Goose_Bay"] = "America/Goose_Bay [Atlantic Standard Time]"; +timezoneMap["America/Grenada"] = "America/Grenada [Atlantic Standard Time]"; +timezoneMap["America/Guadeloupe"] = "America/Guadeloupe [Atlantic Standard Time]"; +timezoneMap["America/Guyana"] = "America/Guyana [Guyana Time]"; +timezoneMap["America/Halifax"] = "America/Halifax [Atlantic Standard Time]"; +timezoneMap["America/Kralendijk"] = "America/Kralendijk [GMT-04:00]"; +timezoneMap["America/La_Paz"] = "America/La_Paz [Bolivia Time]"; +timezoneMap["America/Lower_Princes"] = "America/Lower_Princes [GMT-04:00]"; +timezoneMap["America/Manaus"] = "America/Manaus [Amazon Time]"; +timezoneMap["America/Marigot"] = "America/Marigot [Atlantic Standard Time]"; +timezoneMap["America/Martinique"] = "America/Martinique [Atlantic Standard Time]"; +timezoneMap["America/Moncton"] = "America/Moncton [Atlantic Standard Time]"; +timezoneMap["America/Montserrat"] = "America/Montserrat [Atlantic Standard Time]"; +timezoneMap["America/Port_of_Spain"] = "America/Port_of_Spain [Atlantic Standard Time]"; +timezoneMap["America/Porto_Acre"] = "America/Porto_Acre [Amazon Time]"; +timezoneMap["America/Porto_Velho"] = "America/Porto_Velho [Amazon Time]"; +timezoneMap["America/Puerto_Rico"] = "America/Puerto_Rico [Atlantic Standard Time]"; +timezoneMap["America/Rio_Branco"] = "America/Rio_Branco [Amazon Time]"; +timezoneMap["America/Santiago"] = "America/Santiago [Chile Time]"; +timezoneMap["America/Santo_Domingo"] = "America/Santo_Domingo [Atlantic Standard Time]"; +timezoneMap["America/St_Barthelemy"] = "America/St_Barthelemy [Atlantic Standard Time]"; +timezoneMap["America/St_Kitts"] = "America/St_Kitts [Atlantic Standard Time]"; +timezoneMap["America/St_Lucia"] = "America/St_Lucia [Atlantic Standard Time]"; +timezoneMap["America/St_Thomas"] = "America/St_Thomas [Atlantic Standard Time]"; +timezoneMap["America/St_Vincent"] = "America/St_Vincent [Atlantic Standard Time]"; +timezoneMap["America/Thule"] = "America/Thule [Atlantic Standard Time]"; +timezoneMap["America/Tortola"] = "America/Tortola [Atlantic Standard Time]"; +timezoneMap["America/Virgin"] = "America/Virgin [Atlantic Standard Time]"; +timezoneMap["Antarctica/Palmer"] = "Antarctica/Palmer [Chile Time]"; +timezoneMap["Atlantic/Bermuda"] = "Atlantic/Bermuda [Atlantic Standard Time]"; +timezoneMap["Brazil/Acre"] = "Brazil/Acre [Amazon Time]"; +timezoneMap["Brazil/West"] = "Brazil/West [Amazon Time]"; +timezoneMap["Canada/Atlantic"] = "Canada/Atlantic [Atlantic Standard Time]"; +timezoneMap["Chile/Continental"] = "Chile/Continental [Chile Time]"; +timezoneMap["Etc/GMT+4"] = "Etc/GMT+4 [GMT-04:00]"; +timezoneMap["PRT"] = "PRT [Atlantic Standard Time]"; +timezoneMap["SystemV/AST4"] = "SystemV/AST4 [Atlantic Standard Time]"; +timezoneMap["SystemV/AST4ADT"] = "SystemV/AST4ADT [Atlantic Standard Time]"; +timezoneMap["America/St_Johns"] = "America/St_Johns [Newfoundland Standard Time]"; +timezoneMap["CNT"] = "CNT [Newfoundland Standard Time]"; +timezoneMap["Canada/Newfoundland"] = "Canada/Newfoundland [Newfoundland Standard Time]"; +timezoneMap["AGT"] = "AGT [Argentine Time]"; +timezoneMap["America/Araguaina"] = "America/Araguaina [Brasilia Time]"; +timezoneMap["America/Argentina/Buenos_Aires"] = "America/Argentina/Buenos_Aires [Argentine Time]"; +timezoneMap["America/Argentina/Catamarca"] = "America/Argentina/Catamarca [Argentine Time]"; +timezoneMap["America/Argentina/ComodRivadavia"] = "America/Argentina/ComodRivadavia [Argentine Time]"; +timezoneMap["America/Argentina/Cordoba"] = "America/Argentina/Cordoba [Argentine Time]"; +timezoneMap["America/Argentina/Jujuy"] = "America/Argentina/Jujuy [Argentine Time]"; +timezoneMap["America/Argentina/La_Rioja"] = "America/Argentina/La_Rioja [Argentine Time]"; +timezoneMap["America/Argentina/Mendoza"] = "America/Argentina/Mendoza [Argentine Time]"; +timezoneMap["America/Argentina/Rio_Gallegos"] = "America/Argentina/Rio_Gallegos [Argentine Time]"; +timezoneMap["America/Argentina/Salta"] = "America/Argentina/Salta [Argentine Time]"; +timezoneMap["America/Argentina/San_Juan"] = "America/Argentina/San_Juan [Argentine Time]"; +timezoneMap["America/Argentina/Tucuman"] = "America/Argentina/Tucuman [Argentine Time]"; +timezoneMap["America/Argentina/Ushuaia"] = "America/Argentina/Ushuaia [Argentine Time]"; +timezoneMap["America/Bahia"] = "America/Bahia [Brasilia Time]"; +timezoneMap["America/Belem"] = "America/Belem [Brasilia Time]"; +timezoneMap["America/Buenos_Aires"] = "America/Buenos_Aires [Argentine Time]"; +timezoneMap["America/Catamarca"] = "America/Catamarca [Argentine Time]"; +timezoneMap["America/Cayenne"] = "America/Cayenne [French Guiana Time]"; +timezoneMap["America/Cordoba"] = "America/Cordoba [Argentine Time]"; +timezoneMap["America/Fortaleza"] = "America/Fortaleza [Brasilia Time]"; +timezoneMap["America/Godthab"] = "America/Godthab [Western Greenland Time]"; +timezoneMap["America/Jujuy"] = "America/Jujuy [Argentine Time]"; +timezoneMap["America/Maceio"] = "America/Maceio [Brasilia Time]"; +timezoneMap["America/Mendoza"] = "America/Mendoza [Argentine Time]"; +timezoneMap["America/Miquelon"] = "America/Miquelon [Pierre & Miquelon Standard Time]"; +timezoneMap["America/Montevideo"] = "America/Montevideo [Uruguay Time]"; +timezoneMap["America/Paramaribo"] = "America/Paramaribo [Suriname Time]"; +timezoneMap["America/Recife"] = "America/Recife [Brasilia Time]"; +timezoneMap["America/Rosario"] = "America/Rosario [Argentine Time]"; +timezoneMap["America/Santarem"] = "America/Santarem [Brasilia Time]"; +timezoneMap["America/Sao_Paulo"] = "America/Sao_Paulo [Brasilia Time]"; +timezoneMap["Antarctica/Rothera"] = "Antarctica/Rothera [Rothera Time]"; +timezoneMap["Atlantic/Stanley"] = "Atlantic/Stanley [Falkland Is. Time]"; +timezoneMap["BET"] = "BET [Brasilia Time]"; +timezoneMap["Brazil/East"] = "Brazil/East [Brasilia Time]"; +timezoneMap["Etc/GMT+3"] = "Etc/GMT+3 [GMT-03:00]"; +timezoneMap["America/Noronha"] = "America/Noronha [Fernando de Noronha Time]"; +timezoneMap["Atlantic/South_Georgia"] = "Atlantic/South_Georgia [South Georgia Standard Time]"; +timezoneMap["Brazil/DeNoronha"] = "Brazil/DeNoronha [Fernando de Noronha Time]"; +timezoneMap["Etc/GMT+2"] = "Etc/GMT+2 [GMT-02:00]"; +timezoneMap["America/Scoresbysund"] = "America/Scoresbysund [Eastern Greenland Time]"; +timezoneMap["Atlantic/Azores"] = "Atlantic/Azores [Azores Time]"; +timezoneMap["Atlantic/Cape_Verde"] = "Atlantic/Cape_Verde [Cape Verde Time]"; +timezoneMap["Etc/GMT+1"] = "Etc/GMT+1 [GMT-01:00]"; +timezoneMap["Africa/Abidjan"] = "Africa/Abidjan [Greenwich Mean Time]"; +timezoneMap["Africa/Accra"] = "Africa/Accra [Ghana Mean Time]"; +timezoneMap["Africa/Bamako"] = "Africa/Bamako [Greenwich Mean Time]"; +timezoneMap["Africa/Banjul"] = "Africa/Banjul [Greenwich Mean Time]"; +timezoneMap["Africa/Bissau"] = "Africa/Bissau [Greenwich Mean Time]"; +timezoneMap["Africa/Casablanca"] = "Africa/Casablanca [Western European Time]"; +timezoneMap["Africa/Conakry"] = "Africa/Conakry [Greenwich Mean Time]"; +timezoneMap["Africa/Dakar"] = "Africa/Dakar [Greenwich Mean Time]"; +timezoneMap["Africa/El_Aaiun"] = "Africa/El_Aaiun [Western European Time]"; +timezoneMap["Africa/Freetown"] = "Africa/Freetown [Greenwich Mean Time]"; +timezoneMap["Africa/Lome"] = "Africa/Lome [Greenwich Mean Time]"; +timezoneMap["Africa/Monrovia"] = "Africa/Monrovia [Greenwich Mean Time]"; +timezoneMap["Africa/Nouakchott"] = "Africa/Nouakchott [Greenwich Mean Time]"; +timezoneMap["Africa/Ouagadougou"] = "Africa/Ouagadougou [Greenwich Mean Time]"; +timezoneMap["Africa/Sao_Tome"] = "Africa/Sao_Tome [Greenwich Mean Time]"; +timezoneMap["Africa/Timbuktu"] = "Africa/Timbuktu [Greenwich Mean Time]"; +timezoneMap["America/Danmarkshavn"] = "America/Danmarkshavn [Greenwich Mean Time]"; +timezoneMap["Atlantic/Canary"] = "Atlantic/Canary [Western European Time]"; +timezoneMap["Atlantic/Faeroe"] = "Atlantic/Faeroe [Western European Time]"; +timezoneMap["Atlantic/Faroe"] = "Atlantic/Faroe [Western European Time]"; +timezoneMap["Atlantic/Madeira"] = "Atlantic/Madeira [Western European Time]"; +timezoneMap["Atlantic/Reykjavik"] = "Atlantic/Reykjavik [Greenwich Mean Time]"; +timezoneMap["Atlantic/St_Helena"] = "Atlantic/St_Helena [Greenwich Mean Time]"; +timezoneMap["Eire"] = "Eire [Greenwich Mean Time]"; +timezoneMap["Etc/GMT"] = "Etc/GMT [GMT+00:00]"; +timezoneMap["Etc/GMT+0"] = "Etc/GMT+0 [GMT+00:00]"; +timezoneMap["Etc/GMT-0"] = "Etc/GMT-0 [GMT+00:00]"; +timezoneMap["Etc/GMT0"] = "Etc/GMT0 [GMT+00:00]"; +timezoneMap["Etc/Greenwich"] = "Etc/Greenwich [Greenwich Mean Time]"; +timezoneMap["Etc/UCT"] = "Etc/UCT [Coordinated Universal Time]"; +timezoneMap["Etc/UTC"] = "Etc/UTC [Coordinated Universal Time]"; +timezoneMap["Etc/Universal"] = "Etc/Universal [Coordinated Universal Time]"; +timezoneMap["Etc/Zulu"] = "Etc/Zulu [Coordinated Universal Time]"; +timezoneMap["Europe/Belfast"] = "Europe/Belfast [Greenwich Mean Time]"; +timezoneMap["Europe/Dublin"] = "Europe/Dublin [Greenwich Mean Time]"; +timezoneMap["Europe/Guernsey"] = "Europe/Guernsey [Greenwich Mean Time]"; +timezoneMap["Europe/Isle_of_Man"] = "Europe/Isle_of_Man [Greenwich Mean Time]"; +timezoneMap["Europe/Jersey"] = "Europe/Jersey [Greenwich Mean Time]"; +timezoneMap["Europe/Lisbon"] = "Europe/Lisbon [Western European Time]"; +timezoneMap["Europe/London"] = "Europe/London [Greenwich Mean Time]"; +timezoneMap["GB"] = "GB [Greenwich Mean Time]"; +timezoneMap["GB-Eire"] = "GB-Eire [Greenwich Mean Time]"; +timezoneMap["GMT"] = "GMT [Greenwich Mean Time]"; +timezoneMap["GMT0"] = "GMT0 [GMT+00:00]"; +timezoneMap["Greenwich"] = "Greenwich [Greenwich Mean Time]"; +timezoneMap["Iceland"] = "Iceland [Greenwich Mean Time]"; +timezoneMap["Portugal"] = "Portugal [Western European Time]"; +timezoneMap["UCT"] = "UCT [Coordinated Universal Time]"; +timezoneMap["UTC"] = "UTC [Coordinated Universal Time]"; +timezoneMap["Universal"] = "Universal [Coordinated Universal Time]"; +timezoneMap["WET"] = "WET [Western European Time]"; +timezoneMap["Zulu"] = "Zulu [Coordinated Universal Time]"; +timezoneMap["Africa/Algiers"] = "Africa/Algiers [Central European Time]"; +timezoneMap["Africa/Bangui"] = "Africa/Bangui [Western African Time]"; +timezoneMap["Africa/Brazzaville"] = "Africa/Brazzaville [Western African Time]"; +timezoneMap["Africa/Ceuta"] = "Africa/Ceuta [Central European Time]"; +timezoneMap["Africa/Douala"] = "Africa/Douala [Western African Time]"; +timezoneMap["Africa/Kinshasa"] = "Africa/Kinshasa [Western African Time]"; +timezoneMap["Africa/Lagos"] = "Africa/Lagos [Western African Time]"; +timezoneMap["Africa/Libreville"] = "Africa/Libreville [Western African Time]"; +timezoneMap["Africa/Luanda"] = "Africa/Luanda [Western African Time]"; +timezoneMap["Africa/Malabo"] = "Africa/Malabo [Western African Time]"; +timezoneMap["Africa/Ndjamena"] = "Africa/Ndjamena [Western African Time]"; +timezoneMap["Africa/Niamey"] = "Africa/Niamey [Western African Time]"; +timezoneMap["Africa/Porto-Novo"] = "Africa/Porto-Novo [Western African Time]"; +timezoneMap["Africa/Tripoli"] = "Africa/Tripoli [Eastern European Time]"; +timezoneMap["Africa/Tunis"] = "Africa/Tunis [Central European Time]"; +timezoneMap["Africa/Windhoek"] = "Africa/Windhoek [Western African Time]"; +timezoneMap["Arctic/Longyearbyen"] = "Arctic/Longyearbyen [Central European Time]"; +timezoneMap["Atlantic/Jan_Mayen"] = "Atlantic/Jan_Mayen [Central European Time]"; +timezoneMap["CET"] = "CET [Central European Time]"; +timezoneMap["ECT"] = "ECT [Central European Time]"; +timezoneMap["Etc/GMT-1"] = "Etc/GMT-1 [GMT+01:00]"; +timezoneMap["Europe/Amsterdam"] = "Europe/Amsterdam [Central European Time]"; +timezoneMap["Europe/Andorra"] = "Europe/Andorra [Central European Time]"; +timezoneMap["Europe/Belgrade"] = "Europe/Belgrade [Central European Time]"; +timezoneMap["Europe/Berlin"] = "Europe/Berlin [Central European Time]"; +timezoneMap["Europe/Bratislava"] = "Europe/Bratislava [Central European Time]"; +timezoneMap["Europe/Brussels"] = "Europe/Brussels [Central European Time]"; +timezoneMap["Europe/Budapest"] = "Europe/Budapest [Central European Time]"; +timezoneMap["Europe/Busingen"] = "Europe/Busingen [GMT+01:00]"; +timezoneMap["Europe/Copenhagen"] = "Europe/Copenhagen [Central European Time]"; +timezoneMap["Europe/Gibraltar"] = "Europe/Gibraltar [Central European Time]"; +timezoneMap["Europe/Ljubljana"] = "Europe/Ljubljana [Central European Time]"; +timezoneMap["Europe/Luxembourg"] = "Europe/Luxembourg [Central European Time]"; +timezoneMap["Europe/Madrid"] = "Europe/Madrid [Central European Time]"; +timezoneMap["Europe/Malta"] = "Europe/Malta [Central European Time]"; +timezoneMap["Europe/Monaco"] = "Europe/Monaco [Central European Time]"; +timezoneMap["Europe/Oslo"] = "Europe/Oslo [Central European Time]"; +timezoneMap["Europe/Paris"] = "Europe/Paris [Central European Time]"; +timezoneMap["Europe/Podgorica"] = "Europe/Podgorica [Central European Time]"; +timezoneMap["Europe/Prague"] = "Europe/Prague [Central European Time]"; +timezoneMap["Europe/Rome"] = "Europe/Rome [Central European Time]"; +timezoneMap["Europe/San_Marino"] = "Europe/San_Marino [Central European Time]"; +timezoneMap["Europe/Sarajevo"] = "Europe/Sarajevo [Central European Time]"; +timezoneMap["Europe/Skopje"] = "Europe/Skopje [Central European Time]"; +timezoneMap["Europe/Stockholm"] = "Europe/Stockholm [Central European Time]"; +timezoneMap["Europe/Tirane"] = "Europe/Tirane [Central European Time]"; +timezoneMap["Europe/Vaduz"] = "Europe/Vaduz [Central European Time]"; +timezoneMap["Europe/Vatican"] = "Europe/Vatican [Central European Time]"; +timezoneMap["Europe/Vienna"] = "Europe/Vienna [Central European Time]"; +timezoneMap["Europe/Warsaw"] = "Europe/Warsaw [Central European Time]"; +timezoneMap["Europe/Zagreb"] = "Europe/Zagreb [Central European Time]"; +timezoneMap["Europe/Zurich"] = "Europe/Zurich [Central European Time]"; +timezoneMap["Libya"] = "Libya [Eastern European Time]"; +timezoneMap["MET"] = "MET [Middle Europe Time]"; +timezoneMap["ART"] = "ART [Eastern European Time]"; +timezoneMap["Africa/Blantyre"] = "Africa/Blantyre [Central African Time]"; +timezoneMap["Africa/Bujumbura"] = "Africa/Bujumbura [Central African Time]"; +timezoneMap["Africa/Cairo"] = "Africa/Cairo [Eastern European Time]"; +timezoneMap["Africa/Gaborone"] = "Africa/Gaborone [Central African Time]"; +timezoneMap["Africa/Harare"] = "Africa/Harare [Central African Time]"; +timezoneMap["Africa/Johannesburg"] = "Africa/Johannesburg [South Africa Standard Time]"; +timezoneMap["Africa/Kigali"] = "Africa/Kigali [Central African Time]"; +timezoneMap["Africa/Lubumbashi"] = "Africa/Lubumbashi [Central African Time]"; +timezoneMap["Africa/Lusaka"] = "Africa/Lusaka [Central African Time]"; +timezoneMap["Africa/Maputo"] = "Africa/Maputo [Central African Time]"; +timezoneMap["Africa/Maseru"] = "Africa/Maseru [South Africa Standard Time]"; +timezoneMap["Africa/Mbabane"] = "Africa/Mbabane [South Africa Standard Time]"; +timezoneMap["Asia/Amman"] = "Asia/Amman [Eastern European Time]"; +timezoneMap["Asia/Beirut"] = "Asia/Beirut [Eastern European Time]"; +timezoneMap["Asia/Damascus"] = "Asia/Damascus [Eastern European Time]"; +timezoneMap["Asia/Gaza"] = "Asia/Gaza [Eastern European Time]"; +timezoneMap["Asia/Hebron"] = "Asia/Hebron [GMT+02:00]"; +timezoneMap["Asia/Istanbul"] = "Asia/Istanbul [Eastern European Time]"; +timezoneMap["Asia/Jerusalem"] = "Asia/Jerusalem [Israel Standard Time]"; +timezoneMap["Asia/Nicosia"] = "Asia/Nicosia [Eastern European Time]"; +timezoneMap["Asia/Tel_Aviv"] = "Asia/Tel_Aviv [Israel Standard Time]"; +timezoneMap["CAT"] = "CAT [Central African Time]"; +timezoneMap["EET"] = "EET [Eastern European Time]"; +timezoneMap["Egypt"] = "Egypt [Eastern European Time]"; +timezoneMap["Etc/GMT-2"] = "Etc/GMT-2 [GMT+02:00]"; +timezoneMap["Europe/Athens"] = "Europe/Athens [Eastern European Time]"; +timezoneMap["Europe/Bucharest"] = "Europe/Bucharest [Eastern European Time]"; +timezoneMap["Europe/Chisinau"] = "Europe/Chisinau [Eastern European Time]"; +timezoneMap["Europe/Helsinki"] = "Europe/Helsinki [Eastern European Time]"; +timezoneMap["Europe/Istanbul"] = "Europe/Istanbul [Eastern European Time]"; +timezoneMap["Europe/Kiev"] = "Europe/Kiev [Eastern European Time]"; +timezoneMap["Europe/Mariehamn"] = "Europe/Mariehamn [Eastern European Time]"; +timezoneMap["Europe/Nicosia"] = "Europe/Nicosia [Eastern European Time]"; +timezoneMap["Europe/Riga"] = "Europe/Riga [Eastern European Time]"; +timezoneMap["Europe/Simferopol"] = "Europe/Simferopol [Eastern European Time]"; +timezoneMap["Europe/Sofia"] = "Europe/Sofia [Eastern European Time]"; +timezoneMap["Europe/Tallinn"] = "Europe/Tallinn [Eastern European Time]"; +timezoneMap["Europe/Tiraspol"] = "Europe/Tiraspol [Eastern European Time]"; +timezoneMap["Europe/Uzhgorod"] = "Europe/Uzhgorod [Eastern European Time]"; +timezoneMap["Europe/Vilnius"] = "Europe/Vilnius [Eastern European Time]"; +timezoneMap["Europe/Zaporozhye"] = "Europe/Zaporozhye [Eastern European Time]"; +timezoneMap["Israel"] = "Israel [Israel Standard Time]"; +timezoneMap["Turkey"] = "Turkey [Eastern European Time]"; +timezoneMap["Africa/Addis_Ababa"] = "Africa/Addis_Ababa [Eastern African Time]"; +timezoneMap["Africa/Asmara"] = "Africa/Asmara [Eastern African Time]"; +timezoneMap["Africa/Asmera"] = "Africa/Asmera [Eastern African Time]"; +timezoneMap["Africa/Dar_es_Salaam"] = "Africa/Dar_es_Salaam [Eastern African Time]"; +timezoneMap["Africa/Djibouti"] = "Africa/Djibouti [Eastern African Time]"; +timezoneMap["Africa/Juba"] = "Africa/Juba [GMT+03:00]"; +timezoneMap["Africa/Kampala"] = "Africa/Kampala [Eastern African Time]"; +timezoneMap["Africa/Khartoum"] = "Africa/Khartoum [Eastern African Time]"; +timezoneMap["Africa/Mogadishu"] = "Africa/Mogadishu [Eastern African Time]"; +timezoneMap["Africa/Nairobi"] = "Africa/Nairobi [Eastern African Time]"; +timezoneMap["Antarctica/Syowa"] = "Antarctica/Syowa [Syowa Time]"; +timezoneMap["Asia/Aden"] = "Asia/Aden [Arabia Standard Time]"; +timezoneMap["Asia/Baghdad"] = "Asia/Baghdad [Arabia Standard Time]"; +timezoneMap["Asia/Bahrain"] = "Asia/Bahrain [Arabia Standard Time]"; +timezoneMap["Asia/Kuwait"] = "Asia/Kuwait [Arabia Standard Time]"; +timezoneMap["Asia/Qatar"] = "Asia/Qatar [Arabia Standard Time]"; +timezoneMap["Asia/Riyadh"] = "Asia/Riyadh [Arabia Standard Time]"; +timezoneMap["EAT"] = "EAT [Eastern African Time]"; +timezoneMap["Etc/GMT-3"] = "Etc/GMT-3 [GMT+03:00]"; +timezoneMap["Europe/Kaliningrad"] = "Europe/Kaliningrad [Eastern European Time]"; +timezoneMap["Europe/Minsk"] = "Europe/Minsk [Eastern European Time]"; +timezoneMap["Indian/Antananarivo"] = "Indian/Antananarivo [Eastern African Time]"; +timezoneMap["Indian/Comoro"] = "Indian/Comoro [Eastern African Time]"; +timezoneMap["Indian/Mayotte"] = "Indian/Mayotte [Eastern African Time]"; +timezoneMap["Asia/Riyadh87"] = "Asia/Riyadh87 [GMT+03:07]"; +timezoneMap["Asia/Riyadh88"] = "Asia/Riyadh88 [GMT+03:07]"; +timezoneMap["Asia/Riyadh89"] = "Asia/Riyadh89 [GMT+03:07]"; +timezoneMap["Mideast/Riyadh87"] = "Mideast/Riyadh87 [GMT+03:07]"; +timezoneMap["Mideast/Riyadh88"] = "Mideast/Riyadh88 [GMT+03:07]"; +timezoneMap["Mideast/Riyadh89"] = "Mideast/Riyadh89 [GMT+03:07]"; +timezoneMap["Asia/Tehran"] = "Asia/Tehran [Iran Standard Time]"; +timezoneMap["Iran"] = "Iran [Iran Standard Time]"; +timezoneMap["Asia/Baku"] = "Asia/Baku [Azerbaijan Time]"; +timezoneMap["Asia/Dubai"] = "Asia/Dubai [Gulf Standard Time]"; +timezoneMap["Asia/Muscat"] = "Asia/Muscat [Gulf Standard Time]"; +timezoneMap["Asia/Tbilisi"] = "Asia/Tbilisi [Georgia Time]"; +timezoneMap["Asia/Yerevan"] = "Asia/Yerevan [Armenia Time]"; +timezoneMap["Etc/GMT-4"] = "Etc/GMT-4 [GMT+04:00]"; +timezoneMap["Europe/Moscow"] = "Europe/Moscow [Moscow Standard Time]"; +timezoneMap["Europe/Samara"] = "Europe/Samara [Samara Time]"; +timezoneMap["Europe/Volgograd"] = "Europe/Volgograd [Volgograd Time]"; +timezoneMap["Indian/Mahe"] = "Indian/Mahe [Seychelles Time]"; +timezoneMap["Indian/Mauritius"] = "Indian/Mauritius [Mauritius Time]"; +timezoneMap["Indian/Reunion"] = "Indian/Reunion [Reunion Time]"; +timezoneMap["NET"] = "NET [Armenia Time]"; +timezoneMap["W-SU"] = "W-SU [Moscow Standard Time]"; +timezoneMap["Asia/Kabul"] = "Asia/Kabul [Afghanistan Time]"; +timezoneMap["Antarctica/Mawson"] = "Antarctica/Mawson [Mawson Time]"; +timezoneMap["Asia/Aqtau"] = "Asia/Aqtau [Aqtau Time]"; +timezoneMap["Asia/Aqtobe"] = "Asia/Aqtobe [Aqtobe Time]"; +timezoneMap["Asia/Ashgabat"] = "Asia/Ashgabat [Turkmenistan Time]"; +timezoneMap["Asia/Ashkhabad"] = "Asia/Ashkhabad [Turkmenistan Time]"; +timezoneMap["Asia/Dushanbe"] = "Asia/Dushanbe [Tajikistan Time]"; +timezoneMap["Asia/Karachi"] = "Asia/Karachi [Pakistan Time]"; +timezoneMap["Asia/Oral"] = "Asia/Oral [Oral Time]"; +timezoneMap["Asia/Samarkand"] = "Asia/Samarkand [Uzbekistan Time]"; +timezoneMap["Asia/Tashkent"] = "Asia/Tashkent [Uzbekistan Time]"; +timezoneMap["Etc/GMT-5"] = "Etc/GMT-5 [GMT+05:00]"; +timezoneMap["Indian/Kerguelen"] = "Indian/Kerguelen [French Southern & Antarctic Lands Time]"; +timezoneMap["Indian/Maldives"] = "Indian/Maldives [Maldives Time]"; +timezoneMap["PLT"] = "PLT [Pakistan Time]"; +timezoneMap["Asia/Calcutta"] = "Asia/Calcutta [India Standard Time]"; +timezoneMap["Asia/Colombo"] = "Asia/Colombo [India Standard Time]"; +timezoneMap["Asia/Kolkata"] = "Asia/Kolkata [India Standard Time]"; +timezoneMap["IST"] = "IST [India Standard Time]"; +timezoneMap["Asia/Kathmandu"] = "Asia/Kathmandu [Nepal Time]"; +timezoneMap["Asia/Katmandu"] = "Asia/Katmandu [Nepal Time]"; +timezoneMap["Antarctica/Vostok"] = "Antarctica/Vostok [Vostok Time]"; +timezoneMap["Asia/Almaty"] = "Asia/Almaty [Alma-Ata Time]"; +timezoneMap["Asia/Bishkek"] = "Asia/Bishkek [Kirgizstan Time]"; +timezoneMap["Asia/Dacca"] = "Asia/Dacca [Bangladesh Time]"; +timezoneMap["Asia/Dhaka"] = "Asia/Dhaka [Bangladesh Time]"; +timezoneMap["Asia/Qyzylorda"] = "Asia/Qyzylorda [Qyzylorda Time]"; +timezoneMap["Asia/Thimbu"] = "Asia/Thimbu [Bhutan Time]"; +timezoneMap["Asia/Thimphu"] = "Asia/Thimphu [Bhutan Time]"; +timezoneMap["Asia/Yekaterinburg"] = "Asia/Yekaterinburg [Yekaterinburg Time]"; +timezoneMap["BST"] = "BST [Bangladesh Time]"; +timezoneMap["Etc/GMT-6"] = "Etc/GMT-6 [GMT+06:00]"; +timezoneMap["Indian/Chagos"] = "Indian/Chagos [Indian Ocean Territory Time]"; +timezoneMap["Asia/Rangoon"] = "Asia/Rangoon [Myanmar Time]"; +timezoneMap["Indian/Cocos"] = "Indian/Cocos [Cocos Islands Time]"; +timezoneMap["Antarctica/Davis"] = "Antarctica/Davis [Davis Time]"; +timezoneMap["Asia/Bangkok"] = "Asia/Bangkok [Indochina Time]"; +timezoneMap["Asia/Ho_Chi_Minh"] = "Asia/Ho_Chi_Minh [Indochina Time]"; +timezoneMap["Asia/Hovd"] = "Asia/Hovd [Hovd Time]"; +timezoneMap["Asia/Jakarta"] = "Asia/Jakarta [West Indonesia Time]"; +timezoneMap["Asia/Novokuznetsk"] = "Asia/Novokuznetsk [Novosibirsk Time]"; +timezoneMap["Asia/Novosibirsk"] = "Asia/Novosibirsk [Novosibirsk Time]"; +timezoneMap["Asia/Omsk"] = "Asia/Omsk [Omsk Time]"; +timezoneMap["Asia/Phnom_Penh"] = "Asia/Phnom_Penh [Indochina Time]"; +timezoneMap["Asia/Pontianak"] = "Asia/Pontianak [West Indonesia Time]"; +timezoneMap["Asia/Saigon"] = "Asia/Saigon [Indochina Time]"; +timezoneMap["Asia/Vientiane"] = "Asia/Vientiane [Indochina Time]"; +timezoneMap["Etc/GMT-7"] = "Etc/GMT-7 [GMT+07:00]"; +timezoneMap["Indian/Christmas"] = "Indian/Christmas [Christmas Island Time]"; +timezoneMap["VST"] = "VST [Indochina Time]"; +timezoneMap["Antarctica/Casey"] = "Antarctica/Casey [Western Standard Time (Australia)]"; +timezoneMap["Asia/Brunei"] = "Asia/Brunei [Brunei Time]"; +timezoneMap["Asia/Choibalsan"] = "Asia/Choibalsan [Choibalsan Time]"; +timezoneMap["Asia/Chongqing"] = "Asia/Chongqing [China Standard Time]"; +timezoneMap["Asia/Chungking"] = "Asia/Chungking [China Standard Time]"; +timezoneMap["Asia/Harbin"] = "Asia/Harbin [China Standard Time]"; +timezoneMap["Asia/Hong_Kong"] = "Asia/Hong_Kong [Hong Kong Time]"; +timezoneMap["Asia/Kashgar"] = "Asia/Kashgar [China Standard Time]"; +timezoneMap["Asia/Krasnoyarsk"] = "Asia/Krasnoyarsk [Krasnoyarsk Time]"; +timezoneMap["Asia/Kuala_Lumpur"] = "Asia/Kuala_Lumpur [Malaysia Time]"; +timezoneMap["Asia/Kuching"] = "Asia/Kuching [Malaysia Time]"; +timezoneMap["Asia/Macao"] = "Asia/Macao [China Standard Time]"; +timezoneMap["Asia/Macau"] = "Asia/Macau [China Standard Time]"; +timezoneMap["Asia/Makassar"] = "Asia/Makassar [Central Indonesia Time]"; +timezoneMap["Asia/Manila"] = "Asia/Manila [Philippines Time]"; +timezoneMap["Asia/Shanghai"] = "Asia/Shanghai [China Standard Time]"; +timezoneMap["Asia/Singapore"] = "Asia/Singapore [Singapore Time]"; +timezoneMap["Asia/Taipei"] = "Asia/Taipei [China Standard Time]"; +timezoneMap["Asia/Ujung_Pandang"] = "Asia/Ujung_Pandang [Central Indonesia Time]"; +timezoneMap["Asia/Ulaanbaatar"] = "Asia/Ulaanbaatar [Ulaanbaatar Time]"; +timezoneMap["Asia/Ulan_Bator"] = "Asia/Ulan_Bator [Ulaanbaatar Time]"; +timezoneMap["Asia/Urumqi"] = "Asia/Urumqi [China Standard Time]"; +timezoneMap["Australia/Perth"] = "Australia/Perth [Western Standard Time (Australia)]"; +timezoneMap["Australia/West"] = "Australia/West [Western Standard Time (Australia)]"; +timezoneMap["CTT"] = "CTT [China Standard Time]"; +timezoneMap["Etc/GMT-8"] = "Etc/GMT-8 [GMT+08:00]"; +timezoneMap["Hongkong"] = "Hongkong [Hong Kong Time]"; +timezoneMap["PRC"] = "PRC [China Standard Time]"; +timezoneMap["Singapore"] = "Singapore [Singapore Time]"; +timezoneMap["Australia/Eucla"] = "Australia/Eucla [Central Western Standard Time (Australia)]"; +timezoneMap["Asia/Dili"] = "Asia/Dili [Timor-Leste Time]"; +timezoneMap["Asia/Irkutsk"] = "Asia/Irkutsk [Irkutsk Time]"; +timezoneMap["Asia/Jayapura"] = "Asia/Jayapura [East Indonesia Time]"; +timezoneMap["Asia/Pyongyang"] = "Asia/Pyongyang [Korea Standard Time]"; +timezoneMap["Asia/Seoul"] = "Asia/Seoul [Korea Standard Time]"; +timezoneMap["Asia/Tokyo"] = "Asia/Tokyo [Japan Standard Time]"; +timezoneMap["Etc/GMT-9"] = "Etc/GMT-9 [GMT+09:00]"; +timezoneMap["JST"] = "JST [Japan Standard Time]"; +timezoneMap["Japan"] = "Japan [Japan Standard Time]"; +timezoneMap["Pacific/Palau"] = "Pacific/Palau [Palau Time]"; +timezoneMap["ROK"] = "ROK [Korea Standard Time]"; +timezoneMap["ACT"] = "ACT [Central Standard Time (Northern Territory)]"; +timezoneMap["Australia/Adelaide"] = "Australia/Adelaide [Central Standard Time (South Australia)]"; +timezoneMap["Australia/Broken_Hill"] = "Australia/Broken_Hill [Central Standard Time (South Australia/New South Wales)]"; +timezoneMap["Australia/Darwin"] = "Australia/Darwin [Central Standard Time (Northern Territory)]"; +timezoneMap["Australia/North"] = "Australia/North [Central Standard Time (Northern Territory)]"; +timezoneMap["Australia/South"] = "Australia/South [Central Standard Time (South Australia)]"; +timezoneMap["Australia/Yancowinna"] = "Australia/Yancowinna [Central Standard Time (South Australia/New South Wales)]"; +timezoneMap["AET"] = "AET [Eastern Standard Time (New South Wales)]"; +timezoneMap["Antarctica/DumontDUrville"] = "Antarctica/DumontDUrville [Dumont-d'Urville Time]"; +timezoneMap["Asia/Khandyga"] = "Asia/Khandyga [GMT+10:00]"; +timezoneMap["Asia/Yakutsk"] = "Asia/Yakutsk [Yakutsk Time]"; +timezoneMap["Australia/ACT"] = "Australia/ACT [Eastern Standard Time (New South Wales)]"; +timezoneMap["Australia/Brisbane"] = "Australia/Brisbane [Eastern Standard Time (Queensland)]"; +timezoneMap["Australia/Canberra"] = "Australia/Canberra [Eastern Standard Time (New South Wales)]"; +timezoneMap["Australia/Currie"] = "Australia/Currie [Eastern Standard Time (New South Wales)]"; +timezoneMap["Australia/Hobart"] = "Australia/Hobart [Eastern Standard Time (Tasmania)]"; +timezoneMap["Australia/Lindeman"] = "Australia/Lindeman [Eastern Standard Time (Queensland)]"; +timezoneMap["Australia/Melbourne"] = "Australia/Melbourne [Eastern Standard Time (Victoria)]"; +timezoneMap["Australia/NSW"] = "Australia/NSW [Eastern Standard Time (New South Wales)]"; +timezoneMap["Australia/Queensland"] = "Australia/Queensland [Eastern Standard Time (Queensland)]"; +timezoneMap["Australia/Sydney"] = "Australia/Sydney [Eastern Standard Time (New South Wales)]"; +timezoneMap["Australia/Tasmania"] = "Australia/Tasmania [Eastern Standard Time (Tasmania)]"; +timezoneMap["Australia/Victoria"] = "Australia/Victoria [Eastern Standard Time (Victoria)]"; +timezoneMap["Etc/GMT-10"] = "Etc/GMT-10 [GMT+10:00]"; +timezoneMap["Pacific/Chuuk"] = "Pacific/Chuuk [GMT+10:00]"; +timezoneMap["Pacific/Guam"] = "Pacific/Guam [Chamorro Standard Time]"; +timezoneMap["Pacific/Port_Moresby"] = "Pacific/Port_Moresby [Papua New Guinea Time]"; +timezoneMap["Pacific/Saipan"] = "Pacific/Saipan [Chamorro Standard Time]"; +timezoneMap["Pacific/Truk"] = "Pacific/Truk [Truk Time]"; +timezoneMap["Pacific/Yap"] = "Pacific/Yap [Truk Time]"; +timezoneMap["Australia/LHI"] = "Australia/LHI [Lord Howe Standard Time]"; +timezoneMap["Australia/Lord_Howe"] = "Australia/Lord_Howe [Lord Howe Standard Time]"; +timezoneMap["Antarctica/Macquarie"] = "Antarctica/Macquarie [Macquarie Island Time]"; +timezoneMap["Asia/Sakhalin"] = "Asia/Sakhalin [Sakhalin Time]"; +timezoneMap["Asia/Ust-Nera"] = "Asia/Ust-Nera [GMT+11:00]"; +timezoneMap["Asia/Vladivostok"] = "Asia/Vladivostok [Vladivostok Time]"; +timezoneMap["Etc/GMT-11"] = "Etc/GMT-11 [GMT+11:00]"; +timezoneMap["Pacific/Efate"] = "Pacific/Efate [Vanuatu Time]"; +timezoneMap["Pacific/Guadalcanal"] = "Pacific/Guadalcanal [Solomon Is. Time]"; +timezoneMap["Pacific/Kosrae"] = "Pacific/Kosrae [Kosrae Time]"; +timezoneMap["Pacific/Noumea"] = "Pacific/Noumea [New Caledonia Time]"; +timezoneMap["Pacific/Pohnpei"] = "Pacific/Pohnpei [GMT+11:00]"; +timezoneMap["Pacific/Ponape"] = "Pacific/Ponape [Ponape Time]"; +timezoneMap["SST"] = "SST [Solomon Is. Time]"; +timezoneMap["Pacific/Norfolk"] = "Pacific/Norfolk [Norfolk Time]"; +timezoneMap["Antarctica/McMurdo"] = "Antarctica/McMurdo [New Zealand Standard Time]"; +timezoneMap["Antarctica/South_Pole"] = "Antarctica/South_Pole [New Zealand Standard Time]"; +timezoneMap["Asia/Anadyr"] = "Asia/Anadyr [Anadyr Time]"; +timezoneMap["Asia/Kamchatka"] = "Asia/Kamchatka [Petropavlovsk-Kamchatski Time]"; +timezoneMap["Asia/Magadan"] = "Asia/Magadan [Magadan Time]"; +timezoneMap["Etc/GMT-12"] = "Etc/GMT-12 [GMT+12:00]"; +timezoneMap["Kwajalein"] = "Kwajalein [Marshall Islands Time]"; +timezoneMap["NST"] = "NST [New Zealand Standard Time]"; +timezoneMap["NZ"] = "NZ [New Zealand Standard Time]"; +timezoneMap["Pacific/Auckland"] = "Pacific/Auckland [New Zealand Standard Time]"; +timezoneMap["Pacific/Fiji"] = "Pacific/Fiji [Fiji Time]"; +timezoneMap["Pacific/Funafuti"] = "Pacific/Funafuti [Tuvalu Time]"; +timezoneMap["Pacific/Kwajalein"] = "Pacific/Kwajalein [Marshall Islands Time]"; +timezoneMap["Pacific/Majuro"] = "Pacific/Majuro [Marshall Islands Time]"; +timezoneMap["Pacific/Nauru"] = "Pacific/Nauru [Nauru Time]"; +timezoneMap["Pacific/Tarawa"] = "Pacific/Tarawa [Gilbert Is. Time]"; +timezoneMap["Pacific/Wake"] = "Pacific/Wake [Wake Time]"; +timezoneMap["Pacific/Wallis"] = "Pacific/Wallis [Wallis & Futuna Time]"; +timezoneMap["NZ-CHAT"] = "NZ-CHAT [Chatham Standard Time]"; +timezoneMap["Pacific/Chatham"] = "Pacific/Chatham [Chatham Standard Time]"; +timezoneMap["Etc/GMT-13"] = "Etc/GMT-13 [GMT+13:00]"; +timezoneMap["MIT"] = "MIT [West Samoa Time]"; +timezoneMap["Pacific/Apia"] = "Pacific/Apia [West Samoa Time]"; +timezoneMap["Pacific/Enderbury"] = "Pacific/Enderbury [Phoenix Is. Time]"; +timezoneMap["Pacific/Fakaofo"] = "Pacific/Fakaofo [Tokelau Time]"; +timezoneMap["Pacific/Tongatapu"] = "Pacific/Tongatapu [Tonga Time]"; +timezoneMap["Etc/GMT-14"] = "Etc/GMT-14 [GMT+14:00]"; +timezoneMap["Pacific/Kiritimati"] = "Pacific/Kiritimati [Line Is. Time]"; + // CloudStack common API helpers cloudStack.api = { From 11fb92ce13358dedf667798c7b49a533680fd5e0 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 10 Sep 2013 16:15:39 -0700 Subject: [PATCH 147/251] CLOUDSTACK-4642: UI > storage > volume > recurring snapshot > timezone dropdown > remove hardcoding dropdown option from index.jsp since dropdown option is generated on the fly from timezoneMap variable in JavaScript file. --- ui/index.jsp | 61 +--------------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/ui/index.jsp b/ui/index.jsp index 0ac48c93b84..7a7a4edd86d 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1387,66 +1387,7 @@ under the License.
-
From a94443665633ed2a372430ec900dc6459d849633 Mon Sep 17 00:00:00 2001 From: radhikap Date: Wed, 11 Sep 2013 11:26:40 +0530 Subject: [PATCH 148/251] new features section updated, api section added CLOUDSTACK-4245 --- docs/en-US/Release_Notes.xml | 2992 ++++++++++++++++++++++++++++++-- docs/en-US/removed-api-4.2.xml | 2 +- 2 files changed, 2860 insertions(+), 134 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 9ee70119676..ac010d7d398 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -25,7 +25,7 @@ under the License. Welcome to &PRODUCT; 4.2 Welcome to the 4.2.0 release of &PRODUCT;, the second major release from the Apache CloudStack project since its graduation from the Apache Incubator. &PRODUCT; 4.2 includes more - than 50 new features and enhancements. The focus of the release is on three major + than 70 new features and enhancements. The focus of the release is on three major areas: @@ -55,208 +55,1021 @@ under the License. If you find any errors or problems in this guide, please see . We hope you enjoy working with &PRODUCT;! - - Version 4.2.0 -
- What’s New in 4.2 - Apache CloudStack 4.2.0 includes many new features. This section covers the most - prominent new features and changes. -
- Windows 8 and Windows Server as VM Guest OS - Supported on XenServer, VMware, and KVM. - Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual - machines. The OS would be made available the same as any other, by uploading an ISO or a - template. The instructions for uploading ISOs and templates are given in the - Administrator's Guide. + + What's New in 4.2 + &PRODUCT; 4.2 includes the following new features. +
+ Features to Support Heterogeneous Workloads + The following new features help &PRODUCT; 4.2 better support both legacy and cloud-era + style zones. +
+ Regions + To increase reliability of the cloud, you can optionally group resources into + geographic regions. A region is the largest available organizational unit within a cloud + deployment. A region is made up of several availability zones, where each zone is + equivalent to a datacenter. Each region is controlled by its own cluster of Management + Servers, running in one of the zones. The zones in a region are typically located in close + geographical proximity. Regions are a useful technique for providing fault tolerance and + disaster recovery. + By grouping zones into regions, the cloud can achieve higher availability and + scalability. User accounts can span regions, so that users can deploy VMs in multiple, + widely-dispersed regions. Even if one of the regions becomes unavailable, the services are + still available to the end-user through VMs deployed in another region. And by grouping + communities of zones under their own nearby Management Servers, the latency of + communications within the cloud is reduced compared to managing widely-dispersed zones + from a single central Management Server. + Usage records can also be consolidated and tracked at the region level, creating + reports or invoices for each geographic region. + + + + + + region-overview.png: Nested structure of a region. + + +
+
+ Object Storage Plugin Architecture + Artifacts such as templates, ISOs and snapshots are kept in storage which &PRODUCT; + refers to as secondary storage. To improve scalability and performance, as when a number + of hosts access secondary storage concurrently, object storage can be used for secondary + storage. Object storage can also provide built-in high availability capability. When using + object storage, access to secondary storage data can be made available across multiple + zones in a region. This is a huge benefit, as it is no longer necessary to copy templates, + snapshots etc. across zones as would be needed in an NFS-only environment. + Object storage is provided through third-party software such as Amazon Simple Storage + Service (S3) or any other object storage that supports the S3 interface. These third party + object storages can be integrated with &PRODUCT; by writing plugin software that uses the + object storage plugin capability introduced in &PRODUCT; 4.2. Several new pluggable + service interfaces are available so that different storage providers can develop + vendor-specific plugins based on the well-defined contracts that can be seemlessly managed + by &PRODUCT;. +
+
+ Zone-Wide Primary Storage + (Supported on KVM and VMware) + In &PRODUCT; 4.2, you can provision primary storage on a per-zone basis. Data volumes + in the primary storage can be attached to any VM on any host in the zone. + In previous &PRODUCT; versions, each cluster had its own primary storage. Data in the + primary storage was directly available only to VMs within that cluster. If a VM in a + different cluster needed some of the data, it must be copied from one cluster to another, + using the zone's secondary storage as an intermediate step. This operation was + unnecessarily time-consuming. +
+
+ VMware Datacenter Now Visible As a &PRODUCT; Zone + In order to support zone-wide functions for VMware, changes have been made so that + &PRODUCT; is now aware of VMware Datacenters and can map each Datacenter to a &PRODUCT; + zone. Previously, &PRODUCT; was only aware of VMware Clusters, a smaller organizational + unit than Datacenters. This implies that a single &PRODUCT; zone could possibly contain + clusters from different VMware Datacenters. In order for zone-wide functions, such as + zone-wide primary storage, to work for VMware hosts, &PRODUCT; has to make sure that a + zone contains only a single VMware Datacenter. Therefore, when you are creating a new + &PRODUCT; zone, you will now be able to select a VMware Datacenter for the zone. If you + are provisioning multiple VMware Datacenters, each one will be set up as a single zone in + &PRODUCT;. - Limitation: When used with VMware hosts, this - feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch - 4. + If you are upgrading from a previous &PRODUCT; version, and your existing deployment + contains a zone with clusters from multiple VMware Datacenters, that zone will not be + forcibly migrated to the new model. It will continue to function as before. However, any + new zone-wide operations, such as zone-wide primary storage, will not be available in + that zone. + +
+
+
+ Third-Party UI Plugin Framework + Using the new third-party plugin framework, you can write and install extensions to + &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the + Citrix-provided features. + The basic procedure for adding a UI plugin is explained in the Developer Guide. In + summary, the plugin developer creates the plugin code itself (in Javascript), a thumbnail + image, the plugin listing, and a CSS file. The &PRODUCT; administrator adds the folder + containing the plugin code under the &PRODUCT; PLUGINS folder and adds the plugin name to a + configuration file (plugins.js). + The next time the user refreshes the UI in the browser, the plugin will appear under the + Plugins button in the left navigation bar. + + + + + + plugin4.jpg: The plugin appears in the UI + + +
+
+ Networking Enhancements + The following new features provide additional networking functionality in &PRODUCT; + 4.2. +
+ IPv6 (Technical Preview) + &PRODUCT; 4.2 introduces initial support for IPv6. This feature is provided as a + technical preview only. Full support is planned for a future release.
Portable IPs - CLOUDSTACK-3236:Portable IPs in &PRODUCT; are nothing but elastic IPs that can - be transferred across geographically separated zones. As an administrator, you can - provision a pool of portable IPs at region level and are available for user consumption. - The users can acquire portable IPs if admin has provisioned portable public IPs at the - region level they are part of. These IPs can be used for any service within an advanced - zone. You can also use portable IPs for EIP service in Basic zones. Additionally, a - portable IP can be transferred from one network to another network. + Portable IPs in &PRODUCT; are elastic IPs that can be transferred across + geographically separated zones. As an administrator, you can provision a pool of portable + IPs at region level and are available for user consumption. The users can acquire portable + IPs if admin has provisioned portable public IPs at the region level they are part of. + These IPs can be used for any service within an advanced zone. You can also use portable + IPs for EIP service in Basic zones. Additionally, a portable IP can be transferred from + one network to another network.
N-Tier Applications - CLOUDSTACK-770:In &PRODUCT; 3.0.6, a functionality was added to allow users to - create a multi-tier application connected to a single instance of a Virtual Router that - supports inter-VLAN routing. Such a multi-tier application is called a virtual private - cloud (VPC). Users were also able to connect their multi-tier applications to a private - Gateway or a Site-to-Site VPN tunnel and route certain traffic to those gateways. For - &PRODUCT; 4.2, additional features are implemented to enhance VPC applications. + In &PRODUCT; 3.0.6, a functionality was added to allow users to create a multi-tier + application connected to a single instance of a Virtual Router that supports inter-VLAN + routing. Such a multi-tier application is called a virtual private cloud (VPC). Users were + also able to connect their multi-tier applications to a private Gateway or a Site-to-Site + VPN tunnel and route certain traffic to those gateways. For &PRODUCT; 4.2, additional + features are implemented to enhance VPC applications. - Internal Load Balancing between VPC tiers + - Source NAT and ACL support on private gateways + - Multiple private gateway support + - Support for ACL deny rules + - ACL support on all layer 4 protocols + - Support up to 8 VPN Gateways + - Support for blacklisting routes + - NetScaler support for VPC load balancing + - Support for KVM hypervisor + - Support for the ability to simultaneously deploy an instance on a VPC Tier and one - or more Shared Networks + + + + + + + + + + + + + + + + + + + +
+ Support for KVM + VPC is now supported on KVM hypervisors. +
+
+ Support for Simultaneously Deploying a VM on VPC and Multiple Shared + Networks + Support for the ability to simultaneously deploy a VM on a VPC tier and one or more + Shared networks is supported. +
+
+ Load Balancing Support for VPC + In a VPC, you can configure two types of load balancing—external LB and + internal LB. External LB is nothing but a LB rule created to redirect the traffic + received at a public IP of the VPC virtual router. The traffic is load balanced within a + tier based on your configuration. Citrix NetScaler and VPC virtual router are supported + for external LB. When you use internal LB service, traffic received at a tier is load + balanced across different VMs within that tier. For example, traffic reached at Web tier + is redirected to another VM in that tier. External load balancing devices are not + supported for internal LB. The service is provided by a internal LB VM configured on the + target tier. +
+ Load Balancing Within a Tier (External LB) + A &PRODUCT; user or administrator may create load balancing rules that balance + traffic received at a public IP to one or more VMs that belong to a network tier that + provides load balancing service in a VPC. A user creates a rule, specifies an + algorithm, and assigns the rule to a set of VMs within a tier. +
+
+ Load Balancing Across Tiers + &PRODUCT; supports sharing workload across different tiers within your VPC. Assume + that multiple tiers are set up in your environment, such as Web tier and Application + tier. Traffic to each tier is balanced on the VPC virtual router on the public side. + If you want the traffic coming from the Web tier to the Application tier to be + balanced, use the internal load balancing feature offered by &PRODUCT;. +
+
+ Netscaler Support for VPC + Citrix NetScaler is supported for external LB. Certified version for this feature + is NetScaler 10.0 Build 74.4006.e. +
+
+
+ Enhanced Access Control List + Network Access Control List (ACL) on the VPC virtual router is enhanced. The network + ACLs can be created for the tiers only if the NetworkACL service is supported. In + &PRODUCT; terminology, Network ACL is a group of Network ACL items. Network ACL items + are nothing but numbered rules that are evaluated in order, starting with the lowest + numbered rule. These rules determine whether traffic is allowed in or out of any tier + associated with the network ACL. You need to add the Network ACL items to the Network + ACL, then associate the Network ACL with a tier. Network ACL is associated with a VPC + and can be assigned to multiple VPC tiers within a VPC. A Tier is associated with a + Network ACL at all the times. Each tier can be associated with only one ACL. + The default Network ACL is used when no ACL is associated. Default behavior is all + incoming traffic to guest networks is blocked and all outgoing traffic from guest + networks is allowed. Default network ACL cannot be removed or modified. +
+ ACL on Private Gateway + The traffic on the VPC private gateway is controlled by creating both ingress and + egress network ACL rules. The ACLs contains both allow and deny rules. As per the + rule, all the ingress traffic to the private gateway interface and all the egress + traffic out from the private gateway interface are blocked. You can change this + default behaviour while creating a private gateway. +
+
+ Allow ACL on All Level 4 Protocols + In addition to the existing protocol support for ICMP, TCP, UDP, support for All + Level 4 protocols is added. The protocol numbers from 0 to 255 are supported. +
+
+ Support for ACL Deny Rules + In addition to the existing support for ACL Allow rules, support for ACL Deny + rules has been added in &PRODUCT; 4.2. As part of this, two operations are supported: + Number and Action. You can configure a rule, allow or deny, by using action. Use + Number to add a rule number. +
+
+
+ Deploying VMs to a VPC Tier and Shared Networks + &PRODUCT; allows you to deploy VMs on a VPC tier and one or more shared networks. + With this feature, the VMs deployed in a multi-tier application can receive services + offered by a service provider over the shared network. One example of such a service is + monitoring service. +
+
+ Adding a Private Gateway to a VPC + A private gateway can be added by the root admin only. The VPC private network has + 1:1 relationship with the NIC of the physical network. You can configure multiple + private gateways to a single VPC. No gateways with duplicated VLAN and IP are allowed in + the same data center. +
+ Source NAT on Private Gateway + You might want to deploy multiple VPCs with the same super CIDR and guest tier + CIDR. Therefore, multiple guest VMs from different VPCs can have the same IPs to reach + a enterprise data center through the private gateway. In such cases, a NAT service + need to be configured on the private gateway. If Source NAT is enabled, the guest VMs + in VPC reaches the enterprise network via private gateway IP address by using the NAT + service. + The Source NAT service on a private gateway can be enabled while adding the + private gateway. On deletion of a private gateway, source NAT rules specific to the + private gateway are deleted. +
+
+ VPN Gateways + Support up to 8 VPN Gateways is added. +
+
+ Creating a Static Route + &PRODUCT; enables you to specify routing for the VPN connection you create. You + can enter one or CIDR addresses to indicate which traffic is to be routed back to the + gateway. +
+
+ Blacklisting Routes + &PRODUCT; enables you to block a list of routes so that they are not assigned to + any of the VPC private gateways. Specify the list of routes that you want to blacklist + in the blacklisted.routes global parameter. Note that the parameter + update affects only new static route creations. If you block an existing static route, + it remains intact and continue functioning. You cannot add a static route if the route + is blacklisted for the zone. +
+
+
+
+ Assigning VLANs to Isolated Networks + &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. + You can assign a VLAN ID when a network is created, just the way it's done for Shared + networks. + The former behaviour also is supported — VLAN is randomly allocated to a network + from the VNET range of the physical network when the network turns to Implemented state. + The VLAN is released back to the VNET pool when the network shuts down as a part of the + Network Garbage Collection. The VLAN can be re-used either by the same network when it is + implemented again, or by any other network. On each subsequent implementation of a + network, a new VLAN can be assigned. + + You cannot change a VLAN once it's assigned to the network. The VLAN remains with + the network for its entire life cycle. + +
+
+ Persistent Networks + &PRODUCT; 4.2 supports Persistent Networks. The network that you can provision without + having to deploy any VMs on it is called a Persistent Network. A Persistent Network can be + part of a VPC or a non-VPC environment. With the addition of this feature, you will have + the ability to create a network in &PRODUCT; in which physical devices can be deployed + without having to run any VMs. Additionally, you can deploy physical devices on that + network. Another advantages is that you can create a VPC with a tier that consists only + physical devices. For example, you might create a VPC for a three-tier application, deploy + VMs for Web and Application tier, and use physical machines for the Database tier. Another + use case is that if you are providing services by using physical hardware, you can define + the network as persistent and therefore even if all its VMs are destroyed the services + will not be discontinued.
Cisco VNMC Support - CLOUDSTACK-742:&PRODUCT; supports Cisco Virtual Network Management Center - (VNMC) on Cisco Nexus 1000v dvSwich-enabled VMware hypervisors. &PRODUCT; supports Cisco - ASA 1000v as an external Firewall provider when integrated with Cisco VNMC. - When Cisco VNMC is integrated with ASA 1000v Cloud Firewall and Cisco Nexus 1000v - dvSwitch in &PRODUCT; you will be able to: + Cisco Virtual Network Management Center (VNMC) provides centralized multi-device and + policy management for Cisco Network Virtual Services. When Cisco VNMC is integrated with + ASA 1000v Cloud Firewall and Cisco Nexus 1000v dvSwitch in &PRODUCT; you will be able to: Configure Cisco ASA 1000v Firewalls Create and apply security profiles that contain ACL policy sets for both ingress - and egress traffic, connection timeout, NAT policy sets, and TCP intercept - - - Consider the following use cases before using this feature: - - - A Cloud administrator adds VNMC as a network element by using the admin API - addCiscoVnmcResource after specifying the credentials - - - A Cloud administrator adds ASA 1000v appliances by using the admin API - addCiscoAsa1000vResource. You can configure one per guest network. - - - A Cloud administrator creates an Isolated guest network offering by using ASA - 1000v as the service provider for Firewall, Source NAT, Port Forwarding, and Static - NAT. + and egress traffic, and NAT policy sets + &PRODUCT; supports Cisco VNMC on Cisco Nexus 1000v dvSwich-enabled VMware + hypervisors.
VMware vNetwork Distributed vSwitch - CLOUDSTACK-772:&PRODUCT; 4.2 supports VMware vSphere Distributed Switch (VDS) - for virtual network configuration in a VMware vSphere environment. Each vCenter server - instance can support up to 128 VDSs and each VDS 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. VDS is controlled as - a single distributed switch at the datacenter level. So there needed a component to - ensure that the network configurations on the source and the destination virtual switch - are consistent and will allow the VM to operate without breaking connectivity or network - policies. Particularly during migration of VM across hosts, the sync up among peers need - to be taken care. However in case of distributed vSwitch during VMotion, the vCenter - server, would update the vSwitch modules on the hosts in cluster accordingly. -
-
- 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 specified in - the AddCluster 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. - -
+ &PRODUCT; supports VMware vSphere Distributed Switch (VDS) for virtual network + configuration in a VMware vSphere environment. Each vCenter server instance can support up + to 128 VDSs and each VDS can manage up to 500 VMware hosts. &PRODUCT; supports configuring + virtual networks in a deployment with a mix of Virtual Distributed Switch, Standard + Virtual Switch and Nexus 1000v Virtual Switch. +
+
+ IP Reservation in Isolated Guest Networks + In Isolated guest networks in &PRODUCT; 4.2, a part of the guest IP address space can + be reserved for non-&PRODUCT; VMs or physical servers. To do so, you configure a range of + Reserved IP addresses by specifying the CIDR when a guest network is in Implemented state. + The advantage of having this feature is that if your customers wish to have non-&PRODUCT; + controlled VMs or physical servers on the same network, they can use a part of the IP + address space that is primarily provided to the guest network. When IP reservation is + configured, the administrator can add additional VMs or physical servers that are not part + of &PRODUCT; to the same network and assign them the Reserved IP addresses. &PRODUCT; + guest VMs cannot acquire IPs from the Reserved IP Range. +
+
+ Dedicated Resources: Public IP Addresses and VLANs Per Account + &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs + exclusively for an account. During zone creation, you can continue to define a set of + VLANs and multiple public IP ranges. This feature extends the functionality to enable you + to dedicate a fixed set of VLANs and guest IP addresses for a tenant. + This feature provides you the following capabilities: + + + Reserve a VLAN range and public IP address range from an Advanced zone and assign + it to an account + + + Disassociate a VLAN and public IP address range from an account + + + + Ensure that you check whether the required range is available and conforms to + account limits. The maximum IPs per account limit cannot be superseded. + +
+
+ Enhanced Juniper SRX Support for Egress Firewall Rules + Egress firewall rules were previously supported on virtual routers, and now they are + also supported on Juniper SRX external networking devices. + Egress traffic originates from a private network to a public network, such as the + Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed + from a guest network to the Internet. However, you can control the egress traffic in an + Advanced zone by creating egress firewall rules. When an egress firewall rule is applied, + the traffic specific to the rule is allowed and the remaining traffic is blocked. When all + the firewall rules are removed the default policy, Block, is applied. + + Egress firewall rules are not supported on Shared networks. They are supported only + on Isolated guest networks. + +
+
+ Configuring the Default Egress Policy + The default egress policy for Isolated guest network can be configured by using + Network offering. Use the create network offering option to determine whether the default + policy should be block or allow all the traffic to the public network from a guest + network. Use this network offering to create the network. If no policy is specified, by + default all the traffic is allowed from the guest network that you create by using this + network offering. + You have two options: Allow and Deny. + If you select Allow for a network offering, by default egress traffic is allowed. + However, when an egress rule is configured for a guest network, rules are applied to block + the specified traffic and rest are allowed. If no egress rules are configured for the + network, egress traffic is accepted. If you select Deny for a network offering, by default + egress traffic for the guest network is blocked. However, when an egress rules is + configured for a guest network, rules are applied to allow the specified traffic. While + implementing a guest network, &PRODUCT; adds the firewall egress rule specific to the + default egress policy for the guest network. + This feature is supported only on virtual router and Juniper SRX. +
+
+ Non-Contiguous VLAN Ranges + &PRODUCT; provides you with the flexibility to add non contiguous VLAN ranges to your + network. The administrator can either update an existing VLAN range or add multiple non + contiguous VLAN ranges while creating a zone. You can also use the UpdatephysicalNetwork + API to extend the VLAN range. +
+
+ Isolation in Advanced Zone Using Private VLAN + Isolation of guest traffic in shared networks can be achieved by using Private VLANs + (PVLAN). PVLANs provide Layer 2 isolation between ports within the same VLAN. In a + PVLAN-enabled shared network, a user VM cannot reach other user VM though they can reach + the DHCP server and gateway, this would in turn allow users to control traffic within a + network and help them deploy multiple applications without communication between + application as well as prevent communication with other users’ VMs. + + + Isolate VMs in a shared networks by using Private VLANs. + + + Supported on KVM, XenServer, and VMware hypervisors. + + + PVLAN-enabled shared network can be a part of multiple networks of a guest VM. + + + + For further reading: + + + Understanding Private VLANs + + + Cisco Systems' Private VLANs: + Scalable Security in a Multi-Client Environment + + + Private VLAN (PVLAN) on vNetwork Distributed + Switch - Concept Overview (1010691) + + +
+
+ Configuring Multiple IP Addresses on a Single NIC + (Supported on XenServer, KVM, and VMware hypervisors) + &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 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. +
+
+ Adding Multiple IP Ranges + (Supported on KVM, xenServer, and VMware hypervisors) + &PRODUCT; 4.2 provides you with the flexibility to add guest IP ranges from different + subnets in Basic zones and security groups-enabled Advanced zones. For security + groups-enabled Advanced zones, it implies multiple subnets can be added to the same VLAN. + With the addition of this feature, you will be able to add IP address ranges from the same + subnet or from a different one when IP address are exhausted. This would in turn allows + you to employ higher number of subnets and thus reduce the address management + overhead. + Ensure that you manually configure the gateway of the new subnet before adding the IP + range. Note that &PRODUCT; supports only one gateway for a subnet; overlapping subnets are + not currently supported. + You can also delete IP ranges. This operation fails if an IP from the remove range is + in use. If the remove range contains the IP address on which the DHCP server is running, + &PRODUCT; acquires a new IP from the same subnet. If no IP is available in the subnet, the + remove operation fails. + + The feature can only be implemented on IPv4 addresses. + +
+
+ Support for Multiple Networks in VMs + (Supported on XenServer, VMware and KVM hypervisors) + &PRODUCT; 4.2 provides you the ability to add and remove multiple networks to a VM. + You can remove a network from a VM and add a new network. You can also change the default + network of a VM. With this functionality, hybrid or traditional server loads can be + accommodated with ease. + For adding or removing a NIC to work on VMware, ensure that vm-tools are running on + guest VMs. +
+
+ Global Server Load Balancing + &PRODUCT; 4.2 supports Global Server Load Balancing (GSLB) functionalities to provide + business continuity by load balancing traffic to an instance on active zones only in case + of zone failures . &PRODUCT; achieve this by extending its functionality of integrating + with NetScaler Application Delivery Controller (ADC), which also provides various GSLB + capabilities, such as disaster recovery and load balancing. The DNS redirection technique + is used to achieve GSLB in &PRODUCT;. In order to support this functionality, region level + services and service provider are introduced. A new service 'GSLB' is introduced as a + region level service. The GSLB service provider is introduced that will provider the GSLB + service. Currently, NetScaler is the supported GSLB provider in &PRODUCT;. GSLB + functionality works in an Active-Active data center environment. +
+
+ Enhanced Load Balancing Services Using External Provider on Shared VLANs + Network services like Firewall, Load Balancing, and NAT are now supported in shared + networks created in an advanced zone. In effect, the following network services shall be + made available to a VM in a shared network: Source NAT, Static NAT, Port Forwarding, + Firewall and Load balancing. Subset of these service can be chosen while creating a + network offering for shared networks. Services available in a shared network is defined by + the network offering and the service chosen in the network offering. For example, if + network offering for a shared network has source NAT service enabled, a public IP shall be + provisioned and source NAT is configured on the firewall device to provide public access + to the VMs on the shared network. Static NAT, Port Forwarding, Load Balancing, and + Firewall services shall be available only on the acquired public IPs associated with a + shared network. + Additionally, Netscaler and Juniper SRX firewall device can be configured inline or + side-by-side mode.
Health Checks for Load Balanced Instances - CLOUDSTACK-4243: This feature is supported only on NetScaler version 10.0 and - beyond. The Nitro API is not compatible with NetScaler 9.3 and therefore this version is - not supported for this feature. + This feature is supported only on NetScaler version 10.0 and beyond. - CLOUDSTACK-816:(NetScaler load balancer only) A load balancer rule distributes - requests among a pool of services (a service in this context means an application running - on a virtual machine). When creating a load balancer rule, you can specify a health check - which will ensure that the rule forwards requests only to services that are healthy - (running and available). This is in addition to specifying the stickiness policy, - algorithm, and other load balancer rule options. You can configure one health check policy - per load balancer rule. - When a health check is in effect, the load balancer will stop forwarding requests to - any resources that it has found to be unhealthy. If the resource later becomes available + (NetScaler load balancer only) A load balancer rule distributes requests among a pool + of services (a service in this context means an application running on a virtual machine). + When creating a load balancer rule, you can specify a health check which will ensure that + the rule forwards requests only to services that are healthy (running and available). When + a health check is in effect, the load balancer will stop forwarding requests to any + resources that it has found to be unhealthy. If the resource later becomes available again, the periodic health check (periodicity is configurable) will discover it and the - resource will once again be added to the pool of resources that can receive requests from - the load balancer. - You can delete or modify existing health check policies. + resource will once again be made available to the load balancer. To configure how often the health check is performed by default, use the global configuration setting healthcheck.update.interval. This default applies to all the health check policies in the cloud. You can override this value for an individual health check policy.
+
+
+ Host and Virtual Machine Enhancements + The following new features expand the ways you can use hosts and virtual + machines. +
+ VMware DRS Support + The VMware vSphere Distributed Resources Scheduler (DRS) is supported. +
+
+ Windows 8 and Windows Server 2012 as VM Guest OS + (Supported on XenServer, VMware, and KVM) + Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual + machines. The OS would be made available the same as any other, by uploading an ISO or a + template. The instructions for uploading ISOs and templates are given in the + Administrator's Guide. + + Limitation: When used with VMware hosts, this + feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch + 4. + + +
+
+ Change Account Ownership of Virtual Machines + A root administrator can now change the ownership of any virtual machine from one + account to any other account. A domain or sub-domain administrator can do the same for VMs + within the domain from one account to any other account in the domain. +
+
+ Private Pod, Cluster, or Host + Dedicating pod, cluster or host to a specific domain/account means that the + domain/account will have sole access to the dedicated pod, cluster or hosts such that + scalability, security and manageability within a domain/account can be improved. The + resources which belong to that tenant will be placed into that dedicated pod, cluster or + host. +
+
+ Resizing Volumes + &PRODUCT; provides the ability to resize data disks; &PRODUCT; controls volume size by + using disk offerings. This provides &PRODUCT; administrators with the flexibility to + choose how much space they want to make available to the end users. Volumes within the + disk offerings with the same storage tag can be resized. For example, if you only want to + offer 10, 50, and 100 GB offerings, the allowed resize should stay within those limits. + That implies if you define a 10 GB, a 50 GB and a 100 GB disk offerings, a user can + upgrade from 10 GB to 50 GB, or 50 GB to 100 GB. If you create a custom-sized disk + offering, then you have the option to resize the volume by specifying a new, larger size. + Additionally, using the resizeVolume API, a data volume can be moved from a static disk + offering to a custom disk offering with the size specified. This functionality allows + those who might be billing by certain volume sizes or disk offerings to stick to that + model, while providing the flexibility to migrate to whatever custom size necessary. This + feature is supported on KVM, XenServer, and VMware hosts. However, shrinking volumes is + not supported on VMware hosts +
+
+ VMware Volume Snapshot Improved Performance + When you take a snapshot of a data volume on VMware, &PRODUCT; will now use a more + efficient storage technique to improve performance. + Previously, every snapshot was immediately exported from vCenter to a mounted NFS + share and packaged into an OVA file format. This operation consumed time and resources. + Starting from 4.2, the original file formats (e.g., VMDK) provided by vCenter will be + retained. An OVA file will only be created as needed, on demand. + The new process applies only to newly created snapshots after upgrade to &PRODUCT; + 4.2. Snapshots that have already been taken and stored in OVA format will continue to + exist in that format, and will continue to work as expected. +
+
+ Storage Migration: XenMotion and vMotion + (Supported on XenServer and VMware) + Storage migration allows VMs to be moved from one host to another, where the VMs are + not located on storage shared between the two hosts. It provides the option to live + migrate a VM’s disks along with the VM itself. It is now possible to migrate a VM from one + XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks are on + local storage, or even to migrate a VM’s disks from one storage repository to another, all + while the VM is running. +
+
+ Configuring Usage of Linked Clones on VMware + (For ESX hypervisor in conjunction with vCenter) + In &PRODUCT; 4.2, the creation of VMs as full clones is allowed. In previous versions, + only linked clones were possible. + For a full description of clone types, refer to VMware documentation. In summary: A + full clone is a copy of an existing virtual machine which, once created, does not depend + in any way on the original virtual machine. A linked clone is also a copy of an existing + virtual machine, but it has ongoing dependency on the original. A linked clone shares the + virtual disk of the original VM, and retains access to all files that were present at the + time the clone was created. + A new global configuration setting has been added, vmware.create.full.clone. When the + administrator sets this to true, end users can create guest VMs only as full clones. The + default value is true for new installations. For customers upgrading from a previous + version of &PRODUCT;, the default value of vmware.create.full.clone is false. +
+
+ VM Deployment Rules + Rules can be set up to ensure that particular VMs are not placed on the same physical + host. These "anti-affinity rules" can increase the reliability of applications by ensuring + that the failure of a single host can not take down the entire group of VMs supporting a + given application. See Affinity Groups in the &PRODUCT; 4.2 Administration Guide. +
+
+ CPU and Memory Scaling for Running VMs + (Supported on VMware and XenServer) + You can now change the CPU and RAM values for a running virtual machine. In previous + versions of &PRODUCT;, this could only be done on a stopped VM. + It is not always possible to accurately predict the CPU and RAM requirements when you + first deploy a VM. You might need to increase or decrease these resources at any time + during the life of a VM. With the new ability to dynamically modify CPU and RAM levels, + you can change these resources for a running VM without incurring any downtime. + Dynamic CPU and RAM scaling can be used in the following cases: + + + New VMs that are created after the installation of &PRODUCT; 4.2. If you are + upgrading from a previous version of &PRODUCT;, your existing VMs created with + previous versions will not have the dynamic scaling capability. + + + User VMs on hosts running VMware and XenServer. + + + System VMs on VMware. + + + VM Tools or XenServer Tools must be installed on the virtual machine. + + + The new requested CPU and RAM values must be within the constraints allowed by the + hypervisor and the VM operating system. + + + To configure this feature, use the following new global configuration + variables: + + + enable.dynamic.scale.vm: Set to True to enable the feature. By default, the + feature is turned off. + + + scale.retry: How many times to attempt the scaling operation. Default = 2. + + +
+
+ CPU and Memory Over-Provisioning + (Supported for XenServer, KVM, and VMware) + In &PRODUCT; 4.2, CPU and memory (RAM) over-provisioning factors can be set for each + cluster to change the number of VMs that can run on each host in the cluster. This helps + optimize the use of resources. By increasing the over-provisioning ratio, more resource + capacity will be used. If the ratio is set to 1, no over-provisioning is done. + In previous releases, &PRODUCT; did not perform memory over-provisioning. It performed + CPU over-provisioning based on a ratio configured by the administrator in the global + configuration setting cpu.overprovisioning.factor. Starting in 4.2, the administrator can + specify a memory over-provisioning ratio, and can specify both CPU and memory + over-provisioning ratios on a per-cluster basis, rather than only on a global + basis. + In any given cloud, the optimum number of VMs for each host is affected by such things + as the hypervisor, storage, and hardware configuration. These may be different for each + cluster in the same cloud. A single global over-provisioning setting could not provide the + best utilization for all the different clusters in the cloud. It had to be set for the + lowest common denominator. The new per-cluster setting provides a finer granularity for + better utilization of resources, no matter where the &PRODUCT; placement algorithm decides + to place a VM. +
+
+ Kickstart Installation for Bare Metal Provisioning + &PRODUCT; 4.2 supports the kick start installation method for RPM-based Linux + operating systems on baremetal hosts in basic zones. Users can provision a baremetal host + managed by &PRODUCT; as long as they have the kick start file and corresponding OS + installation ISO ready. + Tested on CentOS 5.5, CentOS 6.2, CentOS 6.3, Ubuntu 12.04. + For more information, see the Baremetal Installation Guide. +
+
+ Enhanced Bare Metal Support on Cisco UCS + You can now more easily provision new Cisco UCS server blades into &PRODUCT; for use + as bare metal hosts. The goal is to enable easy expansion of the cloud by leveraging the + programmability of the UCS converged infrastructure and &PRODUCT;’s knowledge of the cloud + architecture and ability to orchestrate. With this new feature, &PRODUCT; can + automatically understand the UCS environment, server profiles, etc. to make it easy to + deploy a bare metal OS on a Cisco UCS. +
+
+ Changing a VM's Base Image + Every VM is created from a base image, which is a template or ISO which has been + created and stored in &PRODUCT;. Both cloud administrators and end users can create and + modify templates, ISOs, and VMs. + In &PRODUCT; 4.2, there is a new way to modify an existing VM. You can change an + existing VM from one base image to another. For example, suppose there is a template based + on a particular operating system, and the OS vendor releases a software patch. The + administrator or user naturally wants to apply the patch and then make sure existing VMs + start using it. Whether a software update is involved or not, it's also possible to simply + switch a VM from its current template to any other desired template. +
+
+ Reset VM on Reboot + In &PRODUCT; 4.2, you can specify that you want to discard the root disk and create a + new one whenever a given VM is rebooted. This is useful for secure environments that need + a fresh start on every boot and for desktops that should not retain state. The IP address + of the VM will not change due to this operation. +
+
+ Virtual Machine Snapshots for VMware + (VMware hosts only) In addition to the existing &PRODUCT; ability to snapshot + individual VM volumes, you can now take a VM snapshot to preserve all the VM's data + volumes as well as (optionally) its CPU/memory state. This is useful for quick restore of + a VM. For example, you can snapshot a VM, then make changes such as software upgrades. If + anything goes wrong, simply restore the VM to its previous state using the previously + saved VM snapshot. + The snapshot is created using the VMware native snapshot facility. The VM snapshot + includes not only the data volumes, but optionally also whether the VM is running or + turned off (CPU state) and the memory contents. The snapshot is stored in &PRODUCT;'s + primary storage. + VM snapshots can have a parent/child relationship. Each successive snapshot of the + same VM is the child of the snapshot that came before it. Each time you take an additional + snapshot of the same VM, it saves only the differences between the current state of the VM + and the state stored in the most recent previous snapshot. The previous snapshot becomes a + parent, and the new snapshot is its child. It is possible to create a long chain of these + parent/child snapshots, which amount to a "redo" record leading from the current state of + the VM back to the original. +
+
+ Increased Userdata Size When Deploying a VM + You can now specify up to 32KB of userdata when deploying a virtual machine through + the &PRODUCT; UI or the deployVirtualMachine API call. +
+
+ Set VMware Cluster Size Limit Depending on VMware Version + The maximum number of hosts in a vSphere cluster is determined by the VMware + hypervisor software. For VMware versions 4.2, 4.1, 5.0, and 5.1, the limit is 32 + hosts. + For &PRODUCT; 4.2, the global configuration setting vmware.percluster.host.max has + been removed. The maximum number of hosts in a VMware cluster is now determined by the + underlying hypervisor software. + + Best Practice: It is advisable for VMware clusters in &PRODUCT; to be smaller than + the VMware hypervisor's maximum size. A cluster size of up to 8 hosts has been found + optimal for most real-world situations. + +
+
+ Limiting Resource Usage + Previously in &PRODUCT;, resource usage limit was imposed based on the resource count, + that is, restrict a user or domain on the basis of the number of VMs, volumes, or + snapshots used. In &PRODUCT; 4.2, a new set of resource types has been added to the + existing pool of resources (VMs, Volumes, and Snapshots) to support the customization + model—need-basis usage, such as large VM or small VM. The new resource types are now + broadly classified as CPU, RAM, Primary storage, and Secondary storage. &PRODUCT; 4.2 + allows the root administrator to impose resource usage limit by the following resource + types for Domain, Project and Accounts. + + + CPUs + + + Memory (RAM) + + + Primary Storage (Volumes) + + + Secondary Storage (Snapshots, Templates, ISOs) + + +
+
+
+ Monitoring, Maintenance, and Operations Enhancements + +
+ Publish and Subscribe for Event Notification + An event is essentially a significant or meaningful change in the state of both + virtual and physical resources associated with a cloud environment. In &PRODUCT; an event + could be a state change of virtual or psychical resources, an action performed by an user + (action events), or policy based events (alerts). In &PRODUCT; 4.2, a new event + notification framework has been added. This framework provides a means for the Management + Server components to publish and subscribe to &PRODUCT; events. Event notification is + achieved by implementing the concept of event bus abstraction in the Management Server. + A new event for state change, resource state change, is introduced as part of Event + notification framework. Every resource, such as user VM, volume, NIC, network, public IP, + snapshot, and template, is associated with a state machine and generates events as part of + the state change. That implies that a change in the state of a resource results in a state + change event, and the event is published in the corresponding state machine on the event + bus. All the &PRODUCT; events (alerts, action events, usage events) and the additional + category of resource state change events, are published on to the events bus. +
+
+ Deleting and Archiving Events and Alerts + In addition to viewing a list of events and alerts in the UI, the administrator can + now delete and archive them. In order to support deleting and archiving alerts, the + following global parameters have been added: + + + alert.purge.delay: The alerts older than + specified number of days are purged. Set the value to 0 to never purge alerts + automatically. + + + alert.purge.interval: The interval in seconds to + wait before running the alert purge thread. The default is 86400 seconds (one + day). + + + + Archived alerts or events cannot be viewed in the UI, or by using the API. They are + maintained in the database for auditing or compliance purposes. + +
+
+ Increased Granularity for Configuration Parameters + Some configuration parameters which were previously available only at the global level + of the cloud can now be set for smaller components of the cloud, such as at the zone + level. To set these parameters, look for the new Settings tab in the UI. You will find it + on the detail page for an account, cluster, zone, or primary storage. + The account level parameters are: remote.access.vpn.client.iprange, + allow.public.user.templates, use.system.public.ips, and + use.system.guest.vlans + The cluster level parameters are + cluster.storage.allocated.capacity.notificationthreshold, + cluster.storage.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.notificationthreshold, + cluster.memory.allocated.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.disablethreshold, + cluster.memory.allocated.capacity.disablethreshold, + cpu.overprovisioning.factor, mem.overprovisioning.factor, + vmware.reserve.cpu, and vmware.reserve.mem. + The zone level parameters are + pool.storage.allocated.capacity.disablethreshold, + pool.storage.capacity.disablethreshold, + storage.overprovisioning.factor, network.throttling.rate, + guest.domain.suffix, router.template.xen, + router.template.kvm, router.template.vmware, + router.template.hyperv, router.template.lxc, + enable.dynamic.scale.vm, use.external.dns, and + blacklisted.routes. +
+
+ API Request Throttling + In &PRODUCT; 4.2, you can limit the rate at which API requests can be placed for each + account. This is useful to avoid malicious attacks on the Management Server, prevent + performance degradation, and provide fairness to all accounts. + If the number of API calls exceeds the threshold, an error message is returned for any + additional API calls. The caller will have to retry these API calls at another + time. + To control the API request throttling, use the following new global configuration + settings: + + + api.throttling.enabled - Enable/Disable API throttling. By default, this setting + is false, so API throttling is not enabled. + + + api.throttling.interval (in seconds) - Time interval during which the number of + API requests is to be counted. When the interval has passed, the API count is reset to + 0. + + + api.throttling.max - Maximum number of APIs that can be placed within the + api.throttling.interval period. + + + api.throttling.cachesize - Cache size for storing API counters. Use a value higher + than the total number of accounts managed by the cloud. One cache entry is needed for + each account, to store the running API total for that account within the current time + window. + + +
+
+ Sending Alerts to External SNMP and Syslog Managers + In addition to showing administrator alerts on the Dashboard in the &PRODUCT; UI and + sending them in email, &PRODUCT; now can also send the same alerts to external SNMP or + Syslog management software. This is useful if you prefer to use an SNMP or Syslog manager + to monitor your cloud. + The supported protocol is SNMP version 2. +
+
+ Changing the Default Password Encryption + Passwords are encoded when creating or updating users. The new default preferred + encoder, replacing MD5, is SHA256. It is more secure than MD5 hashing. If you take no + action to customize password encryption and authentication, SHA256 Salt will be + used. + If you prefer a different authentication mechanism, &PRODUCT; 4.2 provides a way for + you to determine the default encoding and authentication mechanism for admin and user + logins. Two new configurable lists have been introduced: userPasswordEncoders and + userAuthenticators. userPasswordEncoders allow you to configure the order of preference + for encoding passwords, and userAuthenticator allows you to configure the order in which + authentication schemes are invoked to validate user passwords. + The plain text user authenticator has been modified not to convert supplied passwords + to their md5 sums before checking them with the database entries. It performs a simple + string comparison between retrieved and supplied login passwords instead of comparing the + retrieved md5 hash of the stored password against the supplied md5 hash of the password, + because clients no longer hash the password. +
+
+ Log Collection Utility cloud-bugtool + &PRODUCT; provides a command-line utility called cloud-bugtool to make it easier to + collect the logs and other diagnostic data required for troubleshooting. This is + especially useful when interacting with Citrix Technical Support. + You can use cloud-bugtool to collect the following: + + + Basic system and environment information and network configuration including IP + addresses, routing, and name resolver settings + + + Information about running processes + + + Management Server logs + + + System logs in /var/log/ + + + Dump of the cloud database + + + + cloud-bugtool collects information which might be considered sensitive and + confidential. Using the --nodb option to avoid the cloud database can + reduce this concern, though it is not guaranteed to exclude all sensitive data. + + +
- Snaphotting, backups, cloning and System VMs for RBD Primary Storage + Snaphotting, Backups, Cloning and System VMs for RBD Primary Storage These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt 0.9.14 on the KVM hypervisors. - CLOUDSTACK-1191: - With this release &PRODUCT; will leverage the features of RBD format 2. This allows + This release of &PRODUCT; will leverage the features of RBD format 2. This allows snapshotting and backing up those snapshots. Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they are not RBD diffs. This because when restoring a backup of a snapshot it is not mandatory that this backup is deployed on RBD again, it could also be a NFS Primary Storage. - Another key feature of RBD format 2 is cloning and with this release templates will be - copied to Primary Storage once and using the cloning mechanism new disks will be cloned - from this parent template. This saves space and decreases deployment time for Instances + Another key feature of RBD format 2 is cloning. With this release templates will be + copied to Primary Storage once and by using the cloning mechanism new disks will be cloned + from this parent template. This saves space and decreases deployment time for instances dramatically. - Cloning will however only work with new templates and when they are freshly downloaded - to primary storage. Templates currently stored on RBD Primary Storage are in RBD format 1 - which does not support cloning. Loglevel debug on the Agent will show if cloning is used - when deploying a template or not. - Before this release a NFS Primary Storage was still required for running the System - VMs from. The reason behind this was a so called 'patch disk' which was generated by the + Before this release, a NFS Primary Storage was still required for running the System + VMs from. The reason was a so called 'patch disk' that was generated by the hypervisor which contained metadata for the System VM. The scripts generating this disk didn't support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of the patch disk a VirtIO serial console is used to pass meta information to System VMs. This enabled the deployment of System VMs on RBD Primary Storage.
-
- Disk I/O polling and throttling - CLOUDSTACK-1192: - On KVM hypervisors polling and throttling of disk I/Os is supported. Per disk disk attached to - an Instance the usage server will record the amount of IOps. - Per disk offering you are able to specify the number of Read and Write I/Os. Trottling is - done by Qemu/KVM. - Both polling and throttling only works with KVM and with all types of Primary Storage. -
Issues Fixed in 4.2.0 @@ -2219,6 +3032,1919 @@ service cloudstack-agent start
+ + API Changes from 4.1 to 4.2 +
+ Added API Commands in 4.2 +
+ Secondary Storage + + + addImageStore (Adds all types of secondary storage providers, S3/Swift/NFS) + + + createSecondaryStagingStore (Adds a staging secondary storage in each zone) + + + listImageStores (Lists all secondary storages, S3/Swift/NFS) + + + listSecondaryStagingStores (Lists all staging secondary storages) + + + addS3 (Adds a Amazon Simple Storage Service instance.) It is recommended to use + addImageStore instead. + + + listS3s (Lists all the Amazon Simple Storage Service instances.) It is recommended + to use listImageStores instead. + + +
+
+ VM Snapshot + + + createVMSnapshot (Creates a virtual machine snapshot; see ) + + + deleteVMSnapshot (Deletes a virtual machine snapshot) + + + listVMSnapshot (Shows a virtual machine snapshot) + + + revertToVMSnapshot (Returns a virtual machine to the state and data saved in a + given snapshot) + + +
+
+ Load Balancer Health Check + + + createLBHealthCheckPolicy (Creates a new health check policy for a load balancer + rule; see ) + + + deleteLBHealthCheckPolicy (Deletes an existing health check policy from a load + balancer rule) + + + listLBHealthCheckPolicies (Displays the health check policy for a load balancer + rule) + + +
+
+ Egress Firewall Rules + + + createEgressFirewallRules (Creates an egress firewall rule on the guest network; + see ) + + + deleteEgressFirewallRules (Deletes a egress firewall rule on the guest + network.) + + + listEgressFirewallRules (Lists the egress firewall rules configured for a guest + network.) + + +
+
+ SSH Key + + + resetSSHKeyForVirtualMachine (Resets the SSHkey for virtual machine.) + + +
+
+ Bare Metal + + + addBaremetalHost (Adds a new host. Technically, this API command was present in + v3.0.6, but its functionality was disabled. See ) + + + addBaremetalDhcp (Adds a DHCP server for bare metal hosts) + + + addBaremetalPxePingServer (Adds a PXE PING server for bare metal hosts) + + + addBaremetalPxeKickStartServer (Adds a PXE server for bare metal hosts) + + + listBaremetalDhcp (Shows the DHCP servers currently defined for bare metal + hosts) + + + listBaremetalPxePingServer (Shows the PXE PING servers currently defined for bare + metal hosts) + + +
+
+ NIC + + + addNicToVirtualMachine (Adds a new NIC to the specified VM on a selected network; + see ) + + + removeNicFromVirtualMachine (Removes the specified NIC from a selected VM.) + + + updateDefaultNicForVirtualMachine (Updates the specified NIC to be the default one + for a selected VM.) + + + addIpToNic (Assigns secondary IP to a NIC.) + + + removeIpFromNic (Assigns secondary IP to a NIC.) + + + listNics (Lists the NICs associated with a VM.) + + +
+
+ Regions + + + addRegion (Registers a Region into another Region; see ) + + + updateRegion (Updates Region details: ID, Name, Endpoint, User API Key, and User + Secret Key.) + + + removeRegion (Removes a Region from current Region.) + + + listRegions (Get all the Regions. They can be filtered by using the ID or + Name.) + + +
+
+ User + + + getUser (This API can only be used by the Admin. Get user account details by using + the API Key.) + + +
+
+ API Throttling + + + getApiLimit (Show number of remaining APIs for the invoking user in current + window) + + + resetApiLimit (For root admin, if accountId parameter is passed, it will reset + count for that particular account, otherwise it will reset all counters) + + + resetApiLimit (Reset the API count.) + + +
+
+ Locking + + + lockAccount (Locks an account) + + + lockUser (Locks a user account) + + +
+
+ VM Scaling + + + scaleVirtualMachine (Scales the virtual machine to a new service offering.) + + +
+
+ Migrate Volume + + + migrateVirtualMachineWithVolume (Attempts migrating VM with its volumes to a + different host.) + + + listStorageProviders (Lists storage providers.) + + + findStoragePoolsForMigration (Lists storage pools available for migrating a + volume.) + + +
+
+ Dedicated IP and VLAN + + + dedicatePublicIpRange (Dedicates a Public IP range to an account.) + + + releasePublicIpRange (Releases a Public IP range back to the system pool.) + + + dedicateGuestVlanRange (Dedicates a guest VLAN range to an account.) + + + releaseDedicatedGuestVlanRange (Releases a dedicated guest VLAN range to the + system.) + + + listDedicatedGuestVlanRanges (Lists dedicated guest VLAN ranges.) + + +
+
+ Port Forwarding + + + updatePortForwardingRule (Updates a port forwarding rule. Only the private port + and the VM can be updated.) + + +
+
+ Scale System VM + + + scaleSystemVm (Scale the service offering for a systemVM, console proxy, or + secondary storage.) + + +
+
+ Deployment Planner + + + listDeploymentPlanners (Lists all the deployment planners available.) + + +
+
+ Archive and Delete Events and Alerts + + + archiveEvents (Archive one or more events.) + + + deleteEvents (Delete one or more events.) + + + archiveAlerts (Archive one or more alerts.) + + + deleteAlerts (Delete one or more alerts.) + + +
+
+ Host Reservation + + + releaseHostReservation (Releases host reservation.) + + +
+
+ Resize Volume + + + resizeVolume (Resizes a volume.) + + + updateVolume (Updates the volume.) + + +
+
+ Egress Firewall Rules + + + createEgressFirewallRule (Creates a egress firewall rule for a given network. ) + + + + deleteEgressFirewallRule (Deletes an egress firewall rule.) + + + listEgressFirewallRules (Lists all egress firewall rules for network.) + + +
+
+ Network ACL + + + updateNetworkACLItem (Updates ACL item with specified ID.) + + + createNetworkACLList (Creates a Network ACL for the given VPC.) + + + deleteNetworkACLList (Deletes a Network ACL.) + + + replaceNetworkACLList (Replaces ACL associated with a Network or private gateway.) + + + + listNetworkACLLists (Lists all network ACLs.) + + +
+
+ Resource Detail + + + addResourceDetail (Adds detail for the Resource.) + + + removeResourceDetail (Removes detail for the Resource.) + + + listResourceDetails (List resource details.) + + +
+
+ Nicira Integration + + + addNiciraNvpDevice (Adds a Nicira NVP device.) + + + deleteNiciraNvpDevice (Deletes a Nicira NVP device.) + + + listNiciraNvpDevices (Lists Nicira NVP devices.) + + + listNiciraNvpDeviceNetworks (Lists network that are using a Nicira NVP device.) + + + +
+
+ BigSwitch VNS + + + addBigSwitchVnsDevice (Adds a BigSwitch VNS device.) + + + deleteBigSwitchVnsDevice (Deletes a BigSwitch VNS device.) + + + listBigSwitchVnsDevices (Lists BigSwitch VNS devices.) + + +
+
+ Simulator + + + configureSimulator (Configures a simulator.) + + +
+
+ API Discovery + + + listApis (Lists all the available APIs on the server, provided by the API + Discovery plugin.) + + +
+
+ Global Load Balancer + + + createGlobalLoadBalancerRule (Creates a global load balancer rule.) + + + deleteGlobalLoadBalancerRule (Deletes a global load balancer rule.) + + + updateGlobalLoadBalancerRule (update global load balancer rules.) + + + listGlobalLoadBalancerRules (Lists load balancer rules.) + + + assignToGlobalLoadBalancerRule (Assign load balancer rule or list of load balancer + rules to a global load balancer rules.) + + + removeFromGlobalLoadBalancerRule (Removes a load balancer rule association with + global load balancer rule) + + +
+
+ Load Balancer + + + createLoadBalancer (Creates a Load Balancer) + + + listLoadBalancers (Lists Load Balancers) + + + deleteLoadBalancer (Deletes a load balancer) + + + configureInternalLoadBalancerElement (Configures an Internal Load Balancer + element.) + + + createInternalLoadBalancerElement (Create an Internal Load Balancer element.) + + + + listInternalLoadBalancerElements (Lists all available Internal Load Balancer + elements.) + + +
+
+ Affinity Group + + + createAffinityGroup (Creates an affinity or anti-affinity group.) + + + deleteAffinityGroup (Deletes an affinity group.) + + + listAffinityGroups (Lists all the affinity groups.) + + + updateVMAffinityGroup (Updates the affinity or anti-affinity group associations of + a VM. The VM has to be stopped and restarted for the new properties to take effect.) + + + + listAffinityGroupTypes (Lists affinity group types available.) + + +
+
+ Portable IP + + + createPortableIpRange (Adds a range of portable portable IPs to a Region.) + + + deletePortableIpRange (Deletes a range of portable portable IPs associated with a + Region.) + + + listPortableIpRanges (Lists portable IP ranges.) + + +
+
+ Internal Load Balancer VM + + + stopInternalLoadBalancerVM (Stops an Internal LB VM.) + + + startInternalLoadBalancerVM (Starts an existing Internal LB VM.) + + + listInternalLoadBalancerVMs (List internal LB VMs.) + + +
+
+ Network Isolation + + + listNetworkIsolationMethods (Lists supported methods of network isolation.) + + + +
+
+ Dedicated Resources + + + dedicateZone (Dedicates a zone.) + + + dedicatePod (Dedicates a pod.) + + + dedicateCluster (Dedicate an existing cluster.) + + + dedicateHost (Dedicates a host.) + + + releaseDedicatedZone (Release dedication of zone.) + + + releaseDedicatedPod (Release dedication for the pod.) + + + releaseDedicatedCluster (Release dedication for cluster.) + + + releaseDedicatedHost (Release dedication for host.) + + + listDedicatedZones (List dedicated zones.) + + + listDedicatedPods (Lists dedicated pods.) + + + listDedicatedClusters (Lists dedicated clusters.) + + + listDedicatedHosts (Lists dedicated hosts.) + + +
+
+
+ Changed API Commands in 4.2 + + + + + + + + API Commands + + + Description + + + + + + + listNetworkACLs + + + The following new request parameters are added: aclid (optional), action + (optional), protocol (optional) + The following new response parameters are added: aclid, action, number + + + + + copyTemplate + + + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + listRouters + + + The following new response parameters are added: ip6dns1, ip6dns2, role + + + + + updateConfiguration + + + The following new request parameters are added: accountid (optional), + clusterid (optional), storageid (optional), zoneid (optional) + The following new response parameters are added: id, scope + + + + + listVolumes + + + The following request parameter is removed: details + The following new response parameter is added: displayvolume + + + + + suspendProject + + + The following new response parameters are added: cpuavailable, cpulimit, + cputotal, ipavailable, iplimit, iptotal, memoryavailable, memorylimit, + memorytotal, networkavailable, networklimit, networktotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal, + snapshotavailable, snapshotlimit, snapshottotal, templateavailable, templatelimit, + templatetotal, vmavailable, vmlimit, vmrunning, vmstopped, vmtotal, + volumeavailable, volumelimit, volumetotal, vpcavailable, vpclimit, vpctotal + + + + + + listRemoteAccessVpns + + + The following new response parameters are added: id + + + + + registerTemplate + + + The following new request parameters are added: imagestoreuuid (optional), + isdynamicallyscalable (optional), isrouting (optional) + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + addTrafficMonitor + + + The following response parameters are removed: privateinterface, privatezone, + publicinterface, publiczone, usageinterface, username + + + + + createTemplate + + + The following response parameters are removed: clusterid, clustername, + disksizeallocated, disksizetotal, disksizeused, ipaddress, path, podid, podname, + state, tags, type + The following new response parameters are added: account, accountid, bootable, + checksum, crossZones, details, displaytext, domain, domainid, format, hostid, + hostname, hypervisor, isdynamicallyscalable, isextractable, isfeatured, ispublic, + isready, ostypeid, ostypename, passwordenabled, project, projectid, removed, size, + sourcetemplateid, sshkeyenabled, status, templatetag, templatetype, tags + + + + + listLoadBalancerRuleInstances + + + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + migrateVolume + + + The following new request parameters is added: livemigrate (optional) + The following new response parameters is added: displayvolume + + + + + createAccount + + + The following new request parameters are added: accountid (optional), userid + (optional) + The following new response parameters are added: accountdetails, cpuavailable, + cpulimit, cputotal, defaultzoneid, ipavailable, iplimit, iptotal, + iscleanuprequired, isdefault, memoryavailable, memorylimit, memorytotal, name, + networkavailable, networkdomain, networklimit, networktotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + projectavailable, projectlimit, projecttotal, receivedbytes, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal, + sentbytes, snapshotavailable, snapshotlimit, snapshottotal, templateavailable, + templatelimit, templatetotal, vmavailable, vmlimit, vmrunning, vmstopped, vmtotal, + volumeavailable, volumelimit, volumetotal, vpcavailable, vpclimit, vpctotal, + user + The following parameters are removed: account, accountid, apikey, created, + email, firstname, lastname, secretkey, timezone, username + + + + + updatePhysicalNetwork + + + The following new request parameters is added: removevlan (optional) + + + + + listTrafficMonitors + + + The following response parameters are removed: privateinterface, privatezone, + publicinterface, publiczone, usageinterface, username + + + + + attachIso + + + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + listProjects + + + The following new request parameters are added: cpuavailable, cpulimit, + cputotal, ipavailable, iplimit, iptotal, memoryavailable, memorylimit, + memorytotal, networkavailable, networklimit, networktotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal, + snapshotavailable, snapshotlimit, snapshottotal, templateavailable, templatelimit, + templatetotal, vmavailable, vmlimit, vmrunning, vmstopped, vmtotal, + volumeavailable, volumelimit, volumetotal, vpcavailable, vpclimit, vpctotal + + + + + + enableAccount + + + The following new response parameters are added: cpuavailable, cpulimit, + cputotal, isdefault, memoryavailable, memorylimit, memorytotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal + + + + + listPublicIpAddresses + + + The following new response parameters are added: isportable, vmipaddress + + + + + + enableStorageMaintenance + + + The following new response parameters are added: hypervisor, scope, + suitableformigration + + + + + listLoadBalancerRules + + + The following new request parameters is added: networkid (optional) + The following new response parameters is added: networkid + + + + + stopRouter + + + The following new response parameters are added: ip6dns1, ip6dns2, role + + + + + + listClusters + + + The following new response parameters are added: cpuovercommitratio, + memoryovercommitratio + + + + + attachVolume + + + The following new response parameter is added: displayvolume + + + + + updateVPCOffering + + + The following request parameters is made mandatory: id + + + + + resetSSHKeyForVirtualMachine + + + The following new request parameter is added: keypair (required) + The following parameter is removed: name + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + updateCluster + + + The following request parameters are removed: cpuovercommitratio, + memoryovercommitratio (optional) + + + + + listPrivateGateways + + + The following new response parameters are added: aclid, sourcenatsupported + + + + + + ldapConfig + + + The following new request parameters are added: listall (optional) + The following parameters has been made optional: searchbase, hostname, + queryfilter + The following new response parameter is added: ssl + + + + + listTemplates + + + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + listNetworks + + + The following new response parameters are added: aclid, displaynetwork, + ip6cidr, ip6gateway, ispersistent, networkcidr, reservediprange + + + + + restartNetwork + + + The following new response parameters are added: isportable, vmipaddress + + + + + + prepareTemplate + + + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + rebootVirtualMachine + + + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + changeServiceForRouter + + + The following new request parameters are added: aclid (optional), action + (optional), protocol (optional) + The following new response parameters are added: id, scope + + + + + updateZone + + + The following new request parameters are added: ip6dns1 (optional), ip6dns2 + (optional) + The following new response parameters are added: ip6dns1, ip6dns2 + + + + + ldapRemove + + + The following new response parameters are added: ssl + + + + + updateServiceOffering + + + The following new response parameters are added: deploymentplanner, isvolatile + + + + + + updateStoragePool + + + The following new response parameters are added: hypervisor, scope, + suitableformigration + + + + + listFirewallRules + + + The following request parameter is removed: traffictype + The following new response parameters are added: networkid + + + + + updateUser + + + The following new response parameters are added: iscallerchilddomain, + isdefault + + + + + updateProject + + + The following new response parameters are added: cpuavailable, cpulimit, + cputotal, ipavailable, iplimit, iptotal, memoryavailable, memorylimit, + memorytotal, networkavailable, networklimit, networktotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal, + snapshotavailable, snapshotlimit, snapshottotal, templateavailable, templatelimit, + templatetotal, vmavailable, vmlimit, vmrunning, vmstopped, vmtotal, + volumeavailable, volumelimit, volumetotal, vpcavailable, vpclimit, vpctotal + + + + + + updateTemplate + + + The following new request parameters are added: isdynamicallyscalable + (optional), isrouting (optional) + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + disableUser + + + The following new response parameters are added: iscallerchilddomain, + isdefault + + + + + activateProject + + + The following new response parameters are added: cpuavailable, cpulimit, + cputotal, ipavailable, iplimit, iptotal, memoryavailable, memorylimit, + memorytotal, networkavailable, networklimit, networktotal, + primarystorageavailable, primarystoragelimit, primarystoragetotal, + secondarystorageavailable, secondarystoragelimit, secondarystoragetotal, + snapshotavailable, snapshotlimit, snapshottotal, templateavailable, templatelimit, + templatetotal, vmavailable, vmlimit, vmrunning, vmstopped, vmtotal, + volumeavailable, volumelimit, volumetotal, vpcavailable, vpclimit, vpctotal + + + + + + createNetworkACL + + + The following new request parameters are added: aclid (optional), action + (optional), number (optional) + The following request parameter is now optional: networkid + The following new response parameters are added: aclid, action, number + + + + + enableStaticNat + + + The following new request parameters are added: vmguestip (optional) + + + + + registerIso + + + The following new request parameters are added: imagestoreuuid (optional), + isdynamicallyscalable (optional) + The following new response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + createIpForwardingRule + + + The following new response parameter is added: vmguestip + + + + + resetPasswordForVirtualMachine + + + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + createVolume + + + The following new request parameter is added: displayvolume (optional) + The following new response parameter is added: displayvolume + + + + + startRouter + + + The following new response parameters are added: ip6dns1, ip6dns2, role + + + + + + listCapabilities + + + The following new response parameters are added: apilimitinterval and + apilimitmax. + See . + + + + + createServiceOffering + + + The following new request parameters are added: deploymentplanner (optional), + isvolatile (optional), serviceofferingdetails (optional). + isvolatie indicates whether the service offering includes Volatile VM + capability, which will discard the VM's root disk and create a new one on reboot. + See . + The following new response parameters are added: deploymentplanner, isvolatile + + + + + + restoreVirtualMachine + + + The following request parameter is added: templateID (optional). This is used + to point to the new template ID when the base image is updated. The parameter + templateID can be an ISO ID in case of restore vm deployed using ISO. See . + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + createNetwork + + + The following new request parameters are added: aclid (optional), + displaynetwork (optional), endipv6 (optional), ip6cidr (optional), ip6gateway + (optional), isolatedpvlan (optional), startipv6 (optional) + The following new response parameters are added: aclid, displaynetwork, + ip6cidr, ip6gateway, ispersistent, networkcidr, reservediprange + + + + + createVlanIpRange + + + The following new request parameters are added: startipv6, endipv6, + ip6gateway, ip6cidr + Changed parameters: startip (is now optional) + The following new response parameters are added: startipv6, endipv6, + ip6gateway, ip6cidr + + + + + CreateZone + + + The following new request parameters are added: ip6dns1, ip6dns2 + The following new response parameters are added: ip6dns1, ip6dns2 + + + + + deployVirtualMachine + + + The following request parameters are added: affinitygroupids (optional), + affinitygroupnames (optional), displayvm (optional), ip6address (optional) + The following request parameter is modified: iptonetworklist has a new + possible value, ipv6 + The following new response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + createNetworkOffering + + + The following request parameters are added: details (optional), + egressdefaultpolicy (optional), ispersistent (optional) + ispersistent determines if the network or network offering created or listed + by using this offering are persistent or not. + The following response parameters are added: details, egressdefaultpolicy, + ispersistent + + + + + listNetworks + + + The following request parameters is added: isPersistent. + This parameter determines if the network or network offering created or listed + by using this offering are persistent or not. + + + + + listNetworkOfferings + + + The following request parameters is added: isPersistent. + This parameter determines if the network or network offering created or listed + by using this offering are persistent or not. + For listNetworkOfferings, the following response parameter has been added: + details, egressdefaultpolicy, ispersistent + + + + + addF5LoadBalancer + configureNetscalerLoadBalancer + addNetscalerLoadBalancer + listF5LoadBalancers + configureF5LoadBalancer + listNetscalerLoadBalancers + + + The following response parameter is removed: inline. + + + + + listRouters + + + For nic responses, the following fields have been added. + + + ip6address + + + ip6gateway + + + ip6cidr + + + + + + + listVirtualMachines + + + The following request parameters are added: affinitygroupid (optional), vpcid + (optional) + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + listRouters + listZones + + + For DomainRouter and DataCenter response, the following fields have been + added. + + + ip6dns1 + + + ip6dns2 + + + For listZones, the following optional request parameters are added: name, + networktype + + + + + listFirewallRules + createFirewallRule + + + The following request parameter is added: traffictype (optional). + The following response parameter is added: networkid + + + + + listUsageRecords + + + The following response parameter is added: virtualsize. + + + + + deleteIso + + + The following request parameter is removed: forced + + + + + addCluster + + + The following request parameters are added: guestvswitchtype (optional), + guestvswitchtype (optional), publicvswitchtype (optional), publicvswitchtype + (optional) + See . + The following request parameters are removed: cpuovercommitratio, + memoryovercommitratio + + + + + updateCluster + + + The following request parameters are added: cpuovercommitratio, + ramovercommitratio + See . + + + + + createStoragePool + + + The following request parameters are added: hypervisor (optional), provider + (optional), scope (optional) + The following request parameters have been made mandatory: podid, + clusterid + See . + The following response parameter has been added: hypervisor, scope, + suitableformigration + + + + + listStoragePools + + + The following request parameter is added: scope (optional) + See . + The following response parameters are added: hypervisor, scope, + suitableformigration + + + + + updateDiskOffering + + + The following response parameter is added: displayoffering + + + + + changeServiceForVirtualMachine + + + The following response parameter are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + recoverVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + listCapabilities + + + The following response parameters are added: apilimitinterval, apilimitmax + + + + + + createRemoteAccessVpn + + + The following response parameters are added: id + + + + + startVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + detachIso + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + updateVPC + + + The following request parameters has been made mandatory: id, name + + + + + associateIpAddress + + + The following request parameters are added: isportable (optional), regionid + (optional) + The following response parameters are added: isportable, vmipaddress + + + + + listProjectAccounts + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + ipavailable, iplimit, iptotal, memoryavailable, memorylimit, memorytotal, + networkavailable, networklimit, networktotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal, snapshotavailable, snapshotlimit, + snapshottotal, templateavailable, templatelimit, templatetotal, vmavailable, + vmlimit, vmrunning, vmstopped, vmtotal, volumeavailable, volumelimit, volumetotal, + vpcavailable, vpclimit, vpctotal + + + + + disableAccount + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + isdefault, memoryavailable, memorylimit, memorytotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal + + + + + listPortForwardingRules + + + The following response parameters are added: vmguestip + + + + + migrateVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + cancelStorageMaintenance + + + The following response parameters are added: hypervisor, scope, + suitableformigration + + + + + createPortForwardingRule + + The following request parameter is added: vmguestip (optional) The + following response parameter is added: vmguestip + + + + addVpnUser + + + The following response parameter is added: state + + + + + createVPCOffering + + + The following request parameter is added: serviceproviderlist (optional) + + + + + + assignVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + listConditions + + + The following response parameters are added: account, counter, domain, + domainid, project, projectid, relationaloperator, threshold + Removed response parameters: name, source, value + + + + + createPrivateGateway + + + The following request parameters are added: aclid (optional), + sourcenatsupported (optional) + The following response parameters are added: aclid, sourcenatsupported + + + + + updateVirtualMachine + + + The following request parameters are added: displayvm (optional), + isdynamicallyscalable (optional) + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + destroyRouter + + + The following response parameters are added: ip6dns1, ip6dns2, role + + + + + listServiceOfferings + + + The following response parameters are added: deploymentplanner, isvolatile + + + + + + listUsageRecords + + + The following response parameters are removed: virtualsize + + + + + createProject + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + ipavailable, iplimit, iptotal, memoryavailable, memorylimit, memorytotal, + networkavailable, networklimit, networktotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal, snapshotavailable, snapshotlimit, + snapshottotal, templateavailable, templatelimit, templatetotal, vmavailable, + vmlimit, vmrunning, vmstopped, vmtotal, volumeavailable, volumelimit, volumetotal, + vpcavailable, vpclimit, vpctotal + + + + + enableUser + + + The following response parameters are added: iscallerchilddomain, isdefault + + + + + + createLoadBalancerRule + + + The following response parameter is added: networkid + + + + + updateAccount + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + isdefault, memoryavailable, memorylimit, memorytotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal + + + + + copyIso + + + The following response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + uploadVolume + + + The following request parameters are added: imagestoreuuid (optional), + projectid (optional + The following response parameters are added: displayvolume + + + + + createDomain + + + The following request parameter is added: domainid (optional) + + + + + stopVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + listAccounts + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + isdefault, memoryavailable, memorylimit, memorytotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal + + + + + createSnapshot + + + The following response parameter is added: zoneid + + + + + updateIso + + + The following request parameters are added: isdynamicallyscalable (optional), + isrouting (optional) + The following response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + listIpForwardingRules + + + The following response parameter is added: vmguestip + + + + + updateNetwork + + + The following request parameters are added: displaynetwork (optional), + guestvmcidr (optional) + The following response parameters are added: aclid, displaynetwork, ip6cidr, + ip6gateway, ispersistent, networkcidr, reservediprange + + + + + destroyVirtualMachine + + + The following response parameters are added: diskioread, diskiowrite, + diskkbsread, diskkbswrite, displayvm, isdynamicallyscalable, affinitygroup + + + + + createDiskOffering + + + The following request parameter is added: displayoffering (optional) + The following response parameter is added: displayoffering + + + + + rebootRouter + + + The following response parameters are added: ip6dns1, ip6dns2, role + + + + + listConfigurations + + + The following request parameters are added: accountid (optional), clusterid + (optional), storageid (optional), zoneid (optional) + The following response parameters are added: id, scope + + + + + createUser + + + The following request parameter is added: userid (optional) + The following response parameters are added: iscallerchilddomain, + isdefault + + + + + listDiskOfferings + + + The following response parameter is added: displayoffering + + + + + detachVolume + + + The following response parameter is added: displayvolume + + + + + deleteUser + + + The following response parameters are added: displaytext, success + Removed parameters: id, account, accountid, accounttype, apikey, created, + domain, domainid, email, firstname, lastname, secretkey, state, timezone, username + + + + + + listSnapshots + + + The following request parameter is added: zoneid (optional) + The following response parameter is added: zoneid + + + + + markDefaultZoneForAccount + + + The following response parameters are added: cpuavailable, cpulimit, cputotal, + isdefault, memoryavailable, memorylimit, memorytotal, primarystorageavailable, + primarystoragelimit, primarystoragetotal, secondarystorageavailable, + secondarystoragelimit, secondarystoragetotal + + + + + restartVPC + + + The following request parameters are made mandatory: id + + + + + updateHypervisorCapabilities + + + The following response parameters are added: hypervisor, hypervisorversion, + maxdatavolumeslimit, maxguestslimit, maxhostspercluster, securitygroupenabled, + storagemotionenabled + Removed parameters: cpunumber, cpuspeed, created, defaultuse, displaytext, + domain, domainid, hosttags, issystem, limitcpuuse, memory, name, networkrate, + offerha, storagetype, systemvmtype, tags + + + + + updateLoadBalancerRule + + + The following response parameter is added: networkid + + + + + listVlanIpRanges + + + The following response parameters are added: endipv6, ip6cidr, ip6gateway, + startipv6 + + + + + listHypervisorCapabilities + + + The following response parameters are added: maxdatavolumeslimit, + maxhostspercluster, storagemotionenabled + + + + + updateNetworkOffering + + + The following response parameters are added: details, egressdefaultpolicy, + ispersistent + + + + + createVirtualRouterElement + + + The following request parameters are added: providertype (optional) + + + + + listVpnUsers + + + The following response parameter is added: state + + + + + listUsers + + + The following response parameters are added: iscallerchilddomain, isdefault + + + + + + listSupportedNetworkServices + + + The following response parameter is added: provider + + + + + listIsos + + + The following response parameters are added: isdynamicallyscalable, + sshkeyenabled + + + + + +
+
+ Deprecated APIs + + + addExternalLoadBalancer (Adds F5 external load balancer appliance.) + + + deleteExternalLoadBalancer (Deletes a F5 external load balancer appliance added in a + zone.) + + + listExternalLoadBalancers (Lists F5 external load balancer appliances added in a + zone.) + + +
+
Version 4.1.0
diff --git a/docs/en-US/removed-api-4.2.xml b/docs/en-US/removed-api-4.2.xml index cf4ab741cf3..596d3163fe0 100644 --- a/docs/en-US/removed-api-4.2.xml +++ b/docs/en-US/removed-api-4.2.xml @@ -19,7 +19,7 @@ under the License. -->
- Removed APIs + Deprecated APIs deleteCiscoNexusVSM (Deletes a Cisco Nexus VSM device) From a0c8fdf26f387a90d7aa778c4759bea6b1442e83 Mon Sep 17 00:00:00 2001 From: radhikap Date: Wed, 11 Sep 2013 11:50:31 +0530 Subject: [PATCH 149/251] new workload image for RN --- docs/en-US/images/workloads.png | Bin 0 -> 69265 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/en-US/images/workloads.png diff --git a/docs/en-US/images/workloads.png b/docs/en-US/images/workloads.png new file mode 100644 index 0000000000000000000000000000000000000000..a8334d97546b16c1a7559f32700d7d1c8cc4696b GIT binary patch literal 69265 zcmd421CuOKv#33`XOC^$-eb?~v2EM7ZQHhO+qP}rdC$2xf8dMwI-;Vxy0a>CA#-&; zl`HI@j3_J=CKLbw0Iax}kURhY&?W!?U;+ft?>C7w@|eGGfDZDa0sz(1*k`{dASV3M z`~U!TG0>lSpugvkwqj}y003|U|2Y6hY)TCQ0Dg+Zh4>X+wJ){6Jr#$RbG9tqZ+M7f zXXCV`gyiZF3Ri1aWCM8mp8wSGPADiMan+#-)#?FeANi3B{ZlLyBqRZy_t=@*zTtCm zX3Oxtx!#(xW?cD*n7E!ybNF^JKAuctVxr_nfbd}l!Ul{65cMVMMcjpWWCJ??Hug=K z`TcAQ@_&9J?2SUI2LB&o>>xm!ni_$RojACh5)chSccG)O#VOZzAzxIa)iLt*@^#U z-+$Y*LGWwPhTa5)!~Zpr2p<^GI&l=h0)f=Z!U8K$-XV_TzZ;gT^Q%g{r*|6q=>Ih- zm+7|wdz^j$Ly;ii5zWv16Bq8P>}#76_j zI665wU9om)*QCgVlsKY-C{iYcDLu?g_w$`3{Pu{gIY=Udgs#t@l_qPt(lw`g+#DA) zHbu6J@C$XgBsLBf&P+GL)3D|==H+-_2w=>mp}~omR(Avxmj;d&e;7zxJfvMf44Igh zk)6FhTx3Eo(O~yJRK4TSH*itZriITC*%0jJ`bF~!PFP+<`^@yFt;-?G0+6U5UA2Su zFC&YWM3j}5$Tq3xrqrX zYHDh7^3j2TfuW%xAb;+N1AN=c@;6nKEY?rQMob_0B$jJVwGa^=kJ2BKf&i1jHv@>i z0V(8!w0-nIe`qbuA0;GjC4?H5E-YmwEowL(9iN-(0lDs`q0stG^I9 zjJJv@D;};L?d@es=gvAf;Ij;fw+~Qo(4oLKdEa&2+Wh?8aV5l=*GGF3#*E7MmU(ji zxC9OweUd$>?xu^-q9NMO66nXO%JuyXT=+n#>5a|J%gf8kDk=k3kG2JVKjDOfQ768!STdJJF}Pdza+E}Zec-gNt- z1%rB`!ZZB7=az0Xkyy9nT$#oX;JJ@K7>?oDzDeF6WLEHpJXGPAJk z1&9>fJw7_RxQu{b(Cqs`t@p-4L)Y7UDmNOtj4c@e>89g1LKH(b$Vf#adq|9H649(5 z|9m~oO%Lpl2+Lw)aCvRcF6c$CCctVZ6GGgsMHA9<{)q#A+jznLI{Nz!mr8mf-QBmb zFvGYYlr49Ead+Oq_Km!E(5!Eb#3qU@L0~xo(+)3Xak~o)M!Ya;*nz}hG{O&1Jl4R6 z_oHt!=3^{8iGc_Nk8vq?>id=+jnf(=>~Hen_r&-4_T3ogV4P*2R?^XTWfL;*h8RxN zYr+*Sc#aR!WL35C?A>PcQj(A`oyBv8>7oMPwxEI`GIp^6&9|ylPsoB8F$G7+XEMMG zQA^;Lv_#in_z^lLc?Jn6l(YH1l!q?+rC=gr012;+?65Gn+}ej}ZCdW-aNvc=e}^{N z3VN~DOdGe*0u1EYYycknQ4w04{GQ`n^G0>!voiJVVrc7MDJK{&H($9xr#Su z-xGLvWd9qDz9InnN54CM4kJuNg+N|)A!BGxYnQOG7=|nNKH~!{eB7+(FkXg2V(R4h zGc9}Ye;f4$)UgF6Jo@&r!^i2d-&7EiN!e9!x|JnoI&4-{~IfN z0TFwZJA8gpUoIzi74m@*v<8(O3SHRhiTgu?#vZGEyx*UC+o@*M#|Ko5@dHT6a3LgauV4l7%nt5P%AElhKX#75oz6XM>{04-4k6evfh=06UmjriLm{C>FS5F}qwD+1 zVLgd~M+qt3sSo1cFmzBUbPRf`RBgrIPgwCQxhuL9m_x)UHHC9_xP1B~x!TD#k@?F8 zuV4Y_YUn_0UuVxJhPhjt+G{)8ySs6*hMLrA_H$9V>m${ORh!=2X3~E$9$;F;-pi4{ zxkP*O5PR1xI^L>SV%%Mi3EqDkGjlQ&WH3$X89_doLEN9MZG?y`N{(x8;2s=&g;tTn*i7}66V zH%jqURMI+Y*J-Hcdb#WCI#}^+Ug*HF0jJu56HaY`=jF#l?+W4%r(N zC^(%XSF2;e125n(z=muBu+~B8d2FazsbjM^raSwxPC+2}URRN(_IiF&nbFCY3Z1s$ z;EM}ClRrRPf zC1@*GBC>6K2>Bp;M=dJgDX~d%)`X>nwQBwG23l6qC72z_oXybyTj8UX>}Ah)=?^de zc`hXCqZzO=3&dwuszK%Uy`q+&(@jIlbyWP+kD|9%El@}tJD!lR4)04}9TVQC<(MP# zjpOG;yQ^=sB3H8wX^D=Tjs(yfhbUyl;G6m8;9c|#AG^r3rn`#A6jvR+Os~6$L}iA~ z?0HvGt)y);uFSVlx0=N#$tX0T#)~H+8t=UL^SYGAeUe@M-R@Z_t0OO%#v7H9Qk@Hz z_T5<^DCQ`c*cFrBuchUGQw=83SzbE$wu20sQnd3u3J$MffAA9rfvPTFsJWJ_IVEId zli6vVG>_EuSW6WWdIp3_Bo{-?u6j8gRuN+L4hIkC7$!Leg>$mExP^17zSAbt8F<{+ zCqe2tO1wZzpnn2GVKNLa{k&~(*Sl5Ca~RVZekbI@%YKN7H)**6-lnx`$a(e~ukE+h z&;nn=YXxeWir37Oq+ZFK@YX%GdEMLkEWRdY!g72Lhs!EgTl@X_dV4$PDhUK_-SIdx zs>J;2X6@JPiivEe1ST|h(g1!hnV6^lVhUq%WY?|k-t+i+ytcn&JA;Jsk5;5A>q+NC zaQeAp>@_szK&itArM*?Mp8CV?Sw@Z-Uc(2zFXBa74y;1{ioAE*Rom2Z=3?&qTiHhQ zlxLLs+GIh1+}lB`^Ki6xsb-j!`@q1Tg~rY%L-m(Qvck;uh8ByF@DtXA zSBo1a4Oao2C+(gQ<*jyIk9zhgudno1IQ)#2n+g?@&&NzdyF%0LUUB5T3+J=f(k1W< zy+qZX&;gmZ&+nKQ`C)2wbh1J<#-mfa=7kI#SuwPW9jJ)TnlsTBs7W$0Qq@~i48sR=3J(Vu zH&`m6IW*%gXC?o=&V}p|tOT-Lt`pJzCdo-bsbV??4V!e{1F5_Kv_VCYxo0cVmVb{h zmAlvgTKh{b&a9ucfuInn@Ug7&&m?B zPhSY-Q>aDoU?zw##;RB+4!c6C20hLdGW!bJ6ZS4v;z5 zA~aSb*5GiHY&=-(1M_69NfWZ>9S9?xG>W)%0S@Q)ds6r^<_N{ayzweQzkKPx%KrFj zKDFCPHlUAg4T<<>ZnhfbnOpG@_qy=48p zyng*4Y%byO*s1xEXZ59P#@P_cEJ&Ev=e=(dpO^o)EoSPNxa=UJ9ogi3KqlfV*f^2# zHE@Xsjm&0-I7~{9Br2IQ(N*djC+~b%52+p7uYZmWSOyWK#nJF5bKt&$(kgO_y`vdWTxDdHM%dd1|o%U|vkjpXa`TAA*?a@2_>ld@Wu<$#ls1 zdjpC<0!a{8bP19g-j(CL%;7s$Ok6|SO>W$0nyEnVAMTm4KR^vU!!|$PF>rC<+FiC( z2~;rg;ygHiFPW>VI)={auGi&v9AO#9W3^rNrlv`Y6tTBkU9Cr3u*sjgAmz_cLmYHX zxr$sxr%c8rf{4O!YZ~+78mr*HCSrf=4rH%b4>Fl?4!1o26D=Kxe%hc?SwJPIU-BG5 zPCm4>=kg=A%p{z98whw=Zd+t!9U$)mtg(+mY8Btrq$uOXM7c~PLmVwF*e(WvnsWY~ zh%~8nq0y@UHGCLV09zhn@@7wU*0TQAY!cB*EC=;imSD-k>ww4xIS|dM=_4Sz9#$&@ z3;A^3+uUnacnF!T>)wazk3o(zpc>e1RlDOg^^+_AV$m|hV1*sunmnX9ue%xe7z-ue z%_M8HPD)Nz+hI~e(w5E`c6Kbjk(&f2Th3>%&sL9VjxE^4w%NSasQXL=TSOx4(D#ka z8U`+iPeVD@uR{Doj2sAv>Gu#Q##zs#p#$YXfA}1)wX7Rh zy(kvYoYZo|Xe6L``$tz6cw7a0gE5IbuKr=3=rbo%Z|KXa7)F*F>D=I@>PlDf!5=w! zJDiAXjK7rYbQCo^qS;LE%~Pae$3pJ!QY7aPm3p#xD9F{1xWyV*YMkfR9kr+`;6baE zlnh^32*`!w?Fm%89Z%7VN#Eb!*4Eao#+!kf9dh@lF`~95w+1FIJztAz9gx#4$V9#e z$~haG+hrVe!~uR!FW>BMdovviAx-oJ#1!J^Bq;O}2v{0gT3z=aTsCPh-2S^@z?zyO zCj!2*v58f5RvH(;HrXC8ol1Uwez>@}$0A{U+aeI`dU~n|3P1~m;u>7^aQ00+tDPWRMEEC_wp3f7nvAq$*~0-v&loERZgF1lS3`KSNAV~LVCF}%7*B|)g5qG0RBs7Qy&0D z${hcHWf2TQ{mLWzv2j3VJVuF0f2iGADOxgHUqeq#7X=wCsNv6gwBt zR}aE`_28H`F({$RCN^&V#zM(|`y8K>`^HUvD%m8jYl_b6v07^SO#3kKUh_L3@rW3Iu+$ASIc8zQ1j%q!^=F#8}uEB=_?v%I`KBzwpVibX-7^H5m+g_|L3sB${1DI@!4I z!5V>qeU`5PW9`5q-MUV>OFJCHv~ykM5hQle0CN4S`oO7mt0n?+@1-HdH?GBZ-osQvkE&Hg?a zhDSvp;>O4fiw3gUm2@6pRK#0az!3;5nsxfYT?Bi)m{B=@g zyT=US>%zRwn0*-IWdG9eqZDhRq>-wm#WjsUb%)YUNZ8DpU9n3Ms}z_lnqr?{IJQ<= zfdruivRjit@HY@lYt4mgXDD|J&91QV4|EjLqw;A(^h%?A!QhyqkaffJhOGzs+cEUg z(1enTr~PYZo!N*tQI#~01kQP~808DzQk)(IvA6kpQbM34h`dAm>0U*wM&r_19uSw0 zqSn3t`S{AM%U$$tk}Rp=d(umv6gc9rJj=XNLNZvz8#n>aTr&a)RcbCm67yZAZ)5(J z5O@HjGa$jby?m%ofA$5gU*p8^I*8J^E|pmT-=z6rRyvT}Qp>r9y1FYHWf6Rn8Rlan zy+kzX%`)T^D@`bJ3jvV1m-m{t@sUGEp=>4%gM{e{4JHX^7keQ4M`x|e_rPYn*ueV_ z4t`vLSbuG#3T49qDYA-7z+!6602gR$@v*uaT0VpH!+eP-roc_HL1(G!N(G11GP%W_MCgxBtgsvgPcB$Z{ls3gzZz+N(H zn~9R03pMD1q2cX8nwy~|cwf}5Cfjb|w>Oae3JF0a{_`@IMAA-XDLI6is6vDzHXnyF zdq^#lT%^@P-Qp$?H(U6c@)@4y`L-b%_G1KgwAVd*VRvl{F>&uu=Pl@1WS?AwSA*X9 zPGg-0{VxKvWzYerrDtypyPE@S zA1D5ur-%{U520|)!lH;mggC;bL`uTH{*6Yv5boKYKMPy;ghh^p98?l)T<($}LT%g^ zdmjAfh>V*1CG0MEaNCKVgp)~}qjK`~SaqMoq$mmJOc&#yU3!aD9X<<*p;TpEa>?xZ#hMRO?m7^`q;x--Gm?kA1~C%`NmJC3P|$Jj z%Q`z5(CO=X(U6iVRVrVf$lO$M>oPfE!ic0($bdbvNakiZ7h=%c^R z>G-I01=+#1>2gKWzM*HJ~y4iGff*WF8 zglCKbp}|+Ya~82)pkfX{Un2wHVGQt^ddwt=L5RlK@qxR{y+EhHwc zy7VJ17$PUq*!M0Q6QV>Im_|iyw!CEeg)pb#^nP8)jTgVultzRLXz>msCC3k4{(_@q zc%vC39klGH0kZJfalimwC{bZIbOK=r*RvrCSyd-hW;*XAW3G{#ljw6zB${_Zl=*OK zb&{~UI%od5k!_q%^kf%Ryk{4wnKUE@C+Q30OJzrn(pD!JOTPQ*4Yv7Y8Vw(;Vu7aR z!6sqcYScO{hDLWCj+e}uHN>seGg4mjEV-U@*lJio0Pg8cHc@#bos^kRDiS0{dKak;k+^2+yp)0H=2jRcFC-pP{NK)iP$~ji?Lj7hF|jUZ zudtoX#J?a>q#mc8(}4}b^9-i{bR)B$*&+cRhIY+ePhx} zi!g+S2FodH*Sy5;)EI1f)~W`};61Uq?E5IG4?X!&a|NhxClLo;Bkj7VkXlHLUHSTr zEODW}ZFyUWeew|@q8$+0-}+$9K_Sbp!<8+J62RhzrgT9!oL8DJ@}SP>wz_F>b-5qge%RvnK|fS7My$4Q(kZJ1K!H3hFT- zB4xyXjFxVyXiKXrfMST7Nz&e>XJG&B1o}rrOdVIfDJN){d(m0uCwVF6%7}%pd!1m+ zO6ZgM2XOt&ex;z1RliM6KbP;HN%*n7UfnPizR3o`l0wLX*uGaoxEK-XS}5F{LW5vc zULBFEW~aDh7es`s6U5iNYY4=5j8D1x^0kFTWE6puRm3oJ<;Px&lTWl}>ZX{#=Sgsu zW{bF}r+~Pj)U}T>#D0fWGuso!Y!fb6b2w=)C5%_+JW~o(%`|ehBw{umN60HYUOpO; zAeYR(8gDzHo@26*D`{N%04Tcy!j7}(AcDFcsXsxZU|}yVypDe^2Bv;KVt{dN>cToPhNq5YE)qsO@J% z%T>c1XgOKp6tSnPi1P+QsXB$0%3g3LyA11Mow9Fp=F<8gU@$?fkrad_FN9nvHwbj^ z#o>daodG7qI`n>H?6OQHn*`W<;v7BMaM(1a{7QYd&|nkfL7Ev`6uSgQun((!#!R7b zK`(pD^qN*2o0XJ$mV0Zrc}+Eohs{{#0x3xhtoY}HEvtZ%ez4vi{!0;>Qi@TT^(*~R z4@7@2X89bZu3!EmU3lq4`vc+^gFO z+X6D8;9@xgQG>^PW&%bFu;&1CDJQvMUH3p6a2!o4@#oH}Ov^$-O>E1W1lfCA1 zX3Voo+UhTT3TuFX#|gN-z|Xn}Q*iE#y5Y6kjo_1-e=Z-!#-B*K&s1^* z22{F$!dz-!2|MQe&n`oJa{xXZ9nDmvwK$q-FV>8GiodsSYcgwCX}3`_zaP)clJ>*VHHjc>;X(J>3KGN5e?~K zwt!WK0SiK!(FWZM41HxW99X}bRJk-&vkAGm1Y7%AEY8X!J5l#FQrus@qZa58wFiL4 z%cuh2i#O|N!99Gv2G+>Qs^$K{^FP0Uy2T|lSG6g$jt4Cs{H+ac^1wQoMDMbjpc@>> z`J+;)0-J{1U6Vh* z@Ud*d_4>v7buOitH%FGXOUu>EuU4q(bFRy+ur8w%Ef_uFqQH~$_MjHwUD`_)dt6XS zE6shz@0v0qXd+dD&ALsote)?a=i)FlNp_GlCcNYcE)a0HC-}J9mFA$Z6)V$2!g7c0 z4r%f?Ge3CJMTnwma0F4b%!6jw{_jMQgoF&pIXyjnd}0E5o84a;TfUy%0n?9p1<^7k zNZhX$n7aN|bdDb<0*GCB-4^`PNavdBZT&NW?ds#%@Gq!ONu?GSv zU95+KPXKd|TZ}L}AO3g{H=WU(8g}t-rr8=_VRNcCFdYOKU*Zump*tKLLXYh}7Ntl9 zM5E>`pv@})rym2zH$3W=zWd)Q<``xrtb7fGD#&zKIRvc84w89skWqcgc3>>Z08>Pw zvWQX-Z$-x$`hWARNbrd5<(`;-CA1{;SD_NneqcgbM}w?*eI*ZM;Dh_()nxU@1(L+q zo9a39%xNhE+$7)?askjZN!bNKl_JCc(qPRx7#Ce9xHvt`X#@uc2D-YtxA(3mKi_Kj z7wr+VB$btD)$LJML8Cgn9aKWJHowt|Ymv0@b5u0Wzh0ju>sCa?vr1uue+D2A{Q3>8 zHh#MTO?LP&m=lavF*s4FUM|YXrkJs-YHj)7D|F-H*#)g95zhUZ#L8FX4-~rYytqQ@ zWCd!tgO)QUsx4ay>SeUQYlBK7=ekC9A;#cp4g*Etgri2Z{%&(Ps|aaq5ZlOnB6k>$ zxt2iV#5Um7oPhIaYGI|%s%kiLSHu*+QF^1jHZ{x9?C`ZdZH=}Cg*z3E0do1nM=&xE z5bSM=m-&k3C>O(EZwKWs2O{ERIHzOedlPK($vLJQ`GOSMtfXoAI(M=Q7Ep{v&q5s` zc*hVjTF7i?wRCH@mbpaV=I>!&<4}xg_`#a`MrjE~2E?S-IIZX8h*S|>l^&HNIG?Afo=^}~7as`sNb9@;%tH(0JVJ0GL3d+W+xC4U*)>UDWXucPd?SML6d z+i}!yliL|f+iByRWIBLYhWj2ks|6%7Wh^(#4-%DlKMWtS;d+sMUtSmtxm+jbJ`@X$zGQAef9NbSUl zeXwbHFE!1zeiX-xXhGAm_04F7U;F(rZAM$)xm}ZC0K+x1PP8rfEyqAS_3!PyIM37~ zJa|ehKJdHz(OY-(Xt;58vscJCOC%(?OG`H?h?!K8re=dnO+E+;EW}7UvF7cp$;=m^NF}y87YZe96&`B zq3hJY*>IR9Cf>Urf!=O2@p8i+j(0@%;JS$4lga>Hui6|os&0X-tNy-IZDU80{Udm_0asg% z{xfYv6xOPGWzH259i#8(<(0?8rkzRe8Mcni$AMUX*u)I6%BtNfrhp6%ZD)WZJ;=MqmtV3hT-UoAWSZT$ z@Vc=bhA^LxJt4*GaZpvZ`CTwgypH4S6mrI5J0q8Q-=f;#jR)S^GAi#P)iN2A#lu|c z1Jf`XOH0@@>|}HdUgz-$tCy<3$$-upN1%`^e2Bu}kT42jFj%gLk}}hJ*@iaHTvewTAEED!^#!U2`H!2Xp38h5Qd%}ou>2NX~B`v!^JTa^)8uN zaAJ|z0*#iN=Q{_K7@7R^^L#&)-1EV51a=d9gtg>{Fj?Pv=8q8zA?LFvNzvPJ=G3-q#Gd2iWb{^s8uCOhCUAOe%PNJ@eu`P|60UENiw@1E6 zj}JMeTA}gy631QCH&5IZ^lgIdcZ>BjMRj5KqyS5;@&iq|=ok-#tSKJIyHC9=8F2qt z=MPHk+JdOrhU5F`o{2D;&yQr;`jX&Kv~zR>W{~P)bYJPEtqdn^$)@`2(7n%g*H7=p z%cYMGFQIU0SMCCXETo;CN=NIwLE#wz*nU+Oxd4oX5dc=dT2ZXLN9#ZNL<&(~X-CGp z|L!lDeTuN~R4lnx>n}+6RxZ>k>QpX?DWtl+KB4ZC(ON-7eP#C51^p25*hp z=5zDxgTj5n8Vb`o<>^1fUTB_)S}Nm_NcmO7O(glXftRW!0_ko)GH;3*-=`R9+Mg{IfkGT96SFk6jGPRG1F%Dvhg4U1AX z18xJK`Tr_ktmNiTA>ptV7nv<47QTM&EN8*k3WGorgvcfzWY_G?IU1E<;ebnM z=!B(vh<^5WgwS?|bVDa+b``G;2piCwPA+Yl28TKh`lQk#8ys{v$~OlHpc;LUkM)up z)uNM(ArBzSCSDFrnX_MR7Jfgbb5^}R)2q)4!_quq*S2irRVP@Byc5TzKkeKQ54uG4;M&d&ClRrko-RDS4@@m0sxV`dPIb7iNxV=#+5P+=P!M}`otKY$^zo?Qib4ZyPBEg+nq&B@ zXE>9o6s*csah#8_kn!)zp(%9f4@e}oVE*u255Hg}*?joPHfAwCAw-;ky*GP(Tp%p` zbcxA^i&pem5GMMy;iQvTrFu&Oad1W27uiNRyIhWBJgUqH6r3jeCOHN=Psvu1t?w4w zso`Szmk%~4Dw`31=||=mfoRLsY5)n6yLmZ_Hcu;7bBhwigw3eSFi5d_t3_2pnQvKDLzJum!>x6J}l@# zHj)LZ78^WoJuBKf*&b^kB`9ZC?rW($>GBe!J06KNQRsGU+0!m}++C}ITd%@8|L8;o zQh)jNu&zQD!+{DWCeh#{`DWP($DIu42hBZF7fpL`C@Q(#N4Nfz7wDf7i4MK)TU%Fq zCjyNr@oY8)S9Z>{cfQFL7D7}Rd@MfAi~y`)V1qL)rpHEwI1Te7At%a+#`b2S=iyN1 zO`-rvbC4UEN~oGBWHsUaw6yGU*gtKe>z&AM= zb{Cp%(Z>J5lcAf9>**$yAZ`%4Sm3hDVWn4dNp*tABY*J$l{?XPzP>z7*!hw#{;kPe z;-yN27^xCwSY^K{{NcuK?(BF$X0E<@u9QWrVd$V%CCn~u{R zbc7N#n-ndnIt@y5n-umF17;XY%msi`tH}r(&z8$u+e^i9SF&6n*VQh+89q%AMvQlx zD;YtLcRuF};F5!AgAboTempAZAu>)TYIg+b>#@}rONq4R8ND95Fb`+QL;4wsPp$C3 z*gmpBZf1NdZDq^jo*8MrZyGIJre;SyCAxYjM9&4COz+oaa7E~B znKy~7Zhi0du`Xgu)$6EjwU*7Ac4kSZCynwBdwX}6WMB8mNvj;1Jdr&MG|N;NdSTHZ zy-e2jxJUzA_4PsyVxKMeQWCL6VJ43}k*eY78n^wBc%%q#uX{3=x$Z_Wc)MBw&BVFl zxHUs+s=51NeW4{*9=H;73?Sv@68|+NBP?$!jC4jf)9>h-@GrZOpuDqQt@{%dqG^wh zk+T)N58Ji87d-aR7yr#g3*J}cs%F!Rw}zAB`G2++Vrt4~0Zb{s|DU^#ZG-Jb z>-RfDM0?{_skJ-vN6N=HL)R-?*cK!$c)skrd21P*Kl5Xehn>LdLd9&_p&kz8&V@@W zHGLQjUt^W;+*KiAKD!m1w!kSYCXh~a2s=3_Vf7H=_O?>QILgM{FUXL%L7Y`o&! zZDH(;R9-P%=&*KzC*bEsr;uHBJafw-L!Zh!!`le1e1c!)q~2|d8% zE;3i^rz|6v2mY}ZPjf0cuOumNxRLoX3?V3AnN^LdpJ^3({lvZPvG0!?Dzh=_aGRgV z?2t>vEyW-@^=tKDYaz|9j-^CYf?V%-;?IihU<5;*rLzvTJr8C~~TO)@+ z6@@u2m-*xq`Z}ENS&6z!zJ%Myb%tWnxmLRp*&{u2oLbsZC2 zLU9HAnU=eg#ZS33x0SiQmf8Jb>0o|@|B@sf8{bp+Pjl}wkO#Wl(cG~PCT zr<<}dNcgqWq9ItC4|7vxtfChA50e3j|m*NP-qO7%77!vp+a4=(*jg2yL))*JOB-I4b{v!Ij+jS ziIjjrkc@Av%ZI3^M`&k3OQ@?tNV^wDe#XzDH;{A&@_ruuQZxz2mwe+`A$c6v z3mpLPn*H(QP4@z!V!CyGH1s8zh>3C@%}FpuA<_D#u=2``n}1r*<>%#=@7ifu?JkXW zb|Fau31r%LzP0bDdvy$LGrS3Mh=TN$5=Z=+{H3L;KJwHpb$+qq))Lg!Gep0zt`F(7 zl^`@qFSs5?uvU+{_@}@DnQ0@4fQXc-KIP%aFN(p0nrq2xj<_cZ&nN2uWw9-^U*MMY zB$+JPVwzIY*ji-|+pLFJ-s82@RHijk zs_PE9-Ls*S{VU9YalqUI76&D$pBP#MtP3VBC=z|Eixgp25_B+@U_qBBHg+%@P0c-b z8L=I%w!l=e-30#*F~BEdh#aqsbGEwLfXGXHFAN*n-w@V93Du&z2*c=wm^;7<(KeN% zr;nmvptIE7$?Y_cusCAu3}PW0Xv1;p=A0F~LF6>05t{i;bI@j;sb5DQhLXvPA}%pt z*I45SU3Kv{i@szDDWEjBQ(O9wjUOan2sT~r_kSV-UZ}~D&}4Qzj*{-r{@z#C>zO2@ zMao8xi#!cClXH-{l;STPL8+;r^0VC$d=>=U8(xhcD9k5h-sHw zR|-fC&fY6qF9KuGplx($JH66wpA#E7v55$9ee)`ak3`3sa}=E6Hl7C@`p-3jWUbM7 zTfGNf3GGBnRavNEpN0<{Kq+`l*KIQ%-hzkL6)4tPP$AjeOp1Ck?MjneTa2i`nlq4| zk}%+{Hd5`oM#qe(ny?glHf7Z@8+6v28Qv)OB0Pdz`d4V)l(6xnH*|!#1~o~vH;O;3bbtWjMwp5q<{Mz{Ol!3X)hs1JOhV;8H(skFU;WKILMG*?kk(oh$0lM#uYl_yoG-B- z6I&dm(Tq%Otd={BYFK9v62Ldnh-unV9g4O{1q8=6%|0rO247xYVddn#K>o-f?_TrgC0o4#>8ueZ40SDiEpls7 zcD7~k>%YvUJ!FZJ#Us(sVVbue>osTow|zCWH3G;*zZ}o%*zNjP|Ct6syNf@1W@5c& zSlc)rugfNk(H#l5d%;7X|E$gMSEHdP0C+`R@bk;F(^E4L#hXV1e{33E(t_rI^Eg&BVJ!0pa{#JT2nS{$?l+g;wx4q+#;Uw0) zi^6_4(v|t9sV;d6OgUQgpIsda4nSemLH?16p}nBfp>u%jwKRVL<##2YW?^k&u7L_h zNjlMzM$AwL(97}<+--p%WAMS9XJ>j*f$8UKXPAx2WmsAM2uYT)000yI15*T99QfDR z+JvZv(_$fuzZ}5geRc|n?020|RresXTqGQfre8!wkP{+@wm4$$W zwEv$HFae%3dEEb7PsTr-#$+Nf=FeQoM?puYQ@2-MxzQ{>$$}oAMx-ETBz(h$-fM4( z5dbWhQdk|ok&CTCYSAX?LsO(7Mg5pK+kv-qzEEJ-<{N@iL22(f@wfN>2#be;yZd|a zHprxDwk{+HRF{IlkUUD@FENn-QC+VeI55&Mw~+Ea1$P~q#$QCUy}gV~gz#P&bDr8; zXa?!~p@aDDZ-N5^=Oq#J7(T=mUIYsa#>hb#LVDl+_)=j)kx~~OLP_`xu&_QDFf#>x zPYf?8NaU&9Tf{{e|2m-YVlU?1!Ot8q-~CunKvYVl`^D73{LQAH-dV`gdS?RxZG@@{ zbR>p&#>h^3uhU)%FwhL-l24C+#=XSap1BX+42yrn(p5nt6O|ZVfvNbJC6S7-8T{je zOkX1g^&d+|OE};18MgY!?L!{{KsxjMPq_rRxuqNb-STj;L>Dn+;+M1prspNf5&L)j z1DgxAq;m&JLKAj8S}vq#?p`hB#^GBCU%hi--OcBk_gFJu6m+PSe>iHvj0)K(+oLtz zH9cN-=H~7ZfKWaA++X`Z0}N`^{TEv|NUnzpc`O?d>;dLsaSCA=0#%~b5M;roKb#&} z^KhcsLY`(#qgNN659yCV?@w+9QpDh{xapAma5ke>p0?5L5qyp8_OovF5P_ESB+|@I z9fk-)J*6UU^ZWD^;#}*rmR&zTl814hdZBTB-YP5+`EHl7y=0i$G|_8}e3vc)w`3ZC zs3J1q*$4CNAE9>y>BouHRvd5+c+|`lC=Zi01~6$bSJIN7>~M@-TOn0NV)$srn=G|u zf;W*qEmTHBGa0(@KG>j;t<)?1!(8Wh zCfq9J-}s}Utla!yddAaJF%B&oF6yy%9~MeD#9qFdE}AP{oLN}R zsMAeBMVFKwRpP)6A|TlRdM{mKE@%H1>?Sd*;pXNR1@)lKT1-OBxJuN!(@cvDL2PHd zX25vq^MaaUHpva!H5*vww`Qda<8(koI-td!U!dTA4pZw@T4Pin!0;nxrxN!xJCk~DXjTx1E z6$A!nL;jM;-Vbn6{9A;%_&Bcl%}E5biXgxR`zS4O{b%(H?n8_xo}1mi{U_Z0onDt8u~VaDqz?N z^VPxNo9wab;3%SA!x-3|%H&ZKj4;A)(9DuBX?+T8rl`|I?*I*yV=BwpPa;gwP;hrZ zssAf$xYTXrG$Jsl>)*@oPmQXSbOD%&@SN&W03iFd~U&o7z+=KV?dArg{ zXKqoRR5Vkz1`fzvVRzR+%lU+hPV!qfKHt)?7Si=iDXdoz2IcvD+~$H7-zlerXA1tEi==rKX1N`Ej3#8tH6~xKil1 zYFnUtX7JQUH&J)_MfUsK=7ra#bGOvh6u~DG#&7lXwLx^7lhzK*GnW3VO|Ji3)~z6L zjimPI*L~*fR|g`I9_lnHo-khI&ax})9FxAXtDTc7)e8J0)k!c}YZ^CIni;9YRaJFkd2H%l~g2qw*7h~@j z7)jK2?Z&n}v2EKnC${ZmV%xTD+cqY)lZnl*pXWW_d(O}EyQ{i-qjpu_dtGY{RIK9! z#P8Mb?^Bt_c0eQ?-U>C6Dd}Kdpgr0eB?~&W#s)#^p+2Xl@+OaveU9{yxyPxcmNqHk zPB*&iNCd$k>A+fRX9g32_(7LCV%S3fB>IiIFkTvu1Ai%Yh(4gP)<(1g;XInxw0CdqKe?41ZVKu-(68*VM(Kn`G9EdmV}Zx9O=dms}j# z3rpme>Zd_!kIpzGIw3pyYICP~CDZ>Kq zn3$NAl@&(m+qUY`J%{5HZd<7;#WD}aXA;U>y$X9&Mvo?8L*oi2y~CvFd8>~*nkrI_ zl(abOwR-=I1mK8zvHCQ4SPrPQzJd)`-0n9FaT8##g_PnVOo|v&hm9(%2be zN15XAB-S`LXCb13b3`mkw(ava8WgAi_YLAKJJm=;C|TC{Zpc|a$Z$mPv3LaCW8OL$ zR-a)MVJ{dW9>v--6e0{y-~dL*Mke`MsAzq&)15LF+m*WvV^WeB=lY zSVBMjKv;mGJyjJtpl2`?x|E#@4JY{9|L>1pll;lh9gHu?RUTbdwjkhlED;kX$nZI-d=GWv=GH{ zAI)az#`&*fhi~N$rGrn`akBsQ{pj}>^v5LwEyU?S`d?!chd%&klK)?aC38qm-<%FX zb4l~H|CXRqj*g8`7@<*e1Vc;PUWEcu9uZ=!^92WP2*IJG=?WZlvl42J*S%XEhW?MC zKoAUYisfO~$v)lSQmQ`vklXYxN?XmgI-7sFPCnls#Ui_yZ2XIuva{h2a#=WBOqn~X zmd9`Gc)(^+aN2BG(~V8?OUScS4No)qK~^61IrXK^(t2;-;_8$5r@e<)r^#yw^pFMB z%QHE$c$);dp|SZ5YVJRyB)|SqTQ7HAhU3zy{JSPpbejw94f#{|_pPgSTKd-aqi|nn zCsn_zAK3t7B3~_NfVQi`_o+@Pkhrs|=kQ$6h34}!xvP;cJyX$GD< zC-u*KdnH>fHKIK{fBEvPpWH1_&2P*W8Bs-H<;UbZ5py!p=oTk(dzzwPF74;EW&P$0 z1EC(7roDgBEBn%!AgnPrzmwCssa3BN`T=c>{hv0vTU~tt_#o=F)|;vro+jLlNmY8U zLOT-bvhIpm>< z4Cwoea>PzEp2i^DSp2TtFiTs^d#-!GO2agTPeb7Wt^(0rz+dKQ)4QM8m(CGT>XN2i z6iTXCUg7TZ?&Z-_-5wUlq4IG8OIKpHSTSInqAv+pn4~#Drirnetr*iy+g#Hq68NhEf4*8O^>nDi z4Oc&15x^|KRpy{pt$%MVyR3x9in&b8aJ`;plE;e|I33=sqlC#s(BVw{hZoVWXX(30 z@?|@yoB{Hzk8Nsde|p&VWjJv7-=6&1ImyT>ZfSXvyrU1~MET~#1flNSSE^C(XrJ$M zF^7Cf;#7mLT(+g4e$dv|PIgVzG)?wtZR;+OV?pvEHM7gbsWE%Hb~5{VI$$s~UNjKz zH%W$*M`{P*6#j|WLGm?n6@kt%ejr3C6YO3fR+RW4+^S_1`jRk6wT;p{`svIy#)is(k3Nx9eFP2kN_z=B* zJ_R67SJ6P3o;SuN;9ve^;?R+XMM`2xsYq!7tq_vSYBSAcm)m3`$XG{K{pkIH*W&B6 z)2zWNNg3mx`~T0m1kOp1ePv)i7A@LvJDTWt22&h`oW7cVHj(V<$c#1H|7{7SwVtMI z%sLrs*TdMlGcqU0Y?%L9f#N)I1LJ&3hL}wtvtr&(jX zc+?@_IpT5}$GA<w^JeHYE%-m%)Ib=$0Mi;~I92BLU#*`eV#LwHtA zA-pn}?2-y{i4Y`+29VC91jbVHX)~ME1xFqIj_q?VYe&u3s-w3PF%-!u!^YRem8aJw zR=N`c`sqFui6a@j_Zi3ObX#I|h@ye~+SxWx6;cx2=cHVQFJ&q>r$;AwWRMV*FO9#BtFcr7agcBv2|Lz`W};J zfVLbokxb>N{&veR4s+OjNRw`1h`_B|s+F~vPN<)1IL$4S4%Ct{$e`t_rdaMY{GCAp zxVQy9tE|zSA{gyT!I7M%mgRX;;zcKcz2>tVfCYDE857^lTm zty2#xUYXL0(&(NJ6~|@S8HoSR-d0DAiJj;<==Tg+%#)fnMpeIF$uKJt9Iv17oFBNTOyUpwc9y`;*RG4wl}c-V9v^W{ zZC$FTC9W-A9gO8gWTcVZW|@{(l@(Wge11OG8tS~Wh9D_!@UD{N5Pw>Q2*m@HIzjzFzqwS)9`2nBAh$Flb#}24|MUfs z%0|l#%Z-N)51AusbOwNuXCqk&2_ zM?-9`Kmf8E_q7p7x6!vE9zhGrJk1ipTKjW@p{ya!Du?~@6R+z8l_s_^ubeWHOVvPI zT!JYyVz2!(R9E~-sr1)`_f^Gh9eGjnU-&h#7F0L_YDFD%aHKI>FeJMvXiO@EBd``T z@<(wpRG^Dl`PIy&-+NZ}nnLhobV;9yMjm0y2&fP+sgY@u$s*0bd$KGP$FH=0$+&!r zxPD6aT|$jQoTk0Vo~W?dzT@W}`}vp6w?AvxtT_7LyNtr(0$tA)TUQU5%?l#f=V?Yb z-F32thVX-dI`L%t*JE&=5_Ih#Xxz$0EPL`3!Pw3(zMfOZ*E{WJK~Pe4SQFR2hml=H zaB`KK2i(o-J7cBM%(ClL z8nUv^+zbi}AbGWcP3@)$J$v5%5JfD9AW7-Tl9<_AUZJ_?C2I4weJI#S3i1Thw~Aez zUEz?MFYpO22PZJ%s`@v)l13D>ur)zBVj0=oE*M<5+b_1mOPHszi(^~JgZJWOx#$TW zU+){bJkR(!{MW>`uy7dfzBenujo+M-g@NK(i#l}|zRLAtL9KO4PK`uq9wqwR(V{U# z`$JO8nkXC@#M`V07|JC-jHrG0!?bDLRDP4-hDxj~N z-W#r}z$QZ|+y;-4haZ43%WjtYzb?KA7(@uhn;g#5(jo@3nI69H41q(Hxel6gqrYW0 zv3k8x@w+#e zecq$8a9V_YF4#Emy~)l4c@FKr#q+%6R;i9_mP^m$t6S0SvffkQC)Im?w&U_}f8HKM zgr2LdjZgSLhws#HS*;8eMlwK&5j?KF8&@&rdJhZVxao8^i;0yw6}N-=*~=D8yF(VSG!` zzwCODkBNjM4hi|#=4JeRBF)z%`B&$1fZvTq_%rtG4nsVbb5MbfPw+rNmWbb1bNA@z zxG*2}N3^Oq$=G-~x!|tv>#ZWHs`7rVG*GH-&i$`aax|{JcR9JX4h|*?E>~g4PP(2@ z&P_lv-9`^!>ENAn7Q5gPGkFggjBIkAdmyyJJh%`FH$O>SI3M{?)u>a|eU+WWG%@Xz zXl#{wwDd04U+Ctwt+`0lNK(fqHq?vajfnbK?DZ1&#-fT^1Rp*j- z(o&RpDc2~ir7kn45iQSg!}t>RJoP`r~tsI2t^C`|5?=R#7jYbWGdAnq9Kbz@6v zr*>quUE?Ef8gXU1Li(Pj=d=SV_}h&w@MFS)R6M1Z z(b*X&n?=#>*HixU$jw}MSJaqBpuXKL?s^_Yl_&DUHt}@4{ObR$U#s)yPy+DyVIJRk zu4>;Rnf(45xFYFLeeS(iUZ;se?x|*D?tqz}J1@}*jVvam(AaVG{ydID=mQYnb6>Tn zU%#4xZG4ZL)7;rTU#*@AHf4!J-+oySY955h&#L`9u7i$xdBklpP@ZpK%d1wdP-7zT zwep+$X8X*(1Eb-g{bQuKT!dTMRxo79YxwfQe*Sd)hrrn$43w#4QJ}_x-?`i6(OnVi$P%q^`%| zU$)*2g(O@Ez8y`SMNt}%nkLC`-&TDc3Af(ahTh`+5-kg)G*jTb{6r-gZ=lQigBa}+ zclW*DmRR5@@>|#_Ag)p-h9ZEhK&@`I+;S-eL@D3pAP2vljyf3w>$Bw z9s6bXry~lt>GZs7=0|wYi7d=Uw`tPql)|BMi=+3%e8rFi7O}Ti1$lxn^48n}32~OD zOEur`xmJs3bzHJWQ3;y$tg6nl_2fAklsP;GUOrNImciTpj)EAi8$ShmNIYh?)2^+U zgodasXGcXmu$|YzkM$DIVTMFJ-dtB^i|aLrKu^-fV7A&HM}`Q6;jrF^Q1V6Z=#nOC zSj%&hg!`MJN*aBIw`$(IYG_p-+4qW9Ijy{-c_J1oo7vJsToP1_jw@nqFSzN^v{loz z6BG^xr}pqjfrgl7_D0%Ej$ee(B;~KZCm|fdhxC6*KR0ACiL(Fv5AB`)P$3_3(o6vDC`f zlWN=Tk&}O(N%zk+FVGBOZrACRGY~PgXBEJ4i$duZKVu#~%CG8d$LT-9CSk$-uUNVz zb-ADs^I__r7_o^bcg%Ofm4<2N$s4CuP_s(X)Sy?%F-j~dmekUkRda0y^rMHDLyKQ_ zP&7gR5ZPTf4^%v*@3$dq(AJczYEs=kIgMh~0^p1)BvHORSko@a+-giFa-~EY% z(8Ru6Y_Fs!H>$)+_-mf8npef1`%j$}QcdzUp68GOiY%}pQ9f9!mC-(2R8TV^I1}PNYI*C*BTJ zR&)hU6Xri=%S&QStIv7+L|@O$h@4yNo!;?KSb2++6~l7MYrYdmJ)rJhw7R)JSQ}z$28(1e5evH!6n13usn`fQ(vj*OMf?s}4TP|F>;} zke4_8Zv6n*cl52tTzG-rE38V8-2NS;-P`k&-BYkQXXAjAR<2MR}9A=|`6L3woongW4-o8)MId|rI zs7deF0?u`t^^8dSg?#9L9Vh%9%Q00eeJ`EZhqRa({2Qi|yL3X~d(l$C6!r(?z>`$U z)F{MZBoJ0FzNfd7dEXYphnt9BzFs8Q9A6B|&)}oT9RT>^Qn_}P=to)6o{oNO=bQEZ zJw@wQy5s_D&F^~cYm8HNI{GmZ#|Mt?pZij%^lWeylsMa|x2dJS;PT4kP(j;Qx{UT-pRtIjiV+0dMnsQQ9`^@tF0&!LSv~NA#lTncUB0ENhXU-xb#ME;*xDbzlI}jGKpeAf# z+lCd^z)MGXbpaxh1lzC5#*4vqbWckEt4=r$0!3Hd3Q zL7Z2Yv8et#YX387X^R5SEagdsujMmo1%eQ-nQCfb zYede#i%m~$#aeFZ;BT30mHFDJ8hSGnMp3Uh(g@EZjau7#6`8KYFEaYJFNbx*afBwY zk%Y~RWv&pM?KZYrHFz#yES`-n6-&S~ivwJ=nLGWcz!(G$OB)L%_H$(1(^?=$`LCiK zKrxU9)(1H2e>Iy}uoCC2-Rdvmf(Jt|ca&S8YnG++9lk%1r{VD*6gt&$X(J_dI(U3O z@2<9cTdRKftqIL)XF76ip7HqZEG1Fzc)C{sJ$5EYibeYIeysO}(zpL>u?&x9dIy42 zb{Flxt!p@yqw@PWsWP7Jq<33gVT$`)6#r|d$fj#04{ z&FkcO9D42S;V^rJL{!F$MVEhnl!cvMY8ajs`pj@*{>0??OPVG!29eH^LTwn<%((Q( zOMTx91fI^~@^N%~Y`Y&1Gi0Ns$;{A%DP$l;A#g(1Df!6yEt_~LdzlOYoZgQ1b}yrR zm+EpPi9?4JB#;Gf55rj^I7V@md=~P{?4<_obpxCka(FpDhdXa%!rhUmDm3+p>FU6q zxg!ZtK_?zJUHkEZMZDn%MPqFs4PR+x>pIW;p$12Q3Ou$E$%Z+S(KIAyXfmWo&`^Zp zRX@z$8L0c0haba7pZqGv0|fr2d6en1M0A1{DHOmUaU{SA1c8CMkbKzvbZF$Vb95VQ zlebHKN_jrLD_eTnl68rO+vHC3KM0FZmEVr*G&aWey6*y3b>YOR%$H zepoxw6Ml9+{TmE#*3jd7-U$Tv`r2u!(|dJ%jH)8w59aE5?RBGmkgR`apT0Ujtzqav zah(&m*=hT^J2%;uvBYihiotR;OqHD;{f$nd2hMV{?YEaS20*7%)o(Y>ZwQ1GEfhvv zBga_Ja9mU4ef9smsq^>tXQBkkhxqa_(jKKxm7uB)_U@F>=DO=C_d24jjLC!FL5)t>mB(%poS6p^KPm=KyWLrr08e{|wqIQp#aS$~ z%|LrG!(`V_X~CgypdC)3f7kDu(}UGbZ#3D!gJwqQGOBqyGTwcs~?WhnzNZ$Yt0Ep zAgM*ph|A6*FeX;+ecb_Fzqo~;gQsBN&GzyDX%X0?6|#-Sq25B0`tu|IbaX?yuZlyd z{Wr1W$_Cn#%!!g*h2vL`sfDCucv)FlRFtLNqWpj|Sa7qovU)5_hM(;Dp^i4ZhYCZF{Ul*V>F1`LtvW1R!5x zAS0N7_@ss}g$p=Xf~2>)4E)OlsKH1Il^?XWs`Q}x2ZF4H zD%Pvz!|GQDL7z0e1fi&PDhCpS74mUIZx6jM1B>{OR01nRjbmDmEL0w$>^0n7Z9QoU z6?{N`CyXr3l@!*ORLF<<6>0>r1^Kt-OS)H+q&HSA8`Hn{&4Fr&(QiXA6hJ)UTDjnG zxSAAVdI>d|a4HQbmMg^5NDo*+t;Y33FG|2YJU*@xiZ^!WcUU|AqlalW%~5ozAK(#0 zMLUkYNx*l0j4U*BAmG`61Ofc%+C2ngO#00qP&=;a0EDX-9IuH8HpaSo5&2^o{6lUB zXQFucasOFb+R0uF#t*-u9Uhw8BN%%<`(r((ProU{8*%*_vgvo z$Ry6|n{DMxQ z1n)eRbI;UL_^?*e?>4o2%)D1uvcB9rGRRP@FdVf5o(|r&cfhW|F_aR!RFV>$a|wx< z?GDiul6GwgmuVXI$zr^!#1r}D;@64W0po#9XB~N3_(xtZ(|*oW?KjO1=!PVu?54ms z|A36L#uKyp^Pup%HP!Y>%MK+n2_+f7gI z(I$B64*&w<=nzOca|BaDhBF3(AlMfAxbwF_;fnMcyp#L>m3S|0f-_N#mwzFvSXY9@uwQ2eoW^$OOdY>92~u zh?QVCfkq|>Q>8DhcYYuv=+EAS&{~0q`0Ay(cFQ697O7dq6RJ6bTE%_1_TQH z5nVqULhkLtqXy)|4hbw;kC4uoV*n0WpNyB9tU-iCn}$EOn7t-r8a6B9>gZuG40Z5J z%t}V`4w$;soe>?fK7Vmvk(OUlOFWsLMLJ&?eQaXl>yg3(H2|m=7)|+qX8rpDWrg@> zT@Qg^>_B4+w0rRqF;gZXQZPcZXg_lK8r{LQ7zs;D&HKAF-GLsmW8gj{Mo_EP4gOi= z0=NtombXJTW#}(N2F&1=0|xT2f9q<-^u6a%`DzJ26G8T(D~rnznSY3CaWq`VOk5Ui zCc&-X)iz4s*|C^hmLd^T(Ipx_J+pG9-+Ofq9K0G?i%#r2b^DGcQ~HR)**G_Pl9WJlEXx;K*)OzoS#C3k z3!Jcx8D$OX2dkIcs|rX<=SfpB4M6CNZz?hmW?#Rh`ZN^$tkk9{tspnc*uY*xDeaOq zXF99ZsNjSH9Gd}F>RM-RzQPq^fwAcL^rL3c;`JJpFlW8EUS5!yU9ixxV^m^B;E`1( zSgG?`kQDL$Slq??xR0|a57Opp>b{eNRmA*6%WH$BxZ!a?qv;VYK%tf)42B_l-rRk9sf(UcS;bZT}h@e&J z$73Nc+3F<;d9IXvLhDz=9(noB1aQE=p+g3yRC5}O3#o`~g4>HGA7hfc7vGZUyJ*Q8 zOpUaFS=7lx@c@VefG!N!e}so}9}*DTR|Ph-vm=<7(q_Sl*$`Ns5rebtM+%u6U?Rxn zcUgiK*UAIp@Q~Omk#I9XCgPZi6@zg!6|YhM2l@ z6&J#~IUoOP>v%#)SYl83{F*Pr6ig6cn|y{F>`y0MOaYT~WHgSY83HM3N^C?J_4sGN z4nSV)Awl_9w_mL&IW+kNf?$El!L5dhGk!e%j_nxqe zVz+H66CKkodX(5Q8Knl(rHi>?=Kh=RHh(ix5Z)nvTjGhs%^cB{qlvRb!c-^9&Zdo> zi_!t*@8>1&D!~Nrlo2sn%mQqB(5-U^uWcM$Qr3+!BGK53B$$Y>17akGoPX^;3Z96h0MU4Pryk5^Za+n zAz)~izt*EH6p=2!wh{f(h*dIV0}Mo0ED+t|#EB6TDK@O}(@cZI)qr&GUt|9gGAz|_ zg$g7+DxPEzN=@#Sjv}UmT@L3K63%6SvC}lWzm}kscxt}CBE?H`y#>8hl*O6Msh{{S zpsYvSUQ7w9B>R3|_$-`7?wj@z4I{mXBr)t1qtQOq1I=5LgRBV4_^FbI7nkO1O3Su` zC?4SVS(*e9Sn|n5sw}fla-``?jY2{M@IdfJ_ZnJf@O%nRESjDwka~>e81-KM5?#A#bE{sZh^%A9~vbbS1j3SS!)(y%8 z{uighp(RGbXheaYMrIjmA&+NSxR&G}FuT}gH9Yi{*=(0agm zzLZJJNu|Yj5@t=TjYw`V5l75au)tO+aKjqJP(fO55lHKd*hUN>J%wyIF->dU*$T1! z{epsrg?G+l3wWpIPRHxVJ3iGiYKJ$EzHA zQ@`?x1{U~jiX$>mQBFJa9ThBN+?}FJj16k7#kIJiR=_l^iXyVq90S^iR76-F`a%Bw z;VDfE9fku(k6XgvmT>Rn8*;o`)5OHR&G`f zJuV>e3q`pnj8!!PGEXzU=ajawQ@%q?&)LCwpTB83PslQ{8Xuxox zU}zRAGqm(HlI3ILCP_7)r2hT3S^V($J~2ZoH>=xrTA!-TerDi2wFchuMWBAswCBS2 zE|niUPT^!RdH-fN&C4HOxUTpk+@oWXlUI%$MD?@bL)q?~98$vhLVM}D$Gzm8Wu?pN zVAO6e={5YI?hmso4?@8&GdIuj;VWOVOIYBe8wdY%GQukbsm=I9R9T_+o1K(O2GyI2 zh$?ga?2I4!z}tn!<=vqTg@d%DzPM(-AcK@E%wii{Z^q+L1!srUs$RN(J6#%yEtPH1 zcf(Ypiz}MRzB3OM0*6TNO3a(FV9}(Mf*RCr{JikMzSYE)RaK7Y8m6YEe@1eL)Ut3l zuiTHi!6-dZV*CAt`$f=^uKBEfA9(@{pxd7y{Cc{(_v(``X&ctJIpAxY?&QVGcu(e% zKM;@hzHx-|+zta%HsaK7%Fne9hbbNkGR~2sk0wUkKT~WuaS?a(%wGTe{Z+{k6|wPa zSFy;+)&u$@|Lk2Ah3HRu}pSsoevfkFa(WDi$f!Es-HPB8s(c1||U_3QYM$Z-`}JT@&-MZ8uf_tE6Eu zRUoruS?%}_MU=Pe)8aa79-96nPp;JUOBA&H-53-`|18lKZCeo$k(Afv)m3&5jzip5 z;DNx>T@?&P(Dl*LQHEB5xy9eChkB1A>Ex6DqR@{fJ*6CZ(W`m911B(K_X+Qwo&Hx)Z(L2p?)la2% z&A8}>RBNl58n9*d^{VV5k1t_pJjbmV+KI$>-6d_jOY1uycnD-*Yw8_WpYdHL{C1@` z8iqsl3%fYLRCOlM-eUC}3Lg@s;VxHn*@EN7vVY}2Psxi=07LgX*C7t^tFY6{S+cPl zj-Um@qPW*n2MX08ftm+aP0iKL8xqjB1ls>Qp7OWC8%$`d+eI8{)jpr_RZJO)z(>S& zz<3^G+-8r1vUr*J)j$f6|Ba#}(q)(l&W=0(RRm63D zs&Y9@417&t$RhnpM7{`PV)R#%`ZZy0f$Fmazv9dXN6ObpoHGs$oi>)T6K7hLbmL+X zjVNZsX7|_ES5Q#UdN(}ccf^=N2P9uzTN}r_k-GxC_Z{}`p7${fwrDbEE{uBXc7HH* zAO$5Qlr21BX8MFgVZNo^)4iN;Q?)!;L=r=K$3LIDRD<^;hTIoBc5#v|)XNL`<{6h5 zJdODdFVZUe`Gst+^#1P zmH*-wM{x>K;qCzP$;fFeje&Hb`g6=B?Fab4_z9SK$(WJPDF5sq$Ouf;@=4hXKf{p* zdK@#@Q9j-(-b4^nwVYd zSS$B&OD@-v9$dlGl&ReHITfYhA3W=BU50)H(>bI*h5%XB2DSA|BhhOm>1v1@QOti_ zF!HA15BUB=3x+kb&w7-Y6nTR{V%D1&w`KRASCk4=nD99OT;Pv%r;Yk`R+UO0{}ym? z9p7-(FI$j6wZ^%uj(@yK-NB-)R^4%JMBk( zRy=z5>&a`Viq|^{i+Xp^De(S%()18^`kCpnP|;!Um)ZG^wZNV@{=i|Ppz!{m?-p2v;>aSv3+uH?i^;35l;GNopu`{Xm7i-aS zqe;9skG95t6gsXi@myt3x?W1F{z!WkQ;vaRu2)Gu-fophMUWeT3zYJz#=06dV8 zg~CyV){+}(1t8hMiZ81{v(Eds=%D^_D<-&}%3zP_!V|JW+IdinloGlyO4BCz`c10+ zIU z2kyFk;uvr+%6)9_<(kB?eFWg!zCpM{!jm~FVQ28%8=28% zZ3v%+D=iaG_#T3;h)wPL$-kj3SRJ@U6WcJ~;qq;%MF*lWln<#YyD-}Okn@?w(0{c= zPctEhR*iR8s*D^Xu0$rCPnnsDYu6Wul-_Kh-0;=bBa(^~vgo~$Fg#zGE?t~6ii{`> zN9(&zz^r^yRJ?+$+C3J&_7;U*>~k@04U5~~qmKCPFC??so@=$4#6}V0c!)Z5*;`W2 z^PK9fL>4}pnwyAgb~1?AMKyqd3Hy+Y$EQc9xu1;Ww%ADH>slCa8z6uTWXj`GRnL&{ z=Li{a-(G#81*h3tLk`qTLk&WE`x|6jgvnuvsR(ZJ*{W8(NgG=!pdCDjX42RHJyLENEK(ABz@+)BUT|du2#-P)u#)%=) zNTHRqzo20X2olJktDx-i0SzR)JUl!+*&o#N0YrqXRtq`s)(F+9=<)bZ;Gn?>qq5j` zHcU$_dgI>2rK5}@pM=!v6}K9^AuyX4{<5?t^Ky)@SeNm;7qDF~AquThgoXW^2PNHS z1`?$$Dbr;zgtoAZRB`0fX?0g;cpfJwy%Y(VYOw_cBDoJIpq(g@DODv_#yNh0c^oT6 zTMY4_f$6U7>horAGMmiETK@3SaLcUoJ z8>&VQ0tv|_u&mtTYr>yEQ1R5~(~W~rG)MTpN)&0*1~xFHQ3QCaUc1eQB7?;j%CeH_ z%x{}WSzp{o9v!_ug&mkuu)+TQ36+N$NGD72*eAKiL&D?zzG?*)sYaNnRm(}WdBe1t|i<_SfFymcKtQqBI zBDo~1?-JJ5C>xG^97j>a6+7WMy-$^xjm^w7>8Q56w>u}fe!S#gZ6aVRij*4Ix$LdTaNb=SYU0;>hn>4d)GXK3nS(Z6%NpFHX0k>vjGVC zU=9pL@U&hY;xkr^VoTSiFDc)w^lYKi+b z#APFXgE1zj;*}~(#FRLOKjwvnw?=S#5boXOBpU{&FQ+Z}s|$i@-X?f8S$YUiF>}T>S3A4bW?PhS`#S$DZgAI`m zGRV1tGUt~Ti^>wsAuE5JJXG}#lPzFVdu==W7fv|SQd|(&3M&e`j|kcMglSma$V8A+%R#L&Z<_MBcQqjK_zE2-S*YrgI!&ETFSik{5 z%unrZ_d{)Y?sOFt6gZeZq@%zh9eZd4X)9=DqsdA{QU!<*7}JTXaQmU2r3Vv@&4fL7 z>jk(x32h3eG1mJ6?HWgf$(OPf-XRAMty-k!dvwYN5bzSFW-{da55K~Bk5I^7#VRcJ z@;kBeB}AtXU#0+Flqr{vq0zU|4`}RQ3t{Qwi9q`a&5(^>02x&)&d;}Zg4Oy{!!*rE3gu_@hNwJdRb?L(}g3-q50t5Wh8X%EB zi>RRH;nUHuLY2yZfS)O=Q|*NOkEMl>T%n&KQ#%FeNE7}9?cs{9Ei)s9Zs38znm`U2 z;0`LUtEpn230Xnuhvdc;t5db!51xr$QCA6(n^~Z&Sh2#hq8fnA%VG)70i(HT3wa7| znM@Ce8e5USq^y8>82vYUCLakzeJq&}L+!dsJpl-=YR6$|zWdRl+~5FH`j$`c#{q>Y z8=*Y?A5lhD*T7pe;($o`As8-FH>jl^DMB^;=Ywz@ zK$FCu!Vx}cy(gE?D}sc{_61QWB-q_Qz>$2-ZbZohOd^pGjy#O0 z9D|-8ynr(?m}KGR^xwnZ-5J@(hQ!kj|I>;lsLQ;PsVA75=uf)Vu_YZONNyqvt@pR z`7(|n zYR61g|+u=L{xD?m`LiSo8R7B&0 z$$v36K_#$iX6bH6CRkS?9i+{hJ*{G+X=EVf|0$y_iw`dSC)NlLcmfK5JsMcY_vws1 zfq%@M+9(kdT`#iJIDnmLFA=K$hyFSu2&FgzU?u-a_O1uO6P#D=!Iq=6*6bSw&UaQ@qX`D=f2U5$>8ix>WVd2ulimPJNJrcP7ejj%7Y zx$%K4=V!rUkQk&d^A{Q}b}a0?O=^=;r^HT*y{-uzDPg!R-Kof0D9-%alZSc;-fZ9`r zk#XdB@9F7@bNALj=v;0yIPx>;Kx#++2W(iBpGzv8jTg_H?ckdz`+2uUX%N}K?E8NJ zyC7V$>M?tZ$}h7qYaZvmrS=M)jmmRy=|@uB!)>(KC8cx6HcGS5VUgpqrNjltGQH$R zQvclaQ>0R8NEI!)eUHq;ira`=>3iBbiul+u;$m0!*GhbXa@5?+d-cSh%53D z_o2VzYhPf)I6)Na2H*w(gwuMiwvg2{cJEjJJB0?r``1|~dt4=kE0^O6GG-2mHfcPb zTVj-a$wEuJYOAN}HU0ayfOYNr^^3b)6)sF5Ng5iY3cXQJ*{O&0@X~R^7=dxq4h8!! zzbl)&%#>b&BzNQvhl@YmWvvw@CH-XO6$Qwcg^wwlM1FtUAw(szv5B@=21o>b97@2q zNNLj>XvXk>A2PNG{{!4@vmcKAxIYfgb7MVP@QZ}PC9EwM2ms)6jwzeDFQeJzpQQ9E zYi5Aw6aDG&jJJS=dK$Eqn?4Co@I04ZiuUfwqNZl9j%K2}^dL6hlK_SYDIW=>3b(ql zvJ-Dt;p6V^E*IOm5&Lpxog^Rvgz=(!fU#QQ)ne>}_1fy*&iQHH>giFQ&D@i<_B!A@ zK!_hP+NKxDvFqUQ^78WfI<~^$CaMcBK&TetH!ye0{U288bkomshPfY4880|H-#Z_o&*nH>|mq?#E0yO>7{MQ?mxDV8A?8 zz=ywu1gilnAxwULeNJXvN~61N^p{a`K5*fXR?~5ZE;zusha9{s{F`Fs1mz#g{_I;_ zgJmNA>%IXsEb~|sCNF@=2h`>mh4z>O;=op+33RZ?4%M&LnoK$aaf~k#koiG$!2A~O z4NE24B(Yd*I&D+{pf6xc)rbt_8F--k0v&AFi>!*!m-R%U5gT~JwJO4e!QcKBQ1xW*}teFZx@Ew@bN3lQ!rQF&I^ z=k#ghDQ*5NrcEf|5mOFqG8p21S=7w{1HC60kiuk~HF-DikbhRmlvO~_FUf+tjQAU^ zFPArNeXE7F%JqH@NXi%Nb1Gqa?DnH{Hj^eQwVdgbgHv}`3Pr=L8fH!6OXS|*|4`;k z1=s*7wp1mCsgO}N=C#O|MOztHt%8DD>v9+o2T*H5@NfWuQL>O0TYUn^TH&+S3QaB0 zGlz6~n(1*U+1TZ`>ONy)z{}anpNsYn`gbHp`&PG92gVe&U$JGT;t@ud$u=%ypw0w_ z*jE9rb;}~ZlZ(v`kp5*i*k%L`~;1z zANJRDr@Fi9sj9o4=f19A)Jmog?(SxHxpb~PAyY;QnF`eRI=Gu}R5-=!XkhT=&Cx@* zt(Xd904Vfr`>zeqy+LzoAt$|rwQA;pY z4Rp**Lw!S@t+@6A-9seZ%v4?rlT5DeQMAldv(M~pgET|ha&@r|w4MJjEu_l!(TSIC zT*atl)R^B>{GXDG5CeE*mxC@g8TgzE3CW1a8@fK+BZdV+rT*nHKmX7QEB{Nw!I2;H zrDV4NlYPynBnY-1>_3o7Yj2kX91v@kiDhG6K-deDD=!J4Vp;fv+~D3Q{v>0~=P`tk z2fQPA_O=yB0kCKfggrm}6?_N=(*8ui8JbwEWDFsHp%RS;cGd^k6J^yA{DPr8Hug(g z>QkMPArJ70jnKejuc9@bPIt3_D;G`Tp%w7g_W@$f4+FgFNDKB;8nd$DJqW*2a%*gF zMb6EM*X)duL)vs`3=mGw2@)R#gwFZx+$LZUf;#z06a065J7+$#YbFjE^WcHhS5-41 z8mJ(SR*M0+g&GE#${?VB%Cf|;z&r7% zzh$l)Wpyi{7@5kYn0P1!%{R%(n*V{`N8-;s^+2W7adtl%aG5p!PyW6dQfKlDs3wN{ zlSmUu4WS~$`1dYOmr=4HgZ`tH;Ol41MgKR0n6n@0D3vdkaD+WPwafUYaMdiWpPn#)C9 zmVN+{6f_jnkl0Y?)e5$9->)OU^@et$MS*wOK;}%nDUC^b0gt2o{A$nhSBaikoVN#_ zQweOnQzm81wkHbK}H1KGpmT5{O8G!&))q>mOs~zkF^Pw53 z1n33i1ZKNIwgQ5`zZ#6S+@T}_wMawM;l8z8w4Ww$$^iztAt1e=PDy!h$9sw*g2wGb z^zwX1vHHIf?+1jNgc5;RWFhEZ>%6Joj5}DR<-|+MeebQe>|6enP1&`wAcAD@+P-}p zdkQ5mMD)K|Z*knejTxNrSbvoD807I8?6D{NbNcjSCe{twaKOreQw_66Ix=FFvX82) zXEd3=*RyqD%gJb=`?Yp?KrCF1<5W`8MsQjD`Z;k_@ z0#c}&H@(NMGKa&C&i;E_^k~QFYz%=^`3Sdwt-Te^(_atxsF$W!%|UrCV^?2Ar@AC< zX+>M72dKJ^KM+MnK#a1k4nUlf(8jB)eW>>k?-F^9Ky_y*#@G?9m z7Ul;A&y920cZX`U4M}cJ|0C#c4p-(d&^Hw>4XC*$L7=*NaL?*0XYa1g?ixu(wK)U! z_g&?NdnnFlDfO-JW^}!o-G=@!=Urca$rOE=wEJJ}83R=I?oK}j>{mBrH2n6V>(oP; za2`MauEc^%ItR;TSTKU9tegt*8m+#bTrL^!+l)pgD@GDSX6bJfkh;LO3wT=c%E2V6 z$qX4?+dtlQgH3pYP56S1d3!-wOAiW!0n`mRM#Q++OITOfsF!HMozr6F4;%yE{fQD4 zdaujiDGr57-;=4;fPIa)9`ErTzka2fp8NC=V=H;FAyRzcdckIx%nVp$ z#wvcQhv;r8-M&?!MdJJ>eE%Z*(z{70f;C864b-hMVVztvm+x zdhh4?JPEM7wQ83IV}RXtR;sZD^ks>d&*$L}m>jA|tO^TO_Gs53|732ku@k+HL|9`n zH~G~~ZsNoCDXn;#=NNt%0v=rG|izcz#Yf~V^<dqOH5{e5-r7FIwa;N>9}?D??I*Nh$kilt@|s5j zIrJFK^0jp5%N|L+m-Vk0p0-HDAiV>xi};o81B?J4QseWjbq60S>S6Iwq4n<~;08?i zdU`T~nLXoa@&acJfR+x?iamzkAJM^Gzw(Xp1|OygWC1@aHBi;*39n-;=f)Q0Fl#4% zm+QJdwACSYI*MTo+tp2tsUWDmdK1vi^<4z_N(`{vd#GNTUT4|NXVwPL)WzG>p(nQw zDXK_viXkdTL!K?GFi#>eK6Hnu!Fi%I3K^K9NtJ2yw*Y7V-maF`riszH>41AhW&Go; zCPD6%@$P$dA)$eu$TE+U6WWR5E5P?yWfhUW`|y~QsZe3i+d!Z zZ)L%a_12{}Y9x|7XIiKhV|-Y%I{Gj0;1{u_9$JZ@jaqNaR~Q9n%s2X$c_)#a!T-~I!ECUD`g0BHX%ULSP_M3 zttc+8@XvJZRz;x zTtFhGtzhw=Vk)Q(VN5D8*8+)3C$Mi_*pA?il)pvyvj~R8S*c|O)igsik}A*k`kg*N zdtKUs&ExCnfDpia zszd*0b(ud}goZp|+T(BzwG4Hm=oFJa{dXAw<9!Jy>WMVdG6x8`MO3kLJ?dxl!WkS` zJ&-#t85wGsWLOJ6kMV8v$%ohdoF9$DUeDc=MtM6_*noax^Ci`7fsaN$&gb{-lL4<> z*u0c1>{c(YV~UN%pO+`+8V_692SxYCyLe;qbV`la8H9bqQmEiATp)T7ESb>SWR)0T z-RAgZLG`j+ZmL`@kC)SbFMC{e=@$@s2w|rG4$su+{a67Yi~QgX0ouH-Lt}r$n@wa; zO{I*!m;9pbN}P9HRs3qqCzr(6*hlK}5GRfHVz1xK>c$xwM#mPvn@&s9*6?~rFY42s zvEl-eC*p5BhXE$VnkA$peZ8&f4sWLVDD23uRye?BPrtV-U7>k+Kigm5XZmmht#khx?ZxByIJ=I^Mlh zzLgmj2{TPU+>k{2dO`K97V_K@BDVJCI>2aP$aTfBIXd$xGc~<$5K0W$*Uk-R-GEvE z-_bcR&}x_O0NQ5kW(A4@o}-nuy~`^a#}@J6ylQ;+am%?||L~|HNSl4_4Dm8FZDP&b z@2si3ktn}^NKso2&?f&}Fg_^hu|XLIaSlqngY*J@67AM4RjVYgqc=&hEOj=pKL>et zcwL>NUQ_?~;s(pe%B^gA44B4jH%HEubT#wAS?reMQkm{U)$NBygaU$G%`}Dgg<-P% zkUbTdlzqA+w+T5SKMN@Et<)0L23;Az&2djM-S3rf{?wqhE&?pPL>-TeXJ3>4&p%9G zQBI+1wSyzcCV`Obd|}O;Xmiev_s&E4;17>MN%+7?_@|?{&k@}ur>kRfb?!?0jd0%h zP`)^q=M=RlZP4!Gj;#?};4Hpr3WYWgjt;ph!Pg?je1$0o=BNkzB~bB?sy07C z6#s^G9dhze_B_UJ3Uk#xDNiEP{5Lv|$W|+~8dj~@hh$Ckr6riM1CF}SeGO&*)Jhv> z{5Pte3rMS4@)!0KaY6MKmLNr^*x;MNa@)eiB|*p15LeR66y;)UNsI95-QJIifvKo> z(aKrP7a8|QC}ka9L{vdy^>!KZK6G!nDEOTM_uH%Tb&5c59$nT^hDo%hP*2d|9Y;cP z@~r-#`scJccCl=0w(q|$D~3)HG53#;<9FQ@b;z0r!FoYaWVL>n7)(P&&ZeQfe0y|p zB5D+aoyL+!Log6Vcn>z>?4Y4Q9FG|VF0yBo)(ov}Yk<=Rz^ch9a9yJ`?@LCc*04|n z^2OuHol}roM8Oi~aFeH+bipbfb#Nx@%XQLwsLmzmf>uv2Z0`$Eh&*Y%-Q3+D* zKX)zksrz+%B+x;cwe|k5s4Oh|{zGNaarWzx-x@Ay$9Yr=^kVob^h2Kc*v>(*g3Xen4Q)!sm?J zjKiCx+jog?o7?_q89t($0|J?Tq#4t@8v4{eM@+>TZQ|q_z%N{Vt}_c~%3B0l6(N$` zBTo=2YpHVGgz(8r=$_hksguLs$ai*iSH}I_`eQ;xC z!ezhzrS~u~y&(Rh3#KgmGmfFRKrdV3jd*J4P>UkuINpvhQ-WAV^YDe>Px7;SS)!?zJY6!d4JIWMTMISp#naft^(nAJyxw#?@3_SSTRp z$Ct??l>-%A5~uxjEIN8qZDg|EApX9`Igl{@u?6VA4XEr@1e)X@iCIZYxdEyM(m_Ze zD!%YLj>8n|1+gRU(dFAe``2Pj1LVd!pl-VO|4E*sdbmaBn#6G=gCP)o@Y{H-lgZ8=bCzB4ReQB;AvXI|ZbklOgxdW#CXk zq|gRuXIA^sA9BX}Izfamnxx@!=&YgCf=onoW~H^UiQlR}F+L)w1yBu@)Xx>@M?Ghe zo=TPTjGz(OoRuoQ7d0E=_CC^)l>GP{xt{@AT@R`68@vFe}m(FD~i;Z zGYU)L-x*)*c2aaDu6^*|>_c(~3ez>~8PYR0 zG@E3?rmv>bvsf0ojn{QuFqK@DU~FTN8~D9*WRyhw5G{CW@fEmuUw{Cp$6Uat?hd=I z=acG-{(jcuECZHAV?hL>v`U8o-^7d?3_7&HyzET>gL>cKa52bOuRyg zXBHLTPBgHx`A;gjJVD;`>+VIk^Z}0i*ylXFl$TkQ!;R8ZkP!1b06>-&Qd~FZeUwpu zC>y51aD@oYg2xoZOzT7;RK*f{R#VoWlEn^3i2{-&&$-_lm_;2VvEf23jI*p}&i~6s zb4BbF2vlBW zF$}=Kq-T!RRkZ&GD#o|2ByC8kk|BPN7U3xbg$27{8Ro{Q*W~WbqXy#72>~M3gpkdQ zUnZuaC5s?CQiBAWF`H;&rFc@_EV@_J!^OvB3VxOVq=Ak>RA%lDsp1X>OGk(_JrkN? zDRmz}!|MfCamveIRR|2_K!V-C6(%NY5N-MpEhkWPuW^`QsUEc4#P<~Bd!7!3ZQ3}V zm+4dF2Atf^Skr5_zyECYdc|1(;kViC&^y*5RVu)8d_AD0={=%humqPSEx?Z-oIXVQvQj~;G%nEg6Msy6AV=HkAw?YJeFPg!-~RZmfc$Dr0v z;o!rloP-U$7!^KPnbX=36iGo?yrmmBZwml{*Fs%$ZkniuRDfu0YqShEDgk6NE6NQt z(mI^cD15^=3c;BVyrKdEjFb-*ktpT+ZvdY=NLecYrE4v#2%QcqS{KNMp8^{Ckuxe^ z9>O{*^gSH5WDgV!lT3f;8zLd$S~YhN(3oL6Hn`9VG?kYfaIkAOE9ZDdD5+u>ArF4- z`XF)8MWe(mR-2Zd==^fXM~TwU0!7ST*;T8)N=s=-9HOgAWjc(pQRAt|(gBsrUMquD!4o;1 z5}WLxGM1sqXrk3B7~ZPLRaN}N{#n^s|17VWwy-RvOdRbZ0TcTK=9cMk!f>`7eBa0O zu*nVOz(hOND2IM8k|*KRTz+|t2v;dc$&M0y=%H+-*BSS@8Idsy2D#`IXxMQlVi6w1 zEikT}@TVb@bz~$r5MbKT+%JfbssUg~qGXf#_6ikFUX2M2BvmzJ)LLU$D`M@7z!pe* zfk=;e<@xOR-_FIH|bx>f22PQG_d(GEy`inJ|;J@qO*)YH%UP!f!*vj|umm&T0{^~8v zu9HF&({CScob^vHcbq?HHq3RW#mI1}PeJJ>2Q93r!PEN|fi{(HlvOt<%M=e`GCq+5 zJoy4wtl=hgGmlt`WTPGKkwOkp}w)NYXo@0s1IUK)x`d?k2^v$9JW#S!kG zosI@vACDj*uA(M!yelOxGHii%Mfn~aGS_B_2K{md7+fZ|)XP1Ss$otLH|de`@Y(_P zYa_Dbv<<2mVQpeL6=v@p5|lkz*$~}_#|Cx=8jQ8eCqSU5-r$i^9E5D!jUz-9G4(pPh4a#&X+IAI*O_ zeR_}YdEvRe+qOyac@M}Lq~p3=n$%M8Rn$uL+=naBQ+#WNW`p#L5 z@i{i1127MG4`;3CJ{oKjbWETl_ONPd9?JSwE=dgzSbwHUk{?#_iQau&JABNJ` z_#r4HQ%xigI=&&gKlq%a5{0<{SL22q76Sjc-gu`-@%o_^HJsct?hRm?(E-> zyCs%(pZki>x7B^J%ppO)Czi(Y_h6C*|FiI6qcO(*+1CCH)CGM)C_?OL7SdrMD6%rw zseo3B^Oc*@!cK*5^{qdo%`ULwJq{VlS=tsE@Hh`+x!tswkZMLm*thyvB8rYmhXe*g z$>Wb6)yyNy9F^kZ^dX^#+G&NL@w@GW`Qe3JA&OJaI>bu2PK=$<(5bDCvjfOOBo+(& zM0!V);M0-g0&Y~fk6_FR_hoE@M;6UX2S(0#7YLu*ZYAk=`BM z-Z!>|@dYXjd3gSI^Y0VSRNkoy?*udfahx8()mdkM+t=&gn>)vNCfR6*V76o^`2Ef* z=x$oKD2v>;27Szg_-;lmCf26Bzp;GLi(1mgGoya)1++q1`b1Kg$TEm>ON&5EnfRAK z7){nh*9KoR=J}o*g8$-Z2zWn_l3(x7V0+7kfYW8}XSY5OBB^aN_^4;Gsd$*%|3QW!!L9mfXpO$pM=3 zJcIvz@A<`v@`NHmz=v8Y5`JmX5k)DiC=0mgDxbeoRXJG7_=c7`whEwZJe>ba`%@8p z5`DqrjrI#rox{R4bj|{}T6LS{lk|%(<}xm3E?+0DmKoNq70X`0>tOd2lMOAYMw zX9P#d5s#-aV2#ZH-R}+okr%$+b-c@R$x%>D528r@=kbT3oiEWe#S-2KSieI!{Bs>< z0UUrlB2ILtKj~qUK(lfd$#ATCi-JM?U%sDheOgQ5P_=-1WOL9xo&zRc6#`>!yc zp5mE0R}r5N%#76ZirB^hDJ0BQu0)!Ot{G~fNl=o#(n#OVOoAX+Tif`hA(E6x)>c~G zo>IEwqgjRonO9b-%Ot0P2FEd{DaFI2r)^R07e~u}c|Yg-^8yZKb81ssVcCqR&hso8 zGWtNFIqo@pP75A7*;Bnc8*Bo|2%t-skg&~BFBXgF^In~C==Zuh|9P?-@N>cE55efG z=ue!C-j?)Te)~pg%8CL1r@^%<8va*+x99Ws&gG^9T4BbJt@A97TobfWzeshClA(!TE%6A-KN8ZewVuix)A1
  • +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 literal 0 HcmV?d00001 From 17a186557d2903fdd32cd3ed0cc6bd9a046d4853 Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Sun, 30 Jun 2013 14:51:36 +0530 Subject: [PATCH 150/251] CLOUDSTACK-2813 - Some deployment failures do not release the resources. Applying the short term fix of force cleaning up if the answer recieved from startcommand is not valid Signed off by : nitin mehta --- server/src/com/cloud/vm/VirtualMachineManagerImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 11904b96952..d46bbb0b319 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -870,6 +870,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } vmProfile.setCpuOvercommitRatio(Float.parseFloat(cluster_detail_cpu.getValue())); vmProfile.setMemoryOvercommitRatio(Float.parseFloat(cluster_detail_ram.getValue())); + StartAnswer startAnswer = null; try { if (!changeState(vm, Event.OperationRetry, destHostId, work, Step.Prepare)) { @@ -917,7 +918,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac _workDao.updateStep(work, Step.Started); - StartAnswer startAnswer = cmds.getAnswer(StartAnswer.class); + startAnswer = cmds.getAnswer(StartAnswer.class); if (startAnswer != null && startAnswer.getResult()) { String host_guid = startAnswer.getHost_guid(); if( host_guid != null ) { @@ -933,6 +934,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) { throw new ConcurrentOperationException("Unable to transition to a new state."); } + startedVm = vm; if (s_logger.isDebugEnabled()) { s_logger.debug("Start completed for VM " + vm); @@ -988,7 +990,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (startedVm == null && canRetry) { Step prevStep = work.getStep(); _workDao.updateStep(work, Step.Release); - if (prevStep == Step.Started || prevStep == Step.Starting) { + // If previous step was started/ing && we got a valid answer + if((prevStep == Step.Started || prevStep == Step.Starting) && (startAnswer != null && startAnswer.getResult())){ //TODO check the response of cleanup and record it in DB for retry cleanup(vmGuru, vmProfile, work, Event.OperationFailed, false, caller, account); } else { //if step is not starting/started, send cleanup command with force=true From e74d8a1d14c470baeb160bf1454636ca7dac7564 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 11 Sep 2013 10:06:03 +0200 Subject: [PATCH 151/251] add error message for ResourceAllocationException in DeployVMCmd.java --- .../org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 63198a420be..6f78dd43a1a 100755 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -527,6 +527,9 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { } catch (ConcurrentOperationException ex) { s_logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); } } From 362bf5b8b472ed2a75ae712abc724e4d1bf06f35 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 11 Sep 2013 10:10:46 -0700 Subject: [PATCH 152/251] CLOUDSTACK-4534:[object_store_refactor] Deleting uploaded volume is not deleting the volume from backend. --- .../apache/cloudstack/storage/volume/VolumeServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 1e6858955fb..2c592b25635 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -577,12 +577,12 @@ public class VolumeServiceImpl implements VolumeService { @Override @DB public boolean destroyVolume(long volumeId) throws ConcurrentOperationException { - + // mark volume entry in volumes table as destroy state VolumeInfo vol = volFactory.getVolume(volumeId); - vol.processEvent(Event.DestroyRequested); + vol.stateTransit(Volume.Event.DestroyRequested); snapshotMgr.deletePoliciesForVolume(volumeId); - vol.processEvent(Event.OperationSuccessed); + vol.stateTransit(Volume.Event.OperationSucceeded); return true; } From 66b2809de8d21f1d5ede1247ce837cc8dfd8590d Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 11 Sep 2013 11:10:23 -0700 Subject: [PATCH 153/251] CLOUDSTACK-4642: Fix width of drop-down Fix width of timezone select drop-down to support longer time zone text --- ui/css/cloudstack3.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 0371899b631..04f71ae60e9 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -8133,6 +8133,7 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t .recurring-snapshots .schedule .forms form select { float: left; margin: 3px 10px 3px 3px; + max-width: 100%; } .recurring-snapshots .schedule .forms form input { @@ -8170,7 +8171,7 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t } .recurring-snapshots .schedule .forms form .value { - width: 370px; + width: 470px; float: left; text-align: left; } From 47c06e82523c3b568e98d64619f5a648110cc7f0 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 11 Sep 2013 15:43:42 -0700 Subject: [PATCH 154/251] CLOUDSTACK-4643 put host maintenance mode will not change XS master, when you delete a host, and this host is master, master will be designated by CS --- .../xen/resource/CitrixResourceBase.java | 107 +++++++++--------- .../xen/resource/XenServer56Resource.java | 44 ------- 2 files changed, 53 insertions(+), 98 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 9f254c35a65..4de093d34ea 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 @@ -1819,37 +1819,19 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe protected MaintainAnswer execute(MaintainCommand cmd) { Connection conn = getConnection(); try { - Pool pool = Pool.getByUuid(conn, _host.pool); - Pool.Record poolr = pool.getRecord(conn); - - Host.Record hostr = poolr.master.getRecord(conn); - if (!_host.uuid.equals(hostr.uuid)) { - s_logger.debug("Not the master node so just return ok: " + _host.ip); - return new MaintainAnswer(cmd); - } - Map hostMap = Host.getAllRecords(conn); - if (hostMap.size() == 1) { - s_logger.debug("There is the last host in pool " + poolr.uuid ); - return new MaintainAnswer(cmd); - } - Host newMaster = null; - Host.Record newMasterRecord = null; - for (Map.Entry entry : hostMap.entrySet()) { - if (!_host.uuid.equals(entry.getValue().uuid)) { - newMaster = entry.getKey(); - newMasterRecord = entry.getValue(); - s_logger.debug("New master for the XenPool is " + newMasterRecord.uuid + " : " + newMasterRecord.address); - try { - _connPool.switchMaster(_host.ip, _host.pool, conn, newMaster, _username, _password, _wait); - return new MaintainAnswer(cmd, "New Master is " + newMasterRecord.address); - } catch (XenAPIException e) { - s_logger.warn("Unable to switch the new master to " + newMasterRecord.uuid + ": " + newMasterRecord.address + " Trying again..."); - } catch (XmlRpcException e) { - s_logger.warn("Unable to switch the new master to " + newMasterRecord.uuid + ": " + newMasterRecord.address + " Trying again..."); - } + + Host host = Host.getByUuid(conn, _host.uuid); + // remove all tags cloud stack + Host.Record hr = host.getRecord(conn); + Iterator it = hr.tags.iterator(); + while (it.hasNext()) { + String tag = it.next(); + if (tag.contains("cloud")) { + it.remove(); } } - return new MaintainAnswer(cmd, false, "Unable to find an appropriate host to set as the new master"); + host.setTags(conn, hr.tags); + return new MaintainAnswer(cmd); } catch (XenAPIException e) { s_logger.warn("Unable to put server in maintainence mode", e); return new MaintainAnswer(cmd, false, e.getMessage()); @@ -7788,37 +7770,58 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); String hostuuid = cmd.getHostuuid(); try { - Map hostrs = Host.getAllRecords(conn); - boolean found = false; - for( Host.Record hr : hostrs.values() ) { - if( hr.uuid.equals(hostuuid)) { - found = true; - } - } - if( ! found) { + Host host = Host.getByUuid(conn, hostuuid); + if( isRefNull(host) ) { s_logger.debug("host " + hostuuid + " has already been ejected from pool " + _host.pool); return new Answer(cmd); } - - Pool pool = Pool.getAll(conn).iterator().next(); - Pool.Record poolr = pool.getRecord(conn); - - Host.Record masterRec = poolr.master.getRecord(conn); - if (hostuuid.equals(masterRec.uuid)) { - s_logger.debug("This is last host to eject, so don't need to eject: " + hostuuid); - return new Answer(cmd); - } - - Host host = Host.getByUuid(conn, hostuuid); // remove all tags cloud stack add before eject Host.Record hr = host.getRecord(conn); Iterator it = hr.tags.iterator(); while (it.hasNext()) { String tag = it.next(); - if (tag.startsWith("vmops-version-")) { + if (tag.contains("cloud")) { it.remove(); } } + host.setTags(conn, hr.tags); + Pool pool = Pool.getByUuid(conn, _host.pool); + Pool.Record poolr = pool.getRecord(conn); + + Host.Record hostr = poolr.master.getRecord(conn); + if (_host.uuid.equals(hostr.uuid)) { + boolean mastermigrated = false; + Map hostMap = Host.getAllRecords(conn); + if (hostMap.size() != 1) { + Host newMaster = null; + Host.Record newMasterRecord = null; + for (Map.Entry entry : hostMap.entrySet()) { + if (_host.uuid.equals(entry.getValue().uuid)) { + continue; + } + newMaster = entry.getKey(); + newMasterRecord = entry.getValue(); + s_logger.debug("New master for the XenPool is " + newMasterRecord.uuid + " : " + newMasterRecord.address); + try { + _connPool.switchMaster(_host.ip, _host.pool, conn, newMaster, _username, _password, _wait); + mastermigrated = true; + break; + } catch (Exception e) { + s_logger.warn("Unable to switch the new master to " + newMasterRecord.uuid + ": " + newMasterRecord.address + " due to " + e.toString()); + } + } + } else { + s_logger.debug("This is last host to eject, so don't need to eject: " + hostuuid); + return new Answer(cmd); + } + if ( !mastermigrated ) { + String msg = "this host is master, and cannot designate a new master"; + s_logger.debug(msg); + return new Answer(cmd, false, msg); + + } + } + // eject from pool try { Pool.eject(conn, host); @@ -7832,12 +7835,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe host.destroy(conn); } return new Answer(cmd); - } catch (XenAPIException e) { - String msg = "XenAPIException Unable to destroy host " + _host.uuid + " in xenserver database due to " + e.toString(); - s_logger.warn(msg, e); - return new Answer(cmd, false, msg); } catch (Exception e) { - String msg = "Exception Unable to destroy host " + _host.uuid + " in xenserver database due to " + e.getMessage(); + String msg = "Exception Unable to destroy host " + _host.uuid + " in xenserver database due to " + e.toString(); s_logger.warn(msg, e); return new Answer(cmd, false, msg); } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java index 716bb7cc9cc..24329e62feb 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java @@ -18,14 +18,9 @@ package com.cloud.hypervisor.xen.resource; import java.io.File; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; - import javax.ejb.Local; -import javax.naming.ConfigurationException; - import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; @@ -37,21 +32,14 @@ import com.cloud.agent.api.FenceAnswer; import com.cloud.agent.api.FenceCommand; import com.cloud.agent.api.NetworkUsageAnswer; import com.cloud.agent.api.NetworkUsageCommand; -import com.cloud.agent.api.PoolEjectCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.resource.ServerResource; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -import com.xensource.xenapi.Bond; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Host; import com.xensource.xenapi.Network; -import com.xensource.xenapi.PBD; import com.xensource.xenapi.PIF; -import com.xensource.xenapi.SR; -import com.xensource.xenapi.Types; import com.xensource.xenapi.Types.IpConfigurationMode; import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VLAN; @@ -65,8 +53,6 @@ public class XenServer56Resource extends CitrixResourceBase { public Answer executeRequest(Command cmd) { if (cmd instanceof FenceCommand) { return execute((FenceCommand) cmd); - } else if (cmd instanceof PoolEjectCommand) { - return execute((PoolEjectCommand) cmd); } else if (cmd instanceof NetworkUsageCommand) { return execute((NetworkUsageCommand) cmd); } else { @@ -226,36 +212,6 @@ public class XenServer56Resource extends CitrixResourceBase { } } - @Override - protected Answer execute(PoolEjectCommand cmd) { - Connection conn = getConnection(); - String hostuuid = cmd.getHostuuid(); - try { - Host host = Host.getByUuid(conn, hostuuid); - // remove all tags cloud stack add before eject - Host.Record hr = host.getRecord(conn); - Iterator it = hr.tags.iterator(); - while (it.hasNext()) { - String tag = it.next(); - if (tag.contains("cloud-heartbeat-")) { - it.remove(); - } - } - return super.execute(cmd); - - } catch (XenAPIException e) { - String msg = "Unable to eject host " + _host.uuid + " due to " + e.toString(); - s_logger.warn(msg, e); - return new Answer(cmd, false, msg); - } catch (Exception e) { - s_logger.warn("Unable to eject host " + _host.uuid, e); - String msg = "Unable to eject host " + _host.uuid + " due to " + e.getMessage(); - s_logger.warn(msg, e); - return new Answer(cmd, false, msg); - } - - } - protected FenceAnswer execute(FenceCommand cmd) { Connection conn = getConnection(); try { From 5950d37e9f57a41a945a11724f250aaeffa9b118 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 11 Sep 2013 17:16:25 -0700 Subject: [PATCH 155/251] CLOUDSTACK-4649 use the same virtual hardware platform in XS 6.2 as that in XS 6.0.2 --- .../cloud/hypervisor/xen/resource/XenServer56FP1Resource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java index 214dbd4059a..b22910aec11 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java @@ -160,6 +160,7 @@ public class XenServer56FP1Resource extends XenServer56Resource { VM template = templates.iterator().next(); VM.Record vmr = template.getRecord(conn); + vmr.platform.remove("device_id"); vmr.affinity = host; vmr.otherConfig.remove("disks"); vmr.otherConfig.remove("default_template"); From c08254bc70d7a26d0274d2bcb0e933aea4735af2 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 11 Sep 2013 18:07:47 -0700 Subject: [PATCH 156/251] CLOUDSTACK-4641: fix create volume from snapshot timeout issue --- .../storage/command/CopyCommand.java | 4 +++ .../resource/LibvirtComputingResource.java | 14 +++++----- .../kvm/storage/KVMStoragePoolManager.java | 14 +++++----- .../kvm/storage/KVMStorageProcessor.java | 26 +++++++++---------- .../kvm/storage/LibvirtStorageAdaptor.java | 25 ++++++++++-------- .../kvm/storage/StorageAdaptor.java | 4 +-- .../apache/cloudstack/utils/qemu/QemuImg.java | 11 +++++--- .../cloudstack/utils/qemu/QemuImgTest.java | 20 +++++++------- 8 files changed, 65 insertions(+), 53 deletions(-) diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java index c591fafbfe3..1213779547e 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -80,4 +80,8 @@ public final class CopyCommand extends Command implements StorageSubSystemComman this.cacheTO = cacheTO; } + public int getWaitInMillSeconds() { + return this.getWait() * 1000; + } + } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 6d2f10617be..3223510f501 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1367,7 +1367,7 @@ ServerResource { secondaryStorageUrl + volumeDestPath); _storagePoolMgr.copyPhysicalDisk(volume, - destVolumeName,secondaryStoragePool); + destVolumeName,secondaryStoragePool, 0); return new CopyVolumeAnswer(cmd, true, null, null, volumeName); } else { volumePath = "/volumes/" + cmd.getVolumeId() + File.separator; @@ -1377,7 +1377,7 @@ ServerResource { KVMPhysicalDisk volume = secondaryStoragePool .getPhysicalDisk(cmd.getVolumePath() + ".qcow2"); _storagePoolMgr.copyPhysicalDisk(volume, volumeName, - primaryPool); + primaryPool, 0); return new CopyVolumeAnswer(cmd, true, null, null, volumeName); } } catch (CloudRuntimeException e) { @@ -1463,7 +1463,7 @@ ServerResource { } else { BaseVol = primaryPool.getPhysicalDisk(cmd.getTemplateUrl()); vol = _storagePoolMgr.createDiskFromTemplate(BaseVol, UUID - .randomUUID().toString(), primaryPool); + .randomUUID().toString(), primaryPool, 0); } if (vol == null) { return new Answer(cmd, false, @@ -1524,7 +1524,7 @@ ServerResource { /* Copy volume to primary storage */ - KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), primaryPool); + KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), primaryPool, 0); return primaryVol; } catch (CloudRuntimeException e) { s_logger.error("Failed to download template to primary storage",e); @@ -2381,7 +2381,7 @@ ServerResource { primaryUuid); String volUuid = UUID.randomUUID().toString(); KVMPhysicalDisk disk = _storagePoolMgr.copyPhysicalDisk(snapshot, - volUuid, primaryPool); + volUuid, primaryPool, 0); return new CreateVolumeFromSnapshotAnswer(cmd, true, "", disk.getName()); } catch (CloudRuntimeException e) { @@ -2532,7 +2532,7 @@ ServerResource { QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); destFile.setFormat(PhysicalDiskFormat.QCOW2); - QemuImg q = new QemuImg(); + QemuImg q = new QemuImg(0); try { q.convert(srcFile, destFile); } catch (QemuImgException e) { @@ -2635,7 +2635,7 @@ ServerResource { cmd.getPoolUuid()); KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk( - tmplVol, UUID.randomUUID().toString(), primaryPool); + tmplVol, UUID.randomUUID().toString(), primaryPool, 0); return new PrimaryStorageDownloadAnswer(primaryVol.getName(), primaryVol.getSize()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index e09c9ba44da..945243a9cf7 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -206,25 +206,25 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); // LibvirtStorageAdaptor-specific statement if (destPool.getType() == StoragePoolType.RBD) { return adaptor.createDiskFromTemplate(template, name, - PhysicalDiskFormat.RAW, template.getSize(), destPool); + PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout); } else if (destPool.getType() == StoragePoolType.CLVM) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), - destPool); + destPool, timeout); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, - template.getSize(), destPool); + template.getSize(), destPool, timeout); } else { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, - template.getSize(), destPool); + template.getSize(), destPool, timeout); } } @@ -237,9 +237,9 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); - return adaptor.copyPhysicalDisk(disk, name, destPool); + return adaptor.copyPhysicalDisk(disk, name, destPool, timeout); } public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index c69f9b03963..1b883519073 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -194,7 +194,7 @@ public class KVMStorageProcessor implements StorageProcessor { primaryStore.getUuid()); KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, UUID.randomUUID().toString(), - primaryPool); + primaryPool, cmd.getWaitInMillSeconds()); DataTO data = null; @@ -232,7 +232,7 @@ public class KVMStorageProcessor implements StorageProcessor { } // this is much like PrimaryStorageDownloadCommand, but keeping it separate - private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool) { + private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, int timeout) { int index = templateUrl.lastIndexOf("/"); String mountpoint = templateUrl.substring(0, index); String templateName = null; @@ -269,7 +269,7 @@ public class KVMStorageProcessor implements StorageProcessor { /* Copy volume to primary storage */ KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), - primaryPool); + primaryPool, timeout); return primaryVol; } catch (CloudRuntimeException e) { s_logger.error("Failed to download template to primary storage", e); @@ -300,14 +300,14 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; - vol = templateToPrimaryDownload(templatePath, primaryPool); + vol = templateToPrimaryDownload(templatePath, primaryPool, cmd.getWaitInMillSeconds()); } else { if (templatePath.contains("/mnt")) { //upgrade issue, if the path contains path, need to extract the volume uuid from path templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1); } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); - vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool(), cmd.getWaitInMillSeconds()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -378,7 +378,7 @@ public class KVMStorageProcessor implements StorageProcessor { .getPhysicalDisk(srcVolumeName); volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); KVMPhysicalDisk newDisk = storagePoolMgr.copyPhysicalDisk(volume, volumeName, - primaryPool); + primaryPool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setFormat(ImageFormat.valueOf(newDisk.getFormat().toString().toUpperCase())); newVol.setPath(volumeName); @@ -425,7 +425,7 @@ public class KVMStorageProcessor implements StorageProcessor { secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( secondaryStorageUrl + File.separator + destVolumePath); storagePoolMgr.copyPhysicalDisk(volume, - destVolumeName,secondaryStoragePool); + destVolumeName,secondaryStoragePool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(destVolumePath + File.separator + destVolumeName); newVol.setFormat(destFormat); @@ -443,7 +443,7 @@ public class KVMStorageProcessor implements StorageProcessor { public Answer createTemplateFromVolume(CopyCommand cmd) { DataTO srcData = cmd.getSrcTO(); DataTO destData = cmd.getDestTO(); - int wait = cmd.getWait(); + int wait = cmd.getWaitInMillSeconds(); TemplateObjectTO template = (TemplateObjectTO) destData; DataStoreTO imageStore = template.getDataStore(); VolumeObjectTO volume = (VolumeObjectTO) srcData; @@ -469,7 +469,7 @@ public class KVMStorageProcessor implements StorageProcessor { String templateName = UUID.randomUUID().toString(); if (primary.getType() != StoragePoolType.RBD) { - Script command = new Script(_createTmplPath, wait * 1000, s_logger); + Script command = new Script(_createTmplPath, wait, s_logger); command.add("-f", disk.getPath()); command.add("-t", tmpltPath); command.add("-n", templateName + ".qcow2"); @@ -490,7 +490,7 @@ public class KVMStorageProcessor implements StorageProcessor { QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + templateName + ".qcow2"); destFile.setFormat(PhysicalDiskFormat.QCOW2); - QemuImg q = new QemuImg(); + QemuImg q = new QemuImg(cmd.getWaitInMillSeconds()); try { q.convert(srcFile, destFile); } catch (QemuImgException e) { @@ -618,7 +618,7 @@ public class KVMStorageProcessor implements StorageProcessor { SnapshotObjectTO snapshotOnCacheStore = (SnapshotObjectTO)answer.getNewData(); snapshotOnCacheStore.setDataStore(cacheStore); ((SnapshotObjectTO) destData).setDataStore(imageStore); - CopyCommand newCpyCmd = new CopyCommand(snapshotOnCacheStore, destData, cmd.getWait(), cmd.executeInSequence()); + CopyCommand newCpyCmd = new CopyCommand(snapshotOnCacheStore, destData, cmd.getWaitInMillSeconds(), cmd.executeInSequence()); return copyToObjectStore(newCpyCmd); } @Override @@ -722,7 +722,7 @@ public class KVMStorageProcessor implements StorageProcessor { return new CopyCmdAnswer(e.toString()); } } else { - Script command = new Script(_manageSnapshotPath, cmd.getWait() * 1000, s_logger); + Script command = new Script(_manageSnapshotPath, cmd.getWaitInMillSeconds(), s_logger); command.add("-b", snapshotDisk.getPath()); command.add("-n", snapshotName); command.add("-p", snapshotDestPath); @@ -1184,7 +1184,7 @@ public class KVMStorageProcessor implements StorageProcessor { String primaryUuid = pool.getUuid(); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getPoolType(), primaryUuid); String volUuid = UUID.randomUUID().toString(); - KVMPhysicalDisk disk = storagePoolMgr.copyPhysicalDisk(snapshotDisk, volUuid, primaryPool); + KVMPhysicalDisk disk = storagePoolMgr.copyPhysicalDisk(snapshotDisk, volUuid, primaryPool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(disk.getName()); newVol.setSize(disk.getVirtualSize()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 123a9f10ffb..42d9084727e 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -766,7 +766,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { */ @Override public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, - String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { + String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { String newUuid = UUID.randomUUID().toString(); KVMStoragePool srcPool = template.getPool(); @@ -783,20 +783,20 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (destPool.getType() != StoragePoolType.RBD) { disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); if (template.getFormat() == PhysicalDiskFormat.TAR) { - Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath()); + Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath(), timeout); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { Script.runSimpleBashScript("mkdir -p " + disk.getPath()); Script.runSimpleBashScript("chmod 755 " + disk.getPath()); - Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath()); + Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath(), timeout); } else if (format == PhysicalDiskFormat.QCOW2) { QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); QemuImgFile destFile = new QemuImgFile(disk.getPath()); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); qemu.create(destFile, backingFile); } else if (format == PhysicalDiskFormat.RAW) { QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat()); QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); qemu.convert(sourceFile, destFile); } } else { @@ -806,7 +806,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { disk.setSize(template.getVirtualSize()); disk.setVirtualSize(disk.getSize()); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); QemuImgFile srcFile; QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), @@ -960,7 +960,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { */ @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { /** With RBD you can't run qemu-img convert with an existing RBD image as destination @@ -999,24 +999,27 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { String destPath = newDisk.getPath(); PhysicalDiskFormat destFormat = newDisk.getFormat(); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); QemuImgFile srcFile = null; QemuImgFile destFile = null; if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) { if (sourceFormat == PhysicalDiskFormat.TAR) { - Script.runSimpleBashScript("tar -x -f " + sourcePath + " -C " + destPath); + Script.runSimpleBashScript("tar -x -f " + sourcePath + " -C " + destPath, timeout); } else if (sourceFormat == PhysicalDiskFormat.DIR) { Script.runSimpleBashScript("mkdir -p " + destPath); Script.runSimpleBashScript("chmod 755 " + destPath); - Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath); + Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath, timeout); } else { srcFile = new QemuImgFile(sourcePath, sourceFormat); try { Map info = qemu.info(srcFile); String backingFile = info.get(new String("backing_file")); if (sourceFormat.equals(destFormat) && backingFile == null) { - Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath); + String result = Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath, timeout); + if (result != null) { + throw new CloudRuntimeException("Failed to create disk: " + result); + } } else { destFile = new QemuImgFile(destPath, destFormat); try { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index 4956d8d4717..44e069116d6 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -40,7 +40,7 @@ public interface StorageAdaptor { public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, - KVMStoragePool destPool); + KVMStoragePool destPool, int timeout); public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, @@ -50,7 +50,7 @@ public interface StorageAdaptor { KVMStoragePool pool); public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPools); + KVMStoragePool destPools, int timeout); public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool); diff --git a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java index 4ed85930e1f..0e83bc943e1 100644 --- a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -31,6 +31,7 @@ public class QemuImg { /* The qemu-img binary. We expect this to be in $PATH */ public String _qemuImgPath = "qemu-img"; + private int timeout; /* Shouldn't we have KVMPhysicalDisk and LibvirtVMDef read this? */ public static enum PhysicalDiskFormat { @@ -46,8 +47,12 @@ public class QemuImg { } } - public QemuImg() { + public QemuImg(int timeout) { + this.timeout = timeout; + } + public void setTimeout(int timeout) { + this.timeout = timeout; } /** @@ -84,7 +89,7 @@ public class QemuImg { * @return void */ public void create(QemuImgFile file, QemuImgFile backingFile, Map options) throws QemuImgException { - Script s = new Script(_qemuImgPath); + Script s = new Script(_qemuImgPath, timeout); s.add("create"); if (options != null && !options.isEmpty()) { @@ -181,7 +186,7 @@ public class QemuImg { * @return void */ public void convert(QemuImgFile srcFile, QemuImgFile destFile, Map options) throws QemuImgException { - Script s = new Script(_qemuImgPath); + Script s = new Script(_qemuImgPath, timeout); s.add("convert"); s.add("-f"); s.add(srcFile.getFormat().toString()); diff --git a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java index 9c6ac8b1e6a..5244dda9243 100644 --- a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java +++ b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java @@ -38,7 +38,7 @@ public class QemuImgTest { long size = 10995116277760l; QemuImgFile file = new QemuImgFile(filename, size, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); Map info = qemu.info(file); @@ -69,7 +69,7 @@ public class QemuImgTest { options.put("cluster_size", clusterSize); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file, options); Map info = qemu.info(file); @@ -96,7 +96,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, endSize); Map info = qemu.info(file); @@ -125,7 +125,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, increment, true); Map info = qemu.info(file); @@ -153,7 +153,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, increment, true); Map info = qemu.info(file); @@ -182,7 +182,7 @@ public class QemuImgTest { long endSize = -1; QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); try { qemu.create(file); qemu.resize(file, endSize); @@ -199,7 +199,7 @@ public class QemuImgTest { long startSize = 20480; QemuImgFile file = new QemuImgFile(filename, 20480, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, 0); @@ -216,7 +216,7 @@ public class QemuImgTest { QemuImgFile firstFile = new QemuImgFile(firstFileName, 20480, PhysicalDiskFormat.QCOW2); QemuImgFile secondFile = new QemuImgFile(secondFileName, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(firstFile); qemu.create(secondFile, firstFile); @@ -240,7 +240,7 @@ public class QemuImgTest { QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize); QemuImgFile destFile = new QemuImgFile(destFileName); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(srcFile); qemu.convert(srcFile, destFile); Map info = qemu.info(destFile); @@ -267,7 +267,7 @@ public class QemuImgTest { QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize, srcFormat); QemuImgFile destFile = new QemuImgFile(destFileName, destFormat); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(srcFile); qemu.convert(srcFile, destFile); From 116705744135d11e13d4f5e37a1c4449108f8cff Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Wed, 11 Sep 2013 11:23:47 -0700 Subject: [PATCH 157/251] CLOUDSTACK-4645: There is no upgrade path from 4.1.1 to 4.2.0 Adding upgrade patch for 4.1.1 to 4.2.0 --- .../cloud/upgrade/DatabaseUpgradeChecker.java | 101 ++++++++++++------ .../cloud/upgrade/dao/Upgrade410to411.java | 77 +++++++++++++ ...rade410to420.java => Upgrade411to420.java} | 14 +-- ...leanup.sql => schema-411to420-cleanup.sql} | 0 ...chema-410to420.sql => schema-411to420.sql} | 0 5 files changed, 151 insertions(+), 41 deletions(-) create mode 100644 engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java rename engine/schema/src/com/cloud/upgrade/dao/{Upgrade410to420.java => Upgrade411to420.java} (99%) rename setup/db/db/{schema-410to420-cleanup.sql => schema-411to420-cleanup.sql} (100%) rename setup/db/db/{schema-410to420.sql => schema-411to420.sql} (100%) diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index 723205869f7..84ea7f67ff9 100755 --- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -61,7 +61,8 @@ import com.cloud.upgrade.dao.Upgrade306to307; import com.cloud.upgrade.dao.Upgrade307to410; import com.cloud.upgrade.dao.Upgrade30to301; import com.cloud.upgrade.dao.Upgrade40to41; -import com.cloud.upgrade.dao.Upgrade410to420; +import com.cloud.upgrade.dao.Upgrade410to411; +import com.cloud.upgrade.dao.Upgrade411to420; import com.cloud.upgrade.dao.UpgradeSnapshot217to224; import com.cloud.upgrade.dao.UpgradeSnapshot223to224; import com.cloud.upgrade.dao.VersionDao; @@ -88,108 +89,140 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.1.8", new DbUpgrade[] { new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.1.9", new DbUpgrade[] { new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.1", new DbUpgrade[] { new Upgrade221to222(), new UpgradeSnapshot223to224(), new Upgrade222to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.2", new DbUpgrade[] { new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.3", new DbUpgrade[] { new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.4", new DbUpgrade[] { new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.5", new DbUpgrade[] { new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.6", new DbUpgrade[] { new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.7", new DbUpgrade[] { new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.8", new DbUpgrade[] { new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), - new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30() - , new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.9", new DbUpgrade[] { new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.10", new DbUpgrade[] { new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.11", new DbUpgrade[] { new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.12", new DbUpgrade[] { new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.13", new DbUpgrade[] { new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.14", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.0", new DbUpgrade[] { new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("3.0.0", new DbUpgrade[] { new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.1", new DbUpgrade[] { new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("3.0.1", new DbUpgrade[] { new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.2", new DbUpgrade[] { new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("3.0.2", new DbUpgrade[] { new Upgrade302to40(), new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("4.0.0", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("4.0.0", new DbUpgrade[] { new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("4.0.1", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("4.0.1", new DbUpgrade[] { new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("4.0.2", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("4.0.2", new DbUpgrade[] { new Upgrade40to41(), + new Upgrade410to411(), new Upgrade411to420() }); + + _upgradeMap.put("4.1.0", new DbUpgrade[] { new Upgrade410to411(), new Upgrade411to420() }); + + _upgradeMap.put("4.1.1", new DbUpgrade[] { new Upgrade411to420() }); - _upgradeMap.put("4.1.0", new DbUpgrade[] { new Upgrade410to420() }); - //CP Upgrades - _upgradeMap.put("3.0.3", new DbUpgrade[] { new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); + _upgradeMap.put("3.0.3", new DbUpgrade[] { new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.4", new DbUpgrade[] { new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); + _upgradeMap.put("3.0.4", new DbUpgrade[] { new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.5", new DbUpgrade[] { new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); + _upgradeMap.put("3.0.5", new DbUpgrade[] { new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.6", new DbUpgrade[] { new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); + _upgradeMap.put("3.0.6", new DbUpgrade[] { new Upgrade306to307(), new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); - _upgradeMap.put("3.0.7", new DbUpgrade[] { new Upgrade307to410(), new Upgrade410to420() }); + _upgradeMap.put("3.0.7", new DbUpgrade[] { new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.15", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), new Upgrade410to420()}); + new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); _upgradeMap.put("2.2.16", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), new Upgrade410to420()}); + new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), + new Upgrade410to411(), new Upgrade411to420() }); } protected void runScript(Connection conn, File file) { diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java new file mode 100644 index 00000000000..916e8303111 --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.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 com.cloud.upgrade.dao; + +import java.io.File; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.log4j.Logger; + +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.vpc.NetworkACL; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade410to411 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade410to411.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] { "4.1.0", "4.1.1" }; + } + + @Override + public String getUpgradedVersion() { + return "4.1.1"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public File[] getPrepareScripts() { + // Do nothing. + return null; + } + + @Override + public void performDataMigration(Connection conn) { + // Nothing to be done + } + + @Override + public File[] getCleanupScripts() { + return null; + } +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java similarity index 99% rename from engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java rename to engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java index 8ff07dfa9dd..7400664fc90 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java @@ -45,13 +45,13 @@ import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -public class Upgrade410to420 implements DbUpgrade { - final static Logger s_logger = Logger.getLogger(Upgrade410to420.class); +public class Upgrade411to420 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade411to420.class); @Override public String[] getUpgradableVersionRange() { - return new String[] { "4.1.0", "4.2.0" }; + return new String[] { "4.1.1", "4.2.0" }; } @Override @@ -66,9 +66,9 @@ public class Upgrade410to420 implements DbUpgrade { @Override public File[] getPrepareScripts() { - String script = Script.findScript("", "db/schema-410to420.sql"); + String script = Script.findScript("", "db/schema-411to420.sql"); if (script == null) { - throw new CloudRuntimeException("Unable to find db/schema-410to420.sql"); + throw new CloudRuntimeException("Unable to find db/schema-411to420.sql"); } return new File[] { new File(script) }; @@ -973,9 +973,9 @@ public class Upgrade410to420 implements DbUpgrade { @Override public File[] getCleanupScripts() { - String script = Script.findScript("", "db/schema-410to420-cleanup.sql"); + String script = Script.findScript("", "db/schema-411to420-cleanup.sql"); if (script == null) { - throw new CloudRuntimeException("Unable to find db/schema-410to420-cleanup.sql"); + throw new CloudRuntimeException("Unable to find db/schema-411to420-cleanup.sql"); } return new File[] { new File(script) }; diff --git a/setup/db/db/schema-410to420-cleanup.sql b/setup/db/db/schema-411to420-cleanup.sql similarity index 100% rename from setup/db/db/schema-410to420-cleanup.sql rename to setup/db/db/schema-411to420-cleanup.sql diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-411to420.sql similarity index 100% rename from setup/db/db/schema-410to420.sql rename to setup/db/db/schema-411to420.sql From ec3464b660fe8226e01f9188dd7cdef4cc836705 Mon Sep 17 00:00:00 2001 From: Laszlo Hornyak Date: Thu, 29 Aug 2013 09:23:06 +0200 Subject: [PATCH 158/251] simplified casting double values do not need a Double object to be casted to long Signed-off-by: Laszlo Hornyak Signed-off-by: Min Chen --- .../api/query/dao/UserVmJoinDaoImpl.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 241c0738bc0..8c4705bc558 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -44,7 +44,6 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.vm.UserVmVO; import com.cloud.vm.VmStats; import com.cloud.vm.VirtualMachine.State; @@ -175,26 +174,20 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem cpuUsed = decimalFormat.format(cpuUtil) + "%"; userVmResponse.setCpuUsed(cpuUsed); - Double networkKbRead = Double.valueOf(vmStats.getNetworkReadKBs()); - userVmResponse.setNetworkKbsRead(networkKbRead.longValue()); + userVmResponse.setNetworkKbsRead((long) vmStats.getNetworkReadKBs()); - Double networkKbWrite = Double.valueOf(vmStats.getNetworkWriteKBs()); - userVmResponse.setNetworkKbsWrite(networkKbWrite.longValue()); + userVmResponse.setNetworkKbsWrite((long) vmStats.getNetworkWriteKBs()); if ((userVm.getHypervisorType() != null) && (userVm.getHypervisorType().equals(HypervisorType.KVM) || userVm.getHypervisorType().equals(HypervisorType.XenServer))) { // support KVM and XenServer only util 2013.06.25 - Double diskKbsRead = Double.valueOf(vmStats.getDiskReadKBs()); - userVmResponse.setDiskKbsRead(diskKbsRead.longValue()); + userVmResponse.setDiskKbsRead((long) vmStats.getDiskReadKBs()); - Double diskKbsWrite = Double.valueOf(vmStats.getDiskWriteKBs()); - userVmResponse.setDiskKbsWrite(diskKbsWrite.longValue()); + userVmResponse.setDiskKbsWrite((long) vmStats.getDiskWriteKBs()); - Double diskIORead = Double.valueOf(vmStats.getDiskReadIOs()); - userVmResponse.setDiskIORead(diskIORead.longValue()); + userVmResponse.setDiskIORead((long) vmStats.getDiskReadIOs()); - Double diskIOWrite = Double.valueOf(vmStats.getDiskWriteIOs()); - userVmResponse.setDiskIOWrite(diskIOWrite.longValue()); + userVmResponse.setDiskIOWrite((long) vmStats.getDiskWriteIOs()); } } } From 7df1b4316204efc8b07a9791d8aedd72335e51b4 Mon Sep 17 00:00:00 2001 From: Laszlo Hornyak Date: Thu, 29 Aug 2013 11:37:26 +0200 Subject: [PATCH 159/251] simplify setCpuUsed - variables inlined - cpu utilization is not cast to float from double Signed-off-by: Laszlo Hornyak Signed-off-by: Min Chen --- server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 8c4705bc558..7c16cc06382 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -165,14 +165,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setKeyPairName(userVm.getKeypairName()); if (details.contains(VMDetails.all) || details.contains(VMDetails.stats)) { - DecimalFormat decimalFormat = new DecimalFormat("#.##"); // stats calculation - String cpuUsed = null; VmStats vmStats = ApiDBUtils.getVmStatistics(userVm.getId()); if (vmStats != null) { - float cpuUtil = (float) vmStats.getCPUUtilization(); - cpuUsed = decimalFormat.format(cpuUtil) + "%"; - userVmResponse.setCpuUsed(cpuUsed); + userVmResponse.setCpuUsed(new DecimalFormat("#.##").format(vmStats.getCPUUtilization()) + "%"); userVmResponse.setNetworkKbsRead((long) vmStats.getNetworkReadKBs()); From bf7cda2598dcf9bf72e202560d7cd5f3d10bfdb1 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 12 Sep 2013 11:23:47 +0530 Subject: [PATCH 160/251] vmware dvswitch update, UI has changed for traffic label CLOUDSTACK-4089 doc changes --- docs/en-US/advanced-zone-configuration.xml | 17 +- docs/en-US/basic-zone-configuration.xml | 394 +++++++++++++----- docs/en-US/images/edit-traffic-type.png | Bin 0 -> 93662 bytes docs/en-US/images/traffic-type.png | Bin 0 -> 19159 bytes docs/en-US/vmware-cluster-config-dvswitch.xml | 78 ++-- 5 files changed, 349 insertions(+), 140 deletions(-) create mode 100644 docs/en-US/images/edit-traffic-type.png create mode 100644 docs/en-US/images/traffic-type.png diff --git a/docs/en-US/advanced-zone-configuration.xml b/docs/en-US/advanced-zone-configuration.xml index 43b9391516e..451b5454eb2 100644 --- a/docs/en-US/advanced-zone-configuration.xml +++ b/docs/en-US/advanced-zone-configuration.xml @@ -84,6 +84,14 @@ These traffic labels will be defined only for the hypervisor selected for the first cluster. For all other hypervisors, the labels can be configured after the zone is created. + (VMware only) If you have enabled Nexus dvSwitch in the environment, you must specify + the corresponding Ethernet port profile names as network traffic label for each traffic type + on the physical network. For more information on Nexus dvSwitch, see Configuring a vSphere + Cluster with Nexus 1000v Virtual Switch in the Installation Guide. If you have enabled + VMware dvSwitch in the environment, you must specify the corresponding Switch name as + network traffic label for each traffic type on the physical network. For more information, + see Configuring a VMware Datacenter with VMware Distributed Virtual Switch in the + Installation Guide. Click Next. @@ -219,9 +227,9 @@ Protocol. For XenServer, choose either NFS, iSCSI, - or PreSetup. For KVM, choose NFS, SharedMountPoint, CLVM, and RBD. For vSphere choose either VMFS - (iSCSI or FiberChannel) or NFS. The remaining fields in the screen vary depending on - what you choose here. + or PreSetup. For KVM, choose NFS, SharedMountPoint, CLVM, and RBD. For vSphere choose + either VMFS (iSCSI or FiberChannel) or NFS. The remaining fields in the screen vary + depending on what you choose here. @@ -362,7 +370,8 @@ Secondary Storage : - NFS Server. The IP address of the server or fully qualified domain name of the server. + NFS Server. The IP address of the server or fully + qualified domain name of the server. Path. The exported path from the server. diff --git a/docs/en-US/basic-zone-configuration.xml b/docs/en-US/basic-zone-configuration.xml index 965aff3f644..79d4ab8ce1b 100644 --- a/docs/en-US/basic-zone-configuration.xml +++ b/docs/en-US/basic-zone-configuration.xml @@ -22,124 +22,298 @@ under the License. -->
    - Basic Zone Configuration - - After you select Basic in the Add Zone wizard and click Next, you will be asked to enter the following details. Then click Next. - - Name. A name for the zone. - DNS 1 and 2. These are DNS servers for use by guest VMs in the zone. These DNS servers will be accessed via the public network you will add later. The public IP addresses for the zone must have a route to the DNS server named here. - Internal DNS 1 and Internal DNS 2. These are DNS servers for use by system VMs in the zone (these are VMs used by &PRODUCT; itself, such as virtual routers, console proxies, and Secondary Storage VMs.) These DNS servers will be accessed via the management traffic network interface of the System VMs. The private IP address you provide for the pods must have a route to the internal DNS server named here. - Hypervisor. (Introduced in version 3.0.1) Choose the hypervisor for the first cluster in the zone. You can add clusters with different hypervisors later, after you finish adding the zone. - Network Offering. Your choice here determines what network services will be available on the network for guest VMs. - - - - - - - Network Offering - Description - - - - - DefaultSharedNetworkOfferingWithSGService - If you want to enable security groups for guest traffic isolation, choose this. (See Using Security Groups to Control Traffic to VMs.) - - - DefaultSharedNetworkOffering - If you do not need security groups, choose this. - - - DefaultSharedNetscalerEIPandELBNetworkOffering - If you have installed a Citrix NetScaler appliance as part of your zone network, and you will be using its Elastic IP and Elastic Load Balancing features, choose this. With the EIP and ELB features, a basic zone with security groups enabled can offer 1:1 static NAT and load balancing. - - - - - - Network Domain. (Optional) If you want to assign a special domain name to the guest VM network, specify the DNS suffix. - Public. A public zone is available to all users. A zone that is not public will be assigned to a particular domain. Only users in that domain will be allowed to create guest VMs in this zone. - + Basic Zone Configuration + + + After you select Basic in the Add Zone wizard and click Next, you will be asked to enter + the following details. Then click Next. + + + Name. A name for the zone. - Choose which traffic types will be carried by the physical network. - The traffic types are management, public, guest, and storage traffic. For more information about the types, roll over the icons to display their tool tips, or see Basic Zone Network Traffic Types. This screen starts out with some traffic types already assigned. To add more, drag and drop traffic types onto the network. You can also change the network name if desired. + + DNS 1 and 2. These are DNS servers for use by guest + VMs in the zone. These DNS servers will be accessed via the public network you will add + later. The public IP addresses for the zone must have a route to the DNS server named + here. - Assign a network traffic label to each traffic type on the physical network. These labels must match the labels you have already defined on the hypervisor host. To assign each label, click the Edit button under the traffic type icon. A popup dialog appears where you can type the label, then click OK. - These traffic labels will be defined only for the hypervisor selected for the first cluster. For all other hypervisors, the labels can be configured after the zone is created. + + Internal DNS 1 and Internal DNS 2. These are DNS + servers for use by system VMs in the zone (these are VMs used by &PRODUCT; itself, such + as virtual routers, console proxies, and Secondary Storage VMs.) These DNS servers will + be accessed via the management traffic network interface of the System VMs. The private + IP address you provide for the pods must have a route to the internal DNS server named + here. - Click Next. - (NetScaler only) If you chose the network offering for NetScaler, you have an additional screen to fill out. Provide the requested details to set up the NetScaler, then click Next. - - IP address. The NSIP (NetScaler IP) address of the NetScaler device. - Username/Password. The authentication credentials to access the device. &PRODUCT; uses these credentials to access the device. - Type. NetScaler device type that is being added. It could be NetScaler VPX, NetScaler MPX, or NetScaler SDX. For a comparison of the types, see About Using a NetScaler Load Balancer. - Public interface. Interface of NetScaler that is configured to be part of the public network. - Private interface. Interface of NetScaler that is configured to be part of the private network. - Number of retries. Number of times to attempt a command on the device before considering the operation failed. Default is 2. - Capacity. Number of guest networks/accounts that will share this NetScaler device. - Dedicated. When marked as dedicated, this device will be dedicated to a single account. When Dedicated is checked, the value in the Capacity field has no significance – implicitly, its value is 1. - + + Hypervisor. (Introduced in version 3.0.1) Choose + the hypervisor for the first cluster in the zone. You can add clusters with different + hypervisors later, after you finish adding the zone. - (NetScaler only) Configure the IP range for public traffic. The IPs in this range will be used for the static NAT capability which you enabled by selecting the network offering for NetScaler with EIP and ELB. Enter the following details, then click Add. If desired, you can repeat this step to add more IP ranges. When done, click Next. - - Gateway. The gateway in use for these IP addresses. - Netmask. The netmask associated with this IP range. - VLAN. The VLAN that will be used for public traffic. - Start IP/End IP. A range of IP addresses that are assumed to be accessible from the Internet and will be allocated for access to guest VMs. - + + Network Offering. Your choice here determines what + network services will be available on the network for guest VMs. + + + + + + + Network Offering + Description + + + + + DefaultSharedNetworkOfferingWithSGService + If you want to enable security groups for guest traffic isolation, + choose this. (See Using Security Groups to Control Traffic to + VMs.) + + + DefaultSharedNetworkOffering + If you do not need security groups, choose this. + + + DefaultSharedNetscalerEIPandELBNetworkOffering + If you have installed a Citrix NetScaler appliance as part of your + zone network, and you will be using its Elastic IP and Elastic Load Balancing + features, choose this. With the EIP and ELB features, a basic zone with + security groups enabled can offer 1:1 static NAT and load + balancing. + + + + - In a new zone, &PRODUCT; adds the first pod for you. You can always add more pods later. For an overview of what a pod is, see . - To configure the first pod, enter the following, then click Next: - - Pod Name. A name for the pod. - Reserved system gateway. The gateway for the hosts in that pod. - Reserved system netmask. The network prefix that defines the pod's subnet. Use CIDR notation. - Start/End Reserved System IP. The IP range in the management network that &PRODUCT; uses to manage various system VMs, such as Secondary Storage VMs, Console Proxy VMs, and DHCP. For more information, see System Reserved IP Addresses. - + + Network Domain. (Optional) If you want to assign a + special domain name to the guest VM network, specify the DNS suffix. - Configure the network for guest traffic. Provide the following, then click Next: - - Guest gateway. The gateway that the guests should use. - Guest netmask. The netmask in use on the subnet the guests will use. - Guest start IP/End IP. Enter the first and last IP addresses that define a range that &PRODUCT; can assign to guests. - - We strongly recommend the use of multiple NICs. If multiple NICs are used, they may be in a different subnet. - If one NIC is used, these IPs should be in the same CIDR as the pod CIDR. - - - + + Public. A public zone is available to all users. A + zone that is not public will be assigned to a particular domain. Only users in that + domain will be allowed to create guest VMs in this zone. - In a new pod, &PRODUCT; adds the first cluster for you. You can always add more clusters later. For an overview of what a cluster is, see About Clusters. - To configure the first cluster, enter the following, then click Next: - - Hypervisor. (Version 3.0.0 only; in 3.0.1, this field is read only) Choose the type of hypervisor software that all hosts in this cluster will run. If you choose VMware, additional fields appear so you can give information about a vSphere cluster. For vSphere servers, we recommend creating the cluster of hosts in vCenter and then adding the entire cluster to &PRODUCT;. See Add Cluster: vSphere. - Cluster name. Enter a name for the cluster. This can be text of your choosing and is not used by &PRODUCT;. - + + + + Choose which traffic types will be carried by the physical network. + The traffic types are management, public, guest, and storage traffic. For more + information about the types, roll over the icons to display their tool tips, or see Basic + Zone Network Traffic Types. This screen starts out with some traffic types already assigned. + To add more, drag and drop traffic types onto the network. You can also change the network + name if desired. + + + Assign a network traffic label to each traffic type on the physical network. These + labels must match the labels you have already defined on the hypervisor host. To assign each + label, click the Edit button under the traffic type icon. A popup dialog appears where you + can type the label, then click OK. + These traffic labels will be defined only for the hypervisor selected for the first + cluster. For all other hypervisors, the labels can be configured after the zone is + created. + + + Click Next. + + + (NetScaler only) If you chose the network offering for NetScaler, you have an additional + screen to fill out. Provide the requested details to set up the NetScaler, then click + Next. + + + IP address. The NSIP (NetScaler IP) address of the + NetScaler device. - In a new cluster, &PRODUCT; adds the first host for you. You can always add more hosts later. For an overview of what a host is, see About Hosts. - When you add a hypervisor host to &PRODUCT;, the host must not have any VMs already running. - Before you can configure the host, you need to install the hypervisor software on the host. You will need to know which version of the hypervisor software version is supported by &PRODUCT; and what additional configuration is required to ensure the host will work with &PRODUCT;. To find these installation details, see: - - Citrix XenServer Installation and Configuration - VMware vSphere Installation and Configuration - KVM vSphere Installation and Configuration - - - To configure the first host, enter the following, then click Next: - - Host Name. The DNS name or IP address of the host. - Username. The username is root. - Password. This is the password for the user named above (from your XenServer or KVM install). - Host Tags. (Optional) Any labels that you use to categorize hosts for ease of maintenance. For example, you can set this to the cloud's HA tag (set in the ha.tag global configuration parameter) if you want this host to be used only for VMs with the "high availability" feature enabled. For more information, see HA-Enabled Virtual Machines as well as HA for Hosts. - + + Username/Password. The authentication credentials + to access the device. &PRODUCT; uses these credentials to access the device. - In a new cluster, &PRODUCT; adds the first primary storage server for you. You can always add more servers later. For an overview of what primary storage is, see About Primary Storage. - To configure the first primary storage server, enter the following, then click Next: - - Name. The name of the storage device. - Protocol. For XenServer, choose either NFS, iSCSI, or PreSetup. For KVM, choose NFS, SharedMountPoint,CLVM, or RBD. For vSphere choose either VMFS (iSCSI or FiberChannel) or NFS. The remaining fields in the screen vary depending on what you choose here. - + + Type. NetScaler device type that is being added. It + could be NetScaler VPX, NetScaler MPX, or NetScaler SDX. For a comparison of the types, + see About Using a NetScaler Load Balancer. - + + Public interface. Interface of NetScaler that is + configured to be part of the public network. + + + Private interface. Interface of NetScaler that is + configured to be part of the private network. + + + Number of retries. Number of times to attempt a + command on the device before considering the operation failed. Default is 2. + + + Capacity. Number of guest networks/accounts that + will share this NetScaler device. + + + Dedicated. When marked as dedicated, this device + will be dedicated to a single account. When Dedicated is checked, the value in the + Capacity field has no significance – implicitly, its value is 1. + + + + + (NetScaler only) Configure the IP range for public traffic. The IPs in this range will + be used for the static NAT capability which you enabled by selecting the network offering + for NetScaler with EIP and ELB. Enter the following details, then click Add. If desired, you + can repeat this step to add more IP ranges. When done, click Next. + + + Gateway. The gateway in use for these IP + addresses. + + + Netmask. The netmask associated with this IP + range. + + + VLAN. The VLAN that will be used for public + traffic. + + + Start IP/End IP. A range of IP addresses that are + assumed to be accessible from the Internet and will be allocated for access to guest + VMs. + + + + + In a new zone, &PRODUCT; adds the first pod for you. You can always add more pods later. + For an overview of what a pod is, see . + To configure the first pod, enter the following, then click Next: + + + Pod Name. A name for the pod. + + + Reserved system gateway. The gateway for the hosts + in that pod. + + + Reserved system netmask. The network prefix that + defines the pod's subnet. Use CIDR notation. + + + Start/End Reserved System IP. The IP range in the + management network that &PRODUCT; uses to manage various system VMs, such as Secondary + Storage VMs, Console Proxy VMs, and DHCP. For more information, see System Reserved IP + Addresses. + + + + + Configure the network for guest traffic. Provide the following, then click Next: + + + Guest gateway. The gateway that the guests should + use. + + + Guest netmask. The netmask in use on the subnet the + guests will use. + + + Guest start IP/End IP. Enter the first and last IP + addresses that define a range that &PRODUCT; can assign to guests. + + + We strongly recommend the use of multiple NICs. If multiple NICs are used, they + may be in a different subnet. + + + If one NIC is used, these IPs should be in the same CIDR as the pod CIDR. + + + + + + + In a new pod, &PRODUCT; adds the first cluster for you. You can always add more clusters + later. For an overview of what a cluster is, see About Clusters. + To configure the first cluster, enter the following, then click Next: + + + Hypervisor. (Version 3.0.0 only; in 3.0.1, this + field is read only) Choose the type of hypervisor software that all hosts in this + cluster will run. If you choose VMware, additional fields appear so you can give + information about a vSphere cluster. For vSphere servers, we recommend creating the + cluster of hosts in vCenter and then adding the entire cluster to &PRODUCT;. See Add + Cluster: vSphere. + + + Cluster name. Enter a name for the cluster. This + can be text of your choosing and is not used by &PRODUCT;. + + + + + In a new cluster, &PRODUCT; adds the first host for you. You can always add more hosts + later. For an overview of what a host is, see About Hosts. + + When you add a hypervisor host to &PRODUCT;, the host must not have any VMs already + running. + + Before you can configure the host, you need to install the hypervisor software on the + host. You will need to know which version of the hypervisor software version is supported by + &PRODUCT; and what additional configuration is required to ensure the host will work with + &PRODUCT;. To find these installation details, see: + + + Citrix XenServer Installation and Configuration + + + VMware vSphere Installation and Configuration + + + KVM vSphere Installation and Configuration + + + + To configure the first host, enter the following, then click Next: + + + Host Name. The DNS name or IP address of the + host. + + + Username. The username is root. + + + Password. This is the password for the user named + above (from your XenServer or KVM install). + + + Host Tags. (Optional) Any labels that you use to + categorize hosts for ease of maintenance. For example, you can set this to the cloud's + HA tag (set in the ha.tag global configuration parameter) if you want this host to be + used only for VMs with the "high availability" feature enabled. For more information, + see HA-Enabled Virtual Machines as well as HA for Hosts. + + + + + In a new cluster, &PRODUCT; adds the first primary storage server for you. You can + always add more servers later. For an overview of what primary storage is, see About Primary + Storage. + To configure the first primary storage server, enter the following, then click + Next: + + + Name. The name of the storage device. + + + Protocol. For XenServer, choose either NFS, iSCSI, + or PreSetup. For KVM, choose NFS, SharedMountPoint,CLVM, or RBD. For vSphere choose + either VMFS (iSCSI or FiberChannel) or NFS. The remaining fields in the screen vary + depending on what you choose here. + + + +
    diff --git a/docs/en-US/images/edit-traffic-type.png b/docs/en-US/images/edit-traffic-type.png new file mode 100644 index 0000000000000000000000000000000000000000..16cda947fdbd346838eca731b6a1171d90bec26e GIT binary patch literal 93662 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 z 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 wm9yq8DxSA!C1I{~`dM7;D<{9 literal 0 HcmV?d00001 diff --git a/docs/en-US/vmware-cluster-config-dvswitch.xml b/docs/en-US/vmware-cluster-config-dvswitch.xml index 311673a0e09..2aeea2a5e5c 100644 --- a/docs/en-US/vmware-cluster-config-dvswitch.xml +++ b/docs/en-US/vmware-cluster-config-dvswitch.xml @@ -75,27 +75,52 @@ vds-name.png: Name of the dvSwitch as specified in the vCenter. - Use this VDS name when you specify the switch name in the traffic label while creating the - zone. Traffic label format is [["Name of vSwitch/dvSwitch/EthernetPortProfile"][,"VLAN - ID"[,"vSwitch Type"]]] + Use this VDS name in the following: + + + The switch name in the Edit traffic label dialog while configuring a public and guest + traffic during zone creation. + During a zone creation, ensure that you select VMware vNetwork Distributed Virtual Switch + when you configure guest and public traffic type. + + + + + + traffic-type.png: virtual switch type + + + + + The Public Traffic vSwitch Type field when you add a VMware VDS-enabled cluster. + + + The switch name in the traffic label while updating the switch type in a zone. + + + Traffic label format in the last case is [["Name of + vSwitch/dvSwitch/EthernetPortProfile"][,"VLAN ID"[,"vSwitch Type"]]] The possible values for traffic labels are: - empty string - dvSwitch0 - dvSwitch0,200 - dvSwitch1,300,vmwaredvs - myEthernetPortProfile,,nexusdvs - dvSwitch0,,vmwaredvs + + empty string + + + dvSwitch0 + + + dvSwitch0,200 + + + dvSwitch1,300,vmwaredvs + + + myEthernetPortProfile,,nexusdvs + + + dvSwitch0,,vmwaredvs + - - - - - - - traffic-label.png: Traffic label specified while zone creation. - - @@ -124,10 +149,10 @@ 2 VLAN ID to be used for this traffic wherever applicable. - This field would be used for only public traffic as of now. In case of guest traffic this - field would be ignored and could be left empty for guest traffic. By default empty - string would be assumed which translates to untagged VLAN for that specific traffic - type. + This field would be used for only public traffic as of now. In case of + guest traffic this field would be ignored and could be left empty for guest traffic. + By default empty string would be assumed which translates to untagged VLAN for that + specific traffic type. 3 @@ -153,6 +178,7 @@ +
  • Enabling Virtual Distributed Switch in &PRODUCT; @@ -171,6 +197,10 @@
    Configuring Distributed Virtual Switch in &PRODUCT; You can configure VDS by adding the necessary resources while a zone is created. + Alternatively, at the cluster level, 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: @@ -179,10 +209,6 @@ 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: From ab63a4b902f3835f9bf5f00afe2515a3be03d8e4 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 12 Sep 2013 11:58:27 +0530 Subject: [PATCH 161/251] CLOUDSTACK-2289 updated with all the granular parameter at cluster/zone level --- docs/en-US/global-config.xml | 362 +++++++++++++++++++++++++++-------- 1 file changed, 287 insertions(+), 75 deletions(-) diff --git a/docs/en-US/global-config.xml b/docs/en-US/global-config.xml index 407d97d2ee4..59373de55b1 100644 --- a/docs/en-US/global-config.xml +++ b/docs/en-US/global-config.xml @@ -1,40 +1,43 @@ - %BOOK_ENTITIES; ]> + - +
    Setting Configuration Parameters
    About 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 configuration parameters, depending on what optional features - you are setting up. - You can set default values at the global level, which will be in effect throughout the cloud unless you override them at a lower level. - You can make local settings, which will override the global configuration parameter values, at the level of an account, zone, cluster, or primary storage. - The documentation for each &PRODUCT; feature should direct you to the names of the applicable - parameters. The following table - shows a few of the more useful 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 configuration parameters, depending on what optional features you + are setting up. You can set default values at the global level, which will be in effect + throughout the cloud unless you override them at a lower level. You can make local settings, + which will override the global configuration parameter values, at the level of an account, + zone, cluster, or primary storage. + The documentation for each &PRODUCT; feature should direct you to the names of the + applicable parameters. The following table shows a few of the more useful parameters. - - + + Field @@ -44,53 +47,59 @@ 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. + 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. + 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. + 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. + 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. + 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. + 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 + page size 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. + 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. @@ -98,33 +107,236 @@
    Setting Global Configuration Parameters - Use the following steps to set global configuration parameters. These values will be the defaults in effect throughout your &PRODUCT; deployment. + Use the following steps to set global configuration parameters. These values will be the + defaults in effect throughout your &PRODUCT; deployment. - Log in to the UI as administrator. - In the left navigation bar, click Global Settings. - In Select View, choose one of the following: + + Log in to the UI as administrator. + + + In the left navigation bar, click Global Settings. + + + In Select View, choose one of the following: - Global Settings. This displays a list of the parameters with brief descriptions and current values. - Hypervisor Capabilities. This displays a list of hypervisor versions with the maximum number of guests supported for each. + + Global Settings. This displays a list of the parameters with brief descriptions + and current values. + + + Hypervisor Capabilities. This displays a list of hypervisor versions with the + maximum number of guests supported for each. + - Use the search box to narrow down the list to those you are interested in. - In the Actions column, click the Edit icon to modify a value. If you are viewing Hypervisor Capabilities, you must click the name of the hypervisor first to display the editing screen. - + + Use the search box to narrow down the list to those you are interested in. + + + In the Actions column, click the Edit icon to modify a value. If you are viewing + Hypervisor Capabilities, you must click the name of the hypervisor first to display the + editing screen. + +
    Setting Local Configuration Parameters - Use the following steps to set local configuration parameters for an account, zone, cluster, or primary storage. - These values will override the global configuration settings. + Use the following steps to set local configuration parameters for an account, zone, + cluster, or primary storage. These values will override the global configuration + settings. - Log in to the UI as administrator. - In the left navigation bar, click Infrastructure or Accounts, depending on where you want to set a value. - Find the name of the particular resource that you want to work with. For example, if you are in Infrastructure, - click View All on the Zones, Clusters, or Primary Storage area. - Click the name of the resource where you want to set a limit. - Click the Settings tab. - Use the search box to narrow down the list to those you are interested in. - In the Actions column, click the Edit icon to modify a value. + + Log in to the UI as administrator. + + + In the left navigation bar, click Infrastructure or Accounts, depending on where you + want to set a value. + + + Find the name of the particular resource that you want to work with. For example, if + you are in Infrastructure, click View All on the Zones, Clusters, or Primary Storage + area. + + + Click the name of the resource where you want to set a limit. + + + Click the Settings tab. + + + Use the search box to narrow down the list to those you are interested in. + + + In the Actions column, click the Edit icon to modify a value. +
    - +
    + Granular Global Configuration Parameters + The following global configuration parameters have been made more granular. The parameters + are listed under three different scopes: account, cluster, and zone. + + + + + + + + Field + Field + Value + + + + + account + remote.access.vpn.client.iprange + The range of IPs to be allocated to remotely access the VPN clients. The + first IP in the range is used by the VPN server. + + + account + allow.public.user.templates + If false, users will not be able to create public templates. + + + account + use.system.public.ips + If true and if an account has one or more dedicated public IP ranges, IPs + are acquired from the system pool after all the IPs dedicated to the account have + been consumed. + + + account + use.system.guest.vlans + If true and if an account has one or more dedicated guest VLAN ranges, + VLANs are allocated from the system pool after all the VLANs dedicated to the + account have been consumed. + + + cluster + cluster.storage.allocated.capacity.notificationthreshold + The percentage, as a value between 0 and 1, of allocated storage utilization above which + alerts are sent that the storage is below the threshold. + + + cluster + cluster.storage.capacity.notificationthreshold + The percentage, as a value between 0 and 1, of storage utilization above which alerts are sent + that the available storage is below the threshold. + + + cluster + cluster.cpu.allocated.capacity.notificationthreshold + The percentage, as a value between 0 and 1, of cpu utilization above which alerts are sent + that the available CPU is below the threshold. + + + cluster + cluster.memory.allocated.capacity.notificationthreshold + The percentage, as a value between 0 and 1, of memory utilization above which alerts are sent + that the available memory is below the threshold. + + + cluster + cluster.cpu.allocated.capacity.disablethreshold + The percentage, as a value between 0 and 1, of CPU utilization above which allocators will + disable that cluster from further usage. Keep the corresponding notification + threshold lower than this value to be notified beforehand. + + + cluster + cluster.memory.allocated.capacity.disablethreshold + The percentage, as a value between 0 and 1, of memory utilization above which allocators will + disable that cluster from further usage. Keep the corresponding notification + threshold lower than this value to be notified beforehand. + + + cluster + cpu.overprovisioning.factor + Used for CPU over-provisioning calculation; the available CPU will be the mathematical product + of actualCpuCapacity and cpu.overprovisioning.factor. + + + cluster + mem.overprovisioning.factor + Used for memory over-provisioning calculation. + + + cluster + vmware.reserve.cpu + Specify whether or not to reserve CPU when not over-provisioning; In case of CPU + over-provisioning, CPU is always reserved. + + + cluster + vmware.reserve.mem + Specify whether or not to reserve memory when not over-provisioning; In case of memory + over-provisioning memory is always reserved. + + + zone + pool.storage.allocated.capacity.disablethreshold + The percentage, as a value between 0 and 1, of allocated storage utilization above which + allocators will disable that pool because the available allocated storage is below + the threshold. + + + zone + pool.storage.capacity.disablethreshold + The percentage, as a value between 0 and 1, of storage utilization above which allocators will + disable the pool because the available storage capacity is below the + threshold. + + + zone + storage.overprovisioning.factor + Used for storage over-provisioning calculation; available storage will be the mathematical + product of actualStorageSize and storage.overprovisioning.factor. + + + zone + network.throttling.rate + Default data transfer rate in megabits per second allowed in a network. + + + zone + guest.domain.suffix + Default domain name for VMs inside a virtual networks with a router. + + + zone + router.template.xen + Name of the default router template on Xenserver. + + + zone + router.template.kvm + Name of the default router template on KVM. + + + zone + router.template.vmware + Name of the default router template on VMware. + + + zone + enable.dynamic.scale.vm + Enable or diable dynamically scaling of a VM. + + + zone + use.external.dns + Bypass internal DNS, and use the external DNS1 and DNS2 + + + zone + blacklisted.routes + Routes that are blacklisted cannot be used for creating static routes for a VPC Private + Gateway. + + + + +
    +
    From 9d7f894d10f85f6b6adfd9bbb0ff4a4019814f0e Mon Sep 17 00:00:00 2001 From: Devdeep Singh Date: Thu, 12 Sep 2013 16:14:21 +0530 Subject: [PATCH 162/251] CLOUDSTACK-4617. Add host succeeds but host goes to alert state and comes back to UP state after a short while. Fixed the link local network creation part. --- .../xen/resource/CitrixResourceBase.java | 10 +++++-- .../xen/resource/XenServer610Resource.java | 30 +++++++++++-------- 2 files changed, 26 insertions(+), 14 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 4de093d34ea..4fd2353eb15 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 @@ -4796,6 +4796,12 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } + protected void plugDom0Vif(Connection conn, VIF dom0Vif) throws XmlRpcException, XenAPIException { + if (dom0Vif != null) { + dom0Vif.plug(conn); + } + } + private void setupLinkLocalNetwork(Connection conn) { try { Network.Record rec = new Network.Record(); @@ -4850,11 +4856,11 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vifr.network = linkLocal; vifr.lockingMode = Types.VifLockingMode.NETWORK_DEFAULT; dom0vif = VIF.create(conn, vifr); - dom0vif.plug(conn); + plugDom0Vif(conn, dom0vif); } else { s_logger.debug("already have a vif on dom0 for link local network"); if (!dom0vif.getCurrentlyAttached(conn)) { - dom0vif.plug(conn); + plugDom0Vif(conn, dom0vif); } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java index 20d9a3dc2d5..c3c0307ca1b 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java @@ -19,8 +19,8 @@ package com.cloud.hypervisor.xen.resource; import java.io.File; import java.util.ArrayList; -import java.util.List; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -28,35 +28,37 @@ import javax.ejb.Local; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; -import com.cloud.resource.ServerResource; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; -import com.cloud.vm.VirtualMachine.State; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; -import com.cloud.agent.api.storage.MigrateVolumeAnswer; -import com.cloud.agent.api.storage.MigrateVolumeCommand; import com.cloud.agent.api.MigrateWithStorageAnswer; import com.cloud.agent.api.MigrateWithStorageCommand; +import com.cloud.agent.api.MigrateWithStorageCompleteAnswer; +import com.cloud.agent.api.MigrateWithStorageCompleteCommand; import com.cloud.agent.api.MigrateWithStorageReceiveAnswer; import com.cloud.agent.api.MigrateWithStorageReceiveCommand; import com.cloud.agent.api.MigrateWithStorageSendAnswer; import com.cloud.agent.api.MigrateWithStorageSendCommand; -import com.cloud.agent.api.MigrateWithStorageCompleteAnswer; -import com.cloud.agent.api.MigrateWithStorageCompleteCommand; -import com.cloud.agent.api.to.StorageFilerTO; -import com.cloud.network.Networks.TrafficType; +import com.cloud.agent.api.storage.MigrateVolumeAnswer; +import com.cloud.agent.api.storage.MigrateVolumeCommand; import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; -import com.cloud.agent.api.to.NicTO; +import com.cloud.network.Networks.TrafficType; +import com.cloud.resource.ServerResource; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.vm.VirtualMachine.State; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Host; import com.xensource.xenapi.Network; import com.xensource.xenapi.SR; import com.xensource.xenapi.Task; import com.xensource.xenapi.Types; +import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VBD; import com.xensource.xenapi.VDI; import com.xensource.xenapi.VIF; @@ -447,4 +449,8 @@ public class XenServer610Resource extends XenServer56FP1Resource { return dynamicMinRam; } + @Override + protected void plugDom0Vif(Connection conn, VIF dom0Vif) throws XmlRpcException, XenAPIException { + // do nothing. In xenserver 6.1 and beyond this step isn't needed. + } } From 81510f2b6d18f01031527b973b4e140605642ac9 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 12 Sep 2013 18:11:07 +0530 Subject: [PATCH 163/251] known issues from animesh has been added, edited, updated CLOUDSTACK-4245 and heading changes --- docs/en-US/Release_Notes.xml | 2448 +++++++++++++++++++--------------- 1 file changed, 1397 insertions(+), 1051 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index ac010d7d398..96cf6e63b39 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -19,7 +19,7 @@ specific language governing permissions and limitations under the License. --> - + Welcome to &PRODUCT; 4.2 @@ -55,836 +55,848 @@ under the License. If you find any errors or problems in this guide, please see . We hope you enjoy working with &PRODUCT;! - - What's New in 4.2 - &PRODUCT; 4.2 includes the following new features. -
    - Features to Support Heterogeneous Workloads - The following new features help &PRODUCT; 4.2 better support both legacy and cloud-era - style zones. -
    - Regions - To increase reliability of the cloud, you can optionally group resources into - geographic regions. A region is the largest available organizational unit within a cloud - deployment. A region is made up of several availability zones, where each zone is - equivalent to a datacenter. Each region is controlled by its own cluster of Management - Servers, running in one of the zones. The zones in a region are typically located in close - geographical proximity. Regions are a useful technique for providing fault tolerance and - disaster recovery. - By grouping zones into regions, the cloud can achieve higher availability and - scalability. User accounts can span regions, so that users can deploy VMs in multiple, - widely-dispersed regions. Even if one of the regions becomes unavailable, the services are - still available to the end-user through VMs deployed in another region. And by grouping - communities of zones under their own nearby Management Servers, the latency of - communications within the cloud is reduced compared to managing widely-dispersed zones - from a single central Management Server. - Usage records can also be consolidated and tracked at the region level, creating - reports or invoices for each geographic region. + + Version 4.2.0 +
    + &PRODUCT; 4.2 includes the following new features. +
    + Features to Support Heterogeneous Workloads + The following new features help &PRODUCT; 4.2 better support both legacy and cloud-era + style zones. +
    + Regions + To increase reliability of the cloud, you can optionally group resources into + geographic regions. A region is the largest available organizational unit within a cloud + deployment. A region is made up of several availability zones, where each zone is + equivalent to a datacenter. Each region is controlled by its own cluster of Management + Servers, running in one of the zones. The zones in a region are typically located in + close geographical proximity. Regions are a useful technique for providing fault + tolerance and disaster recovery. + By grouping zones into regions, the cloud can achieve higher availability and + scalability. User accounts can span regions, so that users can deploy VMs in multiple, + widely-dispersed regions. Even if one of the regions becomes unavailable, the services + are still available to the end-user through VMs deployed in another region. And by + grouping communities of zones under their own nearby Management Servers, the latency of + communications within the cloud is reduced compared to managing widely-dispersed zones + from a single central Management Server. + Usage records can also be consolidated and tracked at the region level, creating + reports or invoices for each geographic region. + + + + + + region-overview.png: Nested structure of a region. + + +
    +
    + Object Storage Plugin Architecture + Artifacts such as templates, ISOs and snapshots are kept in storage which &PRODUCT; + refers to as secondary storage. To improve scalability and performance, as when a number + of hosts access secondary storage concurrently, object storage can be used for secondary + storage. Object storage can also provide built-in high availability capability. When + using object storage, access to secondary storage data can be made available across + multiple zones in a region. This is a huge benefit, as it is no longer necessary to copy + templates, snapshots etc. across zones as would be needed in an NFS-only + environment. + Object storage is provided through third-party software such as Amazon Simple + Storage Service (S3) or any other object storage that supports the S3 interface. These + third party object storages can be integrated with &PRODUCT; by writing plugin software + that uses the object storage plugin capability introduced in &PRODUCT; 4.2. Several new + pluggable service interfaces are available so that different storage providers can + develop vendor-specific plugins based on the well-defined contracts that can be + seemlessly managed by &PRODUCT;. +
    +
    + Zone-Wide Primary Storage + (Supported on KVM and VMware) + In &PRODUCT; 4.2, you can provision primary storage on a per-zone basis. Data + volumes in the primary storage can be attached to any VM on any host in the zone. + In previous &PRODUCT; versions, each cluster had its own primary storage. Data in + the primary storage was directly available only to VMs within that cluster. If a VM in a + different cluster needed some of the data, it must be copied from one cluster to + another, using the zone's secondary storage as an intermediate step. This operation was + unnecessarily time-consuming. +
    +
    + VMware Datacenter Now Visible As a &PRODUCT; Zone + In order to support zone-wide functions for VMware, changes have been made so that + &PRODUCT; is now aware of VMware Datacenters and can map each Datacenter to a &PRODUCT; + zone. Previously, &PRODUCT; was only aware of VMware Clusters, a smaller organizational + unit than Datacenters. This implies that a single &PRODUCT; zone could possibly contain + clusters from different VMware Datacenters. In order for zone-wide functions, such as + zone-wide primary storage, to work for VMware hosts, &PRODUCT; has to make sure that a + zone contains only a single VMware Datacenter. Therefore, when you are creating a new + &PRODUCT; zone, you will now be able to select a VMware Datacenter for the zone. If you + are provisioning multiple VMware Datacenters, each one will be set up as a single zone + in &PRODUCT;. + + If you are upgrading from a previous &PRODUCT; version, and your existing + deployment contains a zone with clusters from multiple VMware Datacenters, that zone + will not be forcibly migrated to the new model. It will continue to function as + before. However, any new zone-wide operations, such as zone-wide primary storage, will + not be available in that zone. + + +
    +
    +
    + Third-Party UI Plugin Framework + Using the new third-party plugin framework, you can write and install extensions to + &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the + Citrix-provided features. + The basic procedure for adding a UI plugin is explained in the Developer Guide. In + summary, the plugin developer creates the plugin code itself (in Javascript), a thumbnail + image, the plugin listing, and a CSS file. The &PRODUCT; administrator adds the folder + containing the plugin code under the &PRODUCT; PLUGINS folder and adds the plugin name to + a configuration file (plugins.js). + The next time the user refreshes the UI in the browser, the plugin will appear under + the Plugins button in the left navigation bar. - + - region-overview.png: Nested structure of a region. + plugin4.jpg: The plugin appears in the UI
    -
    - Object Storage Plugin Architecture - Artifacts such as templates, ISOs and snapshots are kept in storage which &PRODUCT; - refers to as secondary storage. To improve scalability and performance, as when a number - of hosts access secondary storage concurrently, object storage can be used for secondary - storage. Object storage can also provide built-in high availability capability. When using - object storage, access to secondary storage data can be made available across multiple - zones in a region. This is a huge benefit, as it is no longer necessary to copy templates, - snapshots etc. across zones as would be needed in an NFS-only environment. - Object storage is provided through third-party software such as Amazon Simple Storage - Service (S3) or any other object storage that supports the S3 interface. These third party - object storages can be integrated with &PRODUCT; by writing plugin software that uses the - object storage plugin capability introduced in &PRODUCT; 4.2. Several new pluggable - service interfaces are available so that different storage providers can develop - vendor-specific plugins based on the well-defined contracts that can be seemlessly managed - by &PRODUCT;. -
    -
    - Zone-Wide Primary Storage - (Supported on KVM and VMware) - In &PRODUCT; 4.2, you can provision primary storage on a per-zone basis. Data volumes - in the primary storage can be attached to any VM on any host in the zone. - In previous &PRODUCT; versions, each cluster had its own primary storage. Data in the - primary storage was directly available only to VMs within that cluster. If a VM in a - different cluster needed some of the data, it must be copied from one cluster to another, - using the zone's secondary storage as an intermediate step. This operation was - unnecessarily time-consuming. -
    -
    - VMware Datacenter Now Visible As a &PRODUCT; Zone - In order to support zone-wide functions for VMware, changes have been made so that - &PRODUCT; is now aware of VMware Datacenters and can map each Datacenter to a &PRODUCT; - zone. Previously, &PRODUCT; was only aware of VMware Clusters, a smaller organizational - unit than Datacenters. This implies that a single &PRODUCT; zone could possibly contain - clusters from different VMware Datacenters. In order for zone-wide functions, such as - zone-wide primary storage, to work for VMware hosts, &PRODUCT; has to make sure that a - zone contains only a single VMware Datacenter. Therefore, when you are creating a new - &PRODUCT; zone, you will now be able to select a VMware Datacenter for the zone. If you - are provisioning multiple VMware Datacenters, each one will be set up as a single zone in - &PRODUCT;. - - If you are upgrading from a previous &PRODUCT; version, and your existing deployment - contains a zone with clusters from multiple VMware Datacenters, that zone will not be - forcibly migrated to the new model. It will continue to function as before. However, any - new zone-wide operations, such as zone-wide primary storage, will not be available in - that zone. - - -
    -
    -
    - Third-Party UI Plugin Framework - Using the new third-party plugin framework, you can write and install extensions to - &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the - Citrix-provided features. - The basic procedure for adding a UI plugin is explained in the Developer Guide. In - summary, the plugin developer creates the plugin code itself (in Javascript), a thumbnail - image, the plugin listing, and a CSS file. The &PRODUCT; administrator adds the folder - containing the plugin code under the &PRODUCT; PLUGINS folder and adds the plugin name to a - configuration file (plugins.js). - The next time the user refreshes the UI in the browser, the plugin will appear under the - Plugins button in the left navigation bar. - - - - - - plugin4.jpg: The plugin appears in the UI - - -
    -
    - Networking Enhancements - The following new features provide additional networking functionality in &PRODUCT; - 4.2. -
    - IPv6 (Technical Preview) - &PRODUCT; 4.2 introduces initial support for IPv6. This feature is provided as a - technical preview only. Full support is planned for a future release. -
    -
    - Portable IPs - Portable IPs in &PRODUCT; are elastic IPs that can be transferred across - geographically separated zones. As an administrator, you can provision a pool of portable - IPs at region level and are available for user consumption. The users can acquire portable - IPs if admin has provisioned portable public IPs at the region level they are part of. - These IPs can be used for any service within an advanced zone. You can also use portable - IPs for EIP service in Basic zones. Additionally, a portable IP can be transferred from - one network to another network. -
    -
    - N-Tier Applications - In &PRODUCT; 3.0.6, a functionality was added to allow users to create a multi-tier - application connected to a single instance of a Virtual Router that supports inter-VLAN - routing. Such a multi-tier application is called a virtual private cloud (VPC). Users were - also able to connect their multi-tier applications to a private Gateway or a Site-to-Site - VPN tunnel and route certain traffic to those gateways. For &PRODUCT; 4.2, additional - features are implemented to enhance VPC applications. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Support for KVM - VPC is now supported on KVM hypervisors. +
    + Networking Enhancements + The following new features provide additional networking functionality in &PRODUCT; + 4.2. +
    + IPv6 (Technical Preview) + &PRODUCT; 4.2 introduces initial support for IPv6. This feature is provided as a + technical preview only. Full support is planned for a future release.
    -
    - Support for Simultaneously Deploying a VM on VPC and Multiple Shared - Networks - Support for the ability to simultaneously deploy a VM on a VPC tier and one or more - Shared networks is supported. +
    + Portable IPs + Portable IPs in &PRODUCT; are elastic IPs that can be transferred across + geographically separated zones. As an administrator, you can provision a pool of + portable IPs at region level and are available for user consumption. The users can + acquire portable IPs if admin has provisioned portable public IPs at the region level + they are part of. These IPs can be used for any service within an advanced zone. You can + also use portable IPs for EIP service in Basic zones. Additionally, a portable IP can be + transferred from one network to another network.
    -
    - Load Balancing Support for VPC - In a VPC, you can configure two types of load balancing—external LB and - internal LB. External LB is nothing but a LB rule created to redirect the traffic - received at a public IP of the VPC virtual router. The traffic is load balanced within a - tier based on your configuration. Citrix NetScaler and VPC virtual router are supported - for external LB. When you use internal LB service, traffic received at a tier is load - balanced across different VMs within that tier. For example, traffic reached at Web tier - is redirected to another VM in that tier. External load balancing devices are not - supported for internal LB. The service is provided by a internal LB VM configured on the - target tier. -
    - Load Balancing Within a Tier (External LB) - A &PRODUCT; user or administrator may create load balancing rules that balance - traffic received at a public IP to one or more VMs that belong to a network tier that - provides load balancing service in a VPC. A user creates a rule, specifies an - algorithm, and assigns the rule to a set of VMs within a tier. +
    + N-Tier Applications + In &PRODUCT; 3.0.6, a functionality was added to allow users to create a multi-tier + application connected to a single instance of a Virtual Router that supports inter-VLAN + routing. Such a multi-tier application is called a virtual private cloud (VPC). Users + were also able to connect their multi-tier applications to a private Gateway or a + Site-to-Site VPN tunnel and route certain traffic to those gateways. For &PRODUCT; 4.2, + additional features are implemented to enhance VPC applications. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Support for KVM + VPC is now supported on KVM hypervisors.
    -
    - Load Balancing Across Tiers - &PRODUCT; supports sharing workload across different tiers within your VPC. Assume - that multiple tiers are set up in your environment, such as Web tier and Application - tier. Traffic to each tier is balanced on the VPC virtual router on the public side. - If you want the traffic coming from the Web tier to the Application tier to be - balanced, use the internal load balancing feature offered by &PRODUCT;. +
    + Support for Simultaneously Deploying a VM on VPC and Multiple Shared + Networks + Support for the ability to simultaneously deploy a VM on a VPC tier and one or + more Shared networks is supported.
    -
    - Netscaler Support for VPC - Citrix NetScaler is supported for external LB. Certified version for this feature - is NetScaler 10.0 Build 74.4006.e. +
    + Load Balancing Support for VPC + In a VPC, you can configure two types of load balancing—external LB and + internal LB. External LB is nothing but a LB rule created to redirect the traffic + received at a public IP of the VPC virtual router. The traffic is load balanced within + a tier based on your configuration. Citrix NetScaler and VPC virtual router are + supported for external LB. When you use internal LB service, traffic received at a + tier is load balanced across different VMs within that tier. For example, traffic + reached at Web tier is redirected to another VM in that tier. External load balancing + devices are not supported for internal LB. The service is provided by a internal LB VM + configured on the target tier. +
    + Load Balancing Within a Tier (External LB) + A &PRODUCT; user or administrator may create load balancing rules that balance + traffic received at a public IP to one or more VMs that belong to a network tier + that provides load balancing service in a VPC. A user creates a rule, specifies an + algorithm, and assigns the rule to a set of VMs within a tier. +
    +
    + Load Balancing Across Tiers + &PRODUCT; supports sharing workload across different tiers within your VPC. + Assume that multiple tiers are set up in your environment, such as Web tier and + Application tier. Traffic to each tier is balanced on the VPC virtual router on the + public side. If you want the traffic coming from the Web tier to the Application + tier to be balanced, use the internal load balancing feature offered by + &PRODUCT;. +
    +
    + Netscaler Support for VPC + Citrix NetScaler is supported for external LB. Certified version for this + feature is NetScaler 10.0 Build 74.4006.e. +
    +
    +
    + Enhanced Access Control List + Network Access Control List (ACL) on the VPC virtual router is enhanced. The + network ACLs can be created for the tiers only if the NetworkACL service is supported. + In &PRODUCT; terminology, Network ACL is a group of Network ACL items. Network ACL + items are nothing but numbered rules that are evaluated in order, starting with the + lowest numbered rule. These rules determine whether traffic is allowed in or out of + any tier associated with the network ACL. You need to add the Network ACL items to the + Network ACL, then associate the Network ACL with a tier. Network ACL is associated + with a VPC and can be assigned to multiple VPC tiers within a VPC. A Tier is + associated with a Network ACL at all the times. Each tier can be associated with only + one ACL. + The default Network ACL is used when no ACL is associated. Default behavior is all + incoming traffic to guest networks is blocked and all outgoing traffic from guest + networks is allowed. Default network ACL cannot be removed or modified. +
    + ACL on Private Gateway + The traffic on the VPC private gateway is controlled by creating both ingress + and egress network ACL rules. The ACLs contains both allow and deny rules. As per + the rule, all the ingress traffic to the private gateway interface and all the + egress traffic out from the private gateway interface are blocked. You can change + this default behaviour while creating a private gateway. +
    +
    + Allow ACL on All Level 4 Protocols + In addition to the existing protocol support for ICMP, TCP, UDP, support for All + Level 4 protocols is added. The protocol numbers from 0 to 255 are supported. +
    +
    + Support for ACL Deny Rules + In addition to the existing support for ACL Allow rules, support for ACL Deny + rules has been added in &PRODUCT; 4.2. As part of this, two operations are + supported: Number and Action. You can configure a rule, allow or deny, by using + action. Use Number to add a rule number. +
    +
    +
    + Deploying VMs to a VPC Tier and Shared Networks + &PRODUCT; allows you to deploy VMs on a VPC tier and one or more shared networks. + With this feature, the VMs deployed in a multi-tier application can receive services + offered by a service provider over the shared network. One example of such a service + is monitoring service. +
    +
    + Adding a Private Gateway to a VPC + A private gateway can be added by the root admin only. The VPC private network has + 1:1 relationship with the NIC of the physical network. You can configure multiple + private gateways to a single VPC. No gateways with duplicated VLAN and IP are allowed + in the same data center. +
    + Source NAT on Private Gateway + You might want to deploy multiple VPCs with the same super CIDR and guest tier + CIDR. Therefore, multiple guest VMs from different VPCs can have the same IPs to + reach a enterprise data center through the private gateway. In such cases, a NAT + service need to be configured on the private gateway. If Source NAT is enabled, the + guest VMs in VPC reaches the enterprise network via private gateway IP address by + using the NAT service. + The Source NAT service on a private gateway can be enabled while adding the + private gateway. On deletion of a private gateway, source NAT rules specific to the + private gateway are deleted. +
    +
    + VPN Gateways + Support up to 8 VPN Gateways is added. +
    +
    + Creating a Static Route + &PRODUCT; enables you to specify routing for the VPN connection you create. You + can enter one or CIDR addresses to indicate which traffic is to be routed back to + the gateway. +
    +
    + Blacklisting Routes + &PRODUCT; enables you to block a list of routes so that they are not assigned to + any of the VPC private gateways. Specify the list of routes that you want to + blacklist in the blacklisted.routes global parameter. Note that the + parameter update affects only new static route creations. If you block an existing + static route, it remains intact and continue functioning. You cannot add a static + route if the route is blacklisted for the zone. +
    -
    - Enhanced Access Control List - Network Access Control List (ACL) on the VPC virtual router is enhanced. The network - ACLs can be created for the tiers only if the NetworkACL service is supported. In - &PRODUCT; terminology, Network ACL is a group of Network ACL items. Network ACL items - are nothing but numbered rules that are evaluated in order, starting with the lowest - numbered rule. These rules determine whether traffic is allowed in or out of any tier - associated with the network ACL. You need to add the Network ACL items to the Network - ACL, then associate the Network ACL with a tier. Network ACL is associated with a VPC - and can be assigned to multiple VPC tiers within a VPC. A Tier is associated with a - Network ACL at all the times. Each tier can be associated with only one ACL. - The default Network ACL is used when no ACL is associated. Default behavior is all - incoming traffic to guest networks is blocked and all outgoing traffic from guest - networks is allowed. Default network ACL cannot be removed or modified. -
    - ACL on Private Gateway - The traffic on the VPC private gateway is controlled by creating both ingress and - egress network ACL rules. The ACLs contains both allow and deny rules. As per the - rule, all the ingress traffic to the private gateway interface and all the egress - traffic out from the private gateway interface are blocked. You can change this - default behaviour while creating a private gateway. -
    -
    - Allow ACL on All Level 4 Protocols - In addition to the existing protocol support for ICMP, TCP, UDP, support for All - Level 4 protocols is added. The protocol numbers from 0 to 255 are supported. -
    -
    - Support for ACL Deny Rules - In addition to the existing support for ACL Allow rules, support for ACL Deny - rules has been added in &PRODUCT; 4.2. As part of this, two operations are supported: - Number and Action. You can configure a rule, allow or deny, by using action. Use - Number to add a rule number. -
    +
    + Assigning VLANs to Isolated Networks + &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. + You can assign a VLAN ID when a network is created, just the way it's done for Shared + networks. + The former behaviour also is supported — VLAN is randomly allocated to a + network from the VNET range of the physical network when the network turns to + Implemented state. The VLAN is released back to the VNET pool when the network shuts + down as a part of the Network Garbage Collection. The VLAN can be re-used either by the + same network when it is implemented again, or by any other network. On each subsequent + implementation of a network, a new VLAN can be assigned. + + You cannot change a VLAN once it's assigned to the network. The VLAN remains with + the network for its entire life cycle. +
    -
    - Deploying VMs to a VPC Tier and Shared Networks - &PRODUCT; allows you to deploy VMs on a VPC tier and one or more shared networks. - With this feature, the VMs deployed in a multi-tier application can receive services - offered by a service provider over the shared network. One example of such a service is - monitoring service. +
    + Persistent Networks + &PRODUCT; 4.2 supports Persistent Networks. The network that you can provision + without having to deploy any VMs on it is called a Persistent Network. A Persistent + Network can be part of a VPC or a non-VPC environment. With the addition of this + feature, you will have the ability to create a network in &PRODUCT; in which physical + devices can be deployed without having to run any VMs. Additionally, you can deploy + physical devices on that network. Another advantages is that you can create a VPC with a + tier that consists only physical devices. For example, you might create a VPC for a + three-tier application, deploy VMs for Web and Application tier, and use physical + machines for the Database tier. Another use case is that if you are providing services + by using physical hardware, you can define the network as persistent and therefore even + if all its VMs are destroyed the services will not be discontinued.
    -
    - Adding a Private Gateway to a VPC - A private gateway can be added by the root admin only. The VPC private network has - 1:1 relationship with the NIC of the physical network. You can configure multiple - private gateways to a single VPC. No gateways with duplicated VLAN and IP are allowed in - the same data center. -
    - Source NAT on Private Gateway - You might want to deploy multiple VPCs with the same super CIDR and guest tier - CIDR. Therefore, multiple guest VMs from different VPCs can have the same IPs to reach - a enterprise data center through the private gateway. In such cases, a NAT service - need to be configured on the private gateway. If Source NAT is enabled, the guest VMs - in VPC reaches the enterprise network via private gateway IP address by using the NAT - service. - The Source NAT service on a private gateway can be enabled while adding the - private gateway. On deletion of a private gateway, source NAT rules specific to the - private gateway are deleted. -
    -
    - VPN Gateways - Support up to 8 VPN Gateways is added. -
    -
    - Creating a Static Route - &PRODUCT; enables you to specify routing for the VPN connection you create. You - can enter one or CIDR addresses to indicate which traffic is to be routed back to the - gateway. -
    -
    - Blacklisting Routes - &PRODUCT; enables you to block a list of routes so that they are not assigned to - any of the VPC private gateways. Specify the list of routes that you want to blacklist - in the blacklisted.routes global parameter. Note that the parameter - update affects only new static route creations. If you block an existing static route, - it remains intact and continue functioning. You cannot add a static route if the route - is blacklisted for the zone. -
    +
    + Cisco VNMC Support + Cisco Virtual Network Management Center (VNMC) provides centralized multi-device and + policy management for Cisco Network Virtual Services. When Cisco VNMC is integrated with + ASA 1000v Cloud Firewall and Cisco Nexus 1000v dvSwitch in &PRODUCT; you will be able + to: + + + Configure Cisco ASA 1000v Firewalls + + + Create and apply security profiles that contain ACL policy sets for both ingress + and egress traffic, and NAT policy sets + + + &PRODUCT; supports Cisco VNMC on Cisco Nexus 1000v dvSwich-enabled VMware + hypervisors. +
    +
    + VMware vNetwork Distributed vSwitch + &PRODUCT; supports VMware vSphere Distributed Switch (VDS) for virtual network + configuration in a VMware vSphere environment. Each vCenter server instance can support + up to 128 VDSs and each VDS can manage up to 500 VMware hosts. &PRODUCT; supports + configuring virtual networks in a deployment with a mix of Virtual Distributed Switch, + Standard Virtual Switch and Nexus 1000v Virtual Switch. +
    +
    + IP Reservation in Isolated Guest Networks + In Isolated guest networks in &PRODUCT; 4.2, a part of the guest IP address space + can be reserved for non-&PRODUCT; VMs or physical servers. To do so, you configure a + range of Reserved IP addresses by specifying the CIDR when a guest network is in + Implemented state. The advantage of having this feature is that if your customers wish + to have non-&PRODUCT; controlled VMs or physical servers on the same network, they can + use a part of the IP address space that is primarily provided to the guest network. When + IP reservation is configured, the administrator can add additional VMs or physical + servers that are not part of &PRODUCT; to the same network and assign them the Reserved + IP addresses. &PRODUCT; guest VMs cannot acquire IPs from the Reserved IP Range. +
    +
    + Dedicated Resources: Public IP Addresses and VLANs Per Account + &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs + exclusively for an account. During zone creation, you can continue to define a set of + VLANs and multiple public IP ranges. This feature extends the functionality to enable + you to dedicate a fixed set of VLANs and guest IP addresses for a tenant. + This feature provides you the following capabilities: + + + Reserve a VLAN range and public IP address range from an Advanced zone and + assign it to an account + + + Disassociate a VLAN and public IP address range from an account + + + + Ensure that you check whether the required range is available and conforms to + account limits. The maximum IPs per account limit cannot be superseded. + +
    +
    + Enhanced Juniper SRX Support for Egress Firewall Rules + Egress firewall rules were previously supported on virtual routers, and now they are + also supported on Juniper SRX external networking devices. + Egress traffic originates from a private network to a public network, such as the + Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed + from a guest network to the Internet. However, you can control the egress traffic in an + Advanced zone by creating egress firewall rules. When an egress firewall rule is + applied, the traffic specific to the rule is allowed and the remaining traffic is + blocked. When all the firewall rules are removed the default policy, Block, is + applied. + + Egress firewall rules are not supported on Shared networks. They are supported + only on Isolated guest networks. + +
    +
    + Configuring the Default Egress Policy + The default egress policy for Isolated guest network can be configured by using + Network offering. Use the create network offering option to determine whether the + default policy should be block or allow all the traffic to the public network from a + guest network. Use this network offering to create the network. If no policy is + specified, by default all the traffic is allowed from the guest network that you create + by using this network offering. + You have two options: Allow and Deny. + If you select Allow for a network offering, by default egress traffic is allowed. + However, when an egress rule is configured for a guest network, rules are applied to + block the specified traffic and rest are allowed. If no egress rules are configured for + the network, egress traffic is accepted. If you select Deny for a network offering, by + default egress traffic for the guest network is blocked. However, when an egress rules + is configured for a guest network, rules are applied to allow the specified traffic. + While implementing a guest network, &PRODUCT; adds the firewall egress rule specific to + the default egress policy for the guest network. + This feature is supported only on virtual router and Juniper SRX. +
    +
    + Non-Contiguous VLAN Ranges + &PRODUCT; provides you with the flexibility to add non contiguous VLAN ranges to + your network. The administrator can either update an existing VLAN range or add multiple + non contiguous VLAN ranges while creating a zone. You can also use the + UpdatephysicalNetwork API to extend the VLAN range. +
    +
    + Isolation in Advanced Zone Using Private VLAN + Isolation of guest traffic in shared networks can be achieved by using Private VLANs + (PVLAN). PVLANs provide Layer 2 isolation between ports within the same VLAN. In a + PVLAN-enabled shared network, a user VM cannot reach other user VM though they can reach + the DHCP server and gateway, this would in turn allow users to control traffic within a + network and help them deploy multiple applications without communication between + application as well as prevent communication with other users’ VMs. + + + Isolate VMs in a shared networks by using Private VLANs. + + + Supported on KVM, XenServer, and VMware hypervisors. + + + PVLAN-enabled shared network can be a part of multiple networks of a guest VM. + + + + For further reading: + + + Understanding Private VLANs + + + Cisco Systems' Private VLANs: + Scalable Security in a Multi-Client Environment + + + Private VLAN (PVLAN) on vNetwork Distributed + Switch - Concept Overview (1010691) + + +
    +
    + Configuring Multiple IP Addresses on a Single NIC + (Supported on XenServer, KVM, and VMware hypervisors) + &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 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. +
    +
    + Adding Multiple IP Ranges + (Supported on KVM, xenServer, and VMware hypervisors) + &PRODUCT; 4.2 provides you with the flexibility to add guest IP ranges from + different subnets in Basic zones and security groups-enabled Advanced zones. For + security groups-enabled Advanced zones, it implies multiple subnets can be added to the + same VLAN. With the addition of this feature, you will be able to add IP address ranges + from the same subnet or from a different one when IP address are exhausted. This would + in turn allows you to employ higher number of subnets and thus reduce the address + management overhead. + Ensure that you manually configure the gateway of the new subnet before adding the + IP range. Note that &PRODUCT; supports only one gateway for a subnet; overlapping + subnets are not currently supported. + You can also delete IP ranges. This operation fails if an IP from the remove range + is in use. If the remove range contains the IP address on which the DHCP server is + running, &PRODUCT; acquires a new IP from the same subnet. If no IP is available in the + subnet, the remove operation fails. + + The feature can only be implemented on IPv4 addresses. + +
    +
    + Support for Multiple Networks in VMs + (Supported on XenServer, VMware and KVM hypervisors) + &PRODUCT; 4.2 provides you the ability to add and remove multiple networks to a VM. + You can remove a network from a VM and add a new network. You can also change the + default network of a VM. With this functionality, hybrid or traditional server loads can + be accommodated with ease. + For adding or removing a NIC to work on VMware, ensure that vm-tools are running on + guest VMs. +
    +
    + Global Server Load Balancing + &PRODUCT; 4.2 supports Global Server Load Balancing (GSLB) functionalities to + provide business continuity by load balancing traffic to an instance on active zones + only in case of zone failures . &PRODUCT; achieve this by extending its functionality of + integrating with NetScaler Application Delivery Controller (ADC), which also provides + various GSLB capabilities, such as disaster recovery and load balancing. The DNS + redirection technique is used to achieve GSLB in &PRODUCT;. In order to support this + functionality, region level services and service provider are introduced. A new service + 'GSLB' is introduced as a region level service. The GSLB service provider is introduced + that will provider the GSLB service. Currently, NetScaler is the supported GSLB provider + in &PRODUCT;. GSLB functionality works in an Active-Active data center environment. + +
    +
    + Enhanced Load Balancing Services Using External Provider on Shared VLANs + Network services like Firewall, Load Balancing, and NAT are now supported in shared + networks created in an advanced zone. In effect, the following network services shall be + made available to a VM in a shared network: Source NAT, Static NAT, Port Forwarding, + Firewall and Load balancing. Subset of these service can be chosen while creating a + network offering for shared networks. Services available in a shared network is defined + by the network offering and the service chosen in the network offering. For example, if + network offering for a shared network has source NAT service enabled, a public IP shall + be provisioned and source NAT is configured on the firewall device to provide public + access to the VMs on the shared network. Static NAT, Port Forwarding, Load Balancing, + and Firewall services shall be available only on the acquired public IPs associated with + a shared network. + Additionally, Netscaler and Juniper SRX firewall device can be configured inline or + side-by-side mode. +
    +
    + Health Checks for Load Balanced Instances + + This feature is supported only on NetScaler version 10.0 and beyond. + + (NetScaler load balancer only) A load balancer rule distributes requests among a + pool of services (a service in this context means an application running on a virtual + machine). When creating a load balancer rule, you can specify a health check which will + ensure that the rule forwards requests only to services that are healthy (running and + available). When a health check is in effect, the load balancer will stop forwarding + requests to any resources that it has found to be unhealthy. If the resource later + becomes available again, the periodic health check (periodicity is configurable) will + discover it and the resource will once again be made available to the load + balancer. + To configure how often the health check is performed by default, use the global + configuration setting healthcheck.update.interval. This default applies to all the + health check policies in the cloud. You can override this value for an individual health + check policy.
    -
    - Assigning VLANs to Isolated Networks - &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. - You can assign a VLAN ID when a network is created, just the way it's done for Shared - networks. - The former behaviour also is supported — VLAN is randomly allocated to a network - from the VNET range of the physical network when the network turns to Implemented state. - The VLAN is released back to the VNET pool when the network shuts down as a part of the - Network Garbage Collection. The VLAN can be re-used either by the same network when it is - implemented again, or by any other network. On each subsequent implementation of a - network, a new VLAN can be assigned. - - You cannot change a VLAN once it's assigned to the network. The VLAN remains with - the network for its entire life cycle. - +
    + Host and Virtual Machine Enhancements + The following new features expand the ways you can use hosts and virtual + machines. +
    + VMware DRS Support + The VMware vSphere Distributed Resources Scheduler (DRS) is supported. +
    +
    + Windows 8 and Windows Server 2012 as VM Guest OS + (Supported on XenServer, VMware, and KVM) + Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual + machines. The OS would be made available the same as any other, by uploading an ISO or a + template. The instructions for uploading ISOs and templates are given in the + Administrator's Guide. + + Limitation: When used with VMware hosts, this + feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch + 4. + + +
    +
    + Change Account Ownership of Virtual Machines + A root administrator can now change the ownership of any virtual machine from one + account to any other account. A domain or sub-domain administrator can do the same for + VMs within the domain from one account to any other account in the domain. +
    +
    + Private Pod, Cluster, or Host + Dedicating pod, cluster or host to a specific domain/account means that the + domain/account will have sole access to the dedicated pod, cluster or hosts such that + scalability, security and manageability within a domain/account can be improved. The + resources which belong to that tenant will be placed into that dedicated pod, cluster or + host. +
    +
    + Resizing Volumes + &PRODUCT; provides the ability to resize data disks; &PRODUCT; controls volume size + by using disk offerings. This provides &PRODUCT; administrators with the flexibility to + choose how much space they want to make available to the end users. Volumes within the + disk offerings with the same storage tag can be resized. For example, if you only want + to offer 10, 50, and 100 GB offerings, the allowed resize should stay within those + limits. That implies if you define a 10 GB, a 50 GB and a 100 GB disk offerings, a user + can upgrade from 10 GB to 50 GB, or 50 GB to 100 GB. If you create a custom-sized disk + offering, then you have the option to resize the volume by specifying a new, larger + size. Additionally, using the resizeVolume API, a data volume can be moved from a static + disk offering to a custom disk offering with the size specified. This functionality + allows those who might be billing by certain volume sizes or disk offerings to stick to + that model, while providing the flexibility to migrate to whatever custom size + necessary. This feature is supported on KVM, XenServer, and VMware hosts. However, + shrinking volumes is not supported on VMware hosts +
    +
    + VMware Volume Snapshot Improved Performance + When you take a snapshot of a data volume on VMware, &PRODUCT; will now use a more + efficient storage technique to improve performance. + Previously, every snapshot was immediately exported from vCenter to a mounted NFS + share and packaged into an OVA file format. This operation consumed time and resources. + Starting from 4.2, the original file formats (e.g., VMDK) provided by vCenter will be + retained. An OVA file will only be created as needed, on demand. + The new process applies only to newly created snapshots after upgrade to &PRODUCT; + 4.2. Snapshots that have already been taken and stored in OVA format will continue to + exist in that format, and will continue to work as expected. +
    +
    + Storage Migration: XenMotion and vMotion + (Supported on XenServer and VMware) + Storage migration allows VMs to be moved from one host to another, where the VMs are + not located on storage shared between the two hosts. It provides the option to live + migrate a VM’s disks along with the VM itself. It is now possible to migrate a VM from + one XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks + are on local storage, or even to migrate a VM’s disks from one storage repository to + another, all while the VM is running. +
    +
    + Configuring Usage of Linked Clones on VMware + (For ESX hypervisor in conjunction with vCenter) + In &PRODUCT; 4.2, the creation of VMs as full clones is allowed. In previous + versions, only linked clones were possible. + For a full description of clone types, refer to VMware documentation. In summary: A + full clone is a copy of an existing virtual machine which, once created, does not depend + in any way on the original virtual machine. A linked clone is also a copy of an existing + virtual machine, but it has ongoing dependency on the original. A linked clone shares + the virtual disk of the original VM, and retains access to all files that were present + at the time the clone was created. + A new global configuration setting has been added, vmware.create.full.clone. When + the administrator sets this to true, end users can create guest VMs only as full clones. + The default value is true for new installations. For customers upgrading from a previous + version of &PRODUCT;, the default value of vmware.create.full.clone is false. +
    +
    + VM Deployment Rules + Rules can be set up to ensure that particular VMs are not placed on the same + physical host. These "anti-affinity rules" can increase the reliability of applications + by ensuring that the failure of a single host can not take down the entire group of VMs + supporting a given application. See Affinity Groups in the &PRODUCT; 4.2 Administration + Guide. +
    +
    + CPU and Memory Scaling for Running VMs + (Supported on VMware and XenServer) + You can now change the CPU and RAM values for a running virtual machine. In previous + versions of &PRODUCT;, this could only be done on a stopped VM. + It is not always possible to accurately predict the CPU and RAM requirements when + you first deploy a VM. You might need to increase or decrease these resources at any + time during the life of a VM. With the new ability to dynamically modify CPU and RAM + levels, you can change these resources for a running VM without incurring any + downtime. + Dynamic CPU and RAM scaling can be used in the following cases: + + + New VMs that are created after the installation of &PRODUCT; 4.2. If you are + upgrading from a previous version of &PRODUCT;, your existing VMs created with + previous versions will not have the dynamic scaling capability. + + + User VMs on hosts running VMware and XenServer. + + + System VMs on VMware. + + + VM Tools or XenServer Tools must be installed on the virtual machine. + + + The new requested CPU and RAM values must be within the constraints allowed by + the hypervisor and the VM operating system. + + + To configure this feature, use the following new global configuration + variables: + + + enable.dynamic.scale.vm: Set to True to enable the feature. By default, the + feature is turned off. + + + scale.retry: How many times to attempt the scaling operation. Default = + 2. + + +
    +
    + CPU and Memory Over-Provisioning + (Supported for XenServer, KVM, and VMware) + In &PRODUCT; 4.2, CPU and memory (RAM) over-provisioning factors can be set for each + cluster to change the number of VMs that can run on each host in the cluster. This helps + optimize the use of resources. By increasing the over-provisioning ratio, more resource + capacity will be used. If the ratio is set to 1, no over-provisioning is done. + In previous releases, &PRODUCT; did not perform memory over-provisioning. It + performed CPU over-provisioning based on a ratio configured by the administrator in the + global configuration setting cpu.overprovisioning.factor. Starting in 4.2, the + administrator can specify a memory over-provisioning ratio, and can specify both CPU and + memory over-provisioning ratios on a per-cluster basis, rather than only on a global + basis. + In any given cloud, the optimum number of VMs for each host is affected by such + things as the hypervisor, storage, and hardware configuration. These may be different + for each cluster in the same cloud. A single global over-provisioning setting could not + provide the best utilization for all the different clusters in the cloud. It had to be + set for the lowest common denominator. The new per-cluster setting provides a finer + granularity for better utilization of resources, no matter where the &PRODUCT; placement + algorithm decides to place a VM. +
    +
    + Kickstart Installation for Bare Metal Provisioning + &PRODUCT; 4.2 supports the kick start installation method for RPM-based Linux + operating systems on baremetal hosts in basic zones. Users can provision a baremetal + host managed by &PRODUCT; as long as they have the kick start file and corresponding OS + installation ISO ready. + Tested on CentOS 5.5, CentOS 6.2, CentOS 6.3, Ubuntu 12.04. + For more information, see the Baremetal Installation Guide. +
    +
    + Enhanced Bare Metal Support on Cisco UCS + You can now more easily provision new Cisco UCS server blades into &PRODUCT; for use + as bare metal hosts. The goal is to enable easy expansion of the cloud by leveraging the + programmability of the UCS converged infrastructure and &PRODUCT;’s knowledge of the + cloud architecture and ability to orchestrate. With this new feature, &PRODUCT; can + automatically understand the UCS environment, server profiles, etc. to make it easy to + deploy a bare metal OS on a Cisco UCS. +
    +
    + Changing a VM's Base Image + Every VM is created from a base image, which is a template or ISO which has been + created and stored in &PRODUCT;. Both cloud administrators and end users can create and + modify templates, ISOs, and VMs. + In &PRODUCT; 4.2, there is a new way to modify an existing VM. You can change an + existing VM from one base image to another. For example, suppose there is a template + based on a particular operating system, and the OS vendor releases a software patch. The + administrator or user naturally wants to apply the patch and then make sure existing VMs + start using it. Whether a software update is involved or not, it's also possible to + simply switch a VM from its current template to any other desired template. +
    +
    + Reset VM on Reboot + In &PRODUCT; 4.2, you can specify that you want to discard the root disk and create + a new one whenever a given VM is rebooted. This is useful for secure environments that + need a fresh start on every boot and for desktops that should not retain state. The IP + address of the VM will not change due to this operation. +
    +
    + Virtual Machine Snapshots for VMware + (VMware hosts only) In addition to the existing &PRODUCT; ability to snapshot + individual VM volumes, you can now take a VM snapshot to preserve all the VM's data + volumes as well as (optionally) its CPU/memory state. This is useful for quick restore + of a VM. For example, you can snapshot a VM, then make changes such as software + upgrades. If anything goes wrong, simply restore the VM to its previous state using the + previously saved VM snapshot. + The snapshot is created using the VMware native snapshot facility. The VM snapshot + includes not only the data volumes, but optionally also whether the VM is running or + turned off (CPU state) and the memory contents. The snapshot is stored in &PRODUCT;'s + primary storage. + VM snapshots can have a parent/child relationship. Each successive snapshot of the + same VM is the child of the snapshot that came before it. Each time you take an + additional snapshot of the same VM, it saves only the differences between the current + state of the VM and the state stored in the most recent previous snapshot. The previous + snapshot becomes a parent, and the new snapshot is its child. It is possible to create a + long chain of these parent/child snapshots, which amount to a "redo" record leading from + the current state of the VM back to the original. +
    +
    + Increased Userdata Size When Deploying a VM + You can now specify up to 32KB of userdata when deploying a virtual machine through + the &PRODUCT; UI or the deployVirtualMachine API call. +
    +
    + Set VMware Cluster Size Limit Depending on VMware Version + The maximum number of hosts in a vSphere cluster is determined by the VMware + hypervisor software. For VMware versions 4.2, 4.1, 5.0, and 5.1, the limit is 32 + hosts. + For &PRODUCT; 4.2, the global configuration setting vmware.percluster.host.max has + been removed. The maximum number of hosts in a VMware cluster is now determined by the + underlying hypervisor software. + + Best Practice: It is advisable for VMware clusters in &PRODUCT; to be smaller than + the VMware hypervisor's maximum size. A cluster size of up to 8 hosts has been found + optimal for most real-world situations. + +
    +
    + Limiting Resource Usage + Previously in &PRODUCT;, resource usage limit was imposed based on the resource + count, that is, restrict a user or domain on the basis of the number of VMs, volumes, or + snapshots used. In &PRODUCT; 4.2, a new set of resource types has been added to the + existing pool of resources (VMs, Volumes, and Snapshots) to support the customization + model—need-basis usage, such as large VM or small VM. The new resource types are + now broadly classified as CPU, RAM, Primary storage, and Secondary storage. &PRODUCT; + 4.2 allows the root administrator to impose resource usage limit by the following + resource types for Domain, Project and Accounts. + + + CPUs + + + Memory (RAM) + + + Primary Storage (Volumes) + + + Secondary Storage (Snapshots, Templates, ISOs) + + +
    -
    - Persistent Networks - &PRODUCT; 4.2 supports Persistent Networks. The network that you can provision without - having to deploy any VMs on it is called a Persistent Network. A Persistent Network can be - part of a VPC or a non-VPC environment. With the addition of this feature, you will have - the ability to create a network in &PRODUCT; in which physical devices can be deployed - without having to run any VMs. Additionally, you can deploy physical devices on that - network. Another advantages is that you can create a VPC with a tier that consists only - physical devices. For example, you might create a VPC for a three-tier application, deploy - VMs for Web and Application tier, and use physical machines for the Database tier. Another - use case is that if you are providing services by using physical hardware, you can define - the network as persistent and therefore even if all its VMs are destroyed the services - will not be discontinued. -
    -
    - Cisco VNMC Support - Cisco Virtual Network Management Center (VNMC) provides centralized multi-device and - policy management for Cisco Network Virtual Services. When Cisco VNMC is integrated with - ASA 1000v Cloud Firewall and Cisco Nexus 1000v dvSwitch in &PRODUCT; you will be able to: - - - Configure Cisco ASA 1000v Firewalls - - - Create and apply security profiles that contain ACL policy sets for both ingress - and egress traffic, and NAT policy sets - - - &PRODUCT; supports Cisco VNMC on Cisco Nexus 1000v dvSwich-enabled VMware - hypervisors. -
    -
    - VMware vNetwork Distributed vSwitch - &PRODUCT; supports VMware vSphere Distributed Switch (VDS) for virtual network - configuration in a VMware vSphere environment. Each vCenter server instance can support up - to 128 VDSs and each VDS can manage up to 500 VMware hosts. &PRODUCT; supports configuring - virtual networks in a deployment with a mix of Virtual Distributed Switch, Standard - Virtual Switch and Nexus 1000v Virtual Switch. -
    -
    - IP Reservation in Isolated Guest Networks - In Isolated guest networks in &PRODUCT; 4.2, a part of the guest IP address space can - be reserved for non-&PRODUCT; VMs or physical servers. To do so, you configure a range of - Reserved IP addresses by specifying the CIDR when a guest network is in Implemented state. - The advantage of having this feature is that if your customers wish to have non-&PRODUCT; - controlled VMs or physical servers on the same network, they can use a part of the IP - address space that is primarily provided to the guest network. When IP reservation is - configured, the administrator can add additional VMs or physical servers that are not part - of &PRODUCT; to the same network and assign them the Reserved IP addresses. &PRODUCT; - guest VMs cannot acquire IPs from the Reserved IP Range. -
    -
    - Dedicated Resources: Public IP Addresses and VLANs Per Account - &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs - exclusively for an account. During zone creation, you can continue to define a set of - VLANs and multiple public IP ranges. This feature extends the functionality to enable you - to dedicate a fixed set of VLANs and guest IP addresses for a tenant. - This feature provides you the following capabilities: - - - Reserve a VLAN range and public IP address range from an Advanced zone and assign - it to an account - - - Disassociate a VLAN and public IP address range from an account - - - - Ensure that you check whether the required range is available and conforms to - account limits. The maximum IPs per account limit cannot be superseded. - -
    -
    - Enhanced Juniper SRX Support for Egress Firewall Rules - Egress firewall rules were previously supported on virtual routers, and now they are - also supported on Juniper SRX external networking devices. - Egress traffic originates from a private network to a public network, such as the - Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed - from a guest network to the Internet. However, you can control the egress traffic in an - Advanced zone by creating egress firewall rules. When an egress firewall rule is applied, - the traffic specific to the rule is allowed and the remaining traffic is blocked. When all - the firewall rules are removed the default policy, Block, is applied. - - Egress firewall rules are not supported on Shared networks. They are supported only - on Isolated guest networks. - -
    -
    - Configuring the Default Egress Policy - The default egress policy for Isolated guest network can be configured by using - Network offering. Use the create network offering option to determine whether the default - policy should be block or allow all the traffic to the public network from a guest - network. Use this network offering to create the network. If no policy is specified, by - default all the traffic is allowed from the guest network that you create by using this - network offering. - You have two options: Allow and Deny. - If you select Allow for a network offering, by default egress traffic is allowed. - However, when an egress rule is configured for a guest network, rules are applied to block - the specified traffic and rest are allowed. If no egress rules are configured for the - network, egress traffic is accepted. If you select Deny for a network offering, by default - egress traffic for the guest network is blocked. However, when an egress rules is - configured for a guest network, rules are applied to allow the specified traffic. While - implementing a guest network, &PRODUCT; adds the firewall egress rule specific to the - default egress policy for the guest network. - This feature is supported only on virtual router and Juniper SRX. -
    -
    - Non-Contiguous VLAN Ranges - &PRODUCT; provides you with the flexibility to add non contiguous VLAN ranges to your - network. The administrator can either update an existing VLAN range or add multiple non - contiguous VLAN ranges while creating a zone. You can also use the UpdatephysicalNetwork - API to extend the VLAN range. -
    -
    - Isolation in Advanced Zone Using Private VLAN - Isolation of guest traffic in shared networks can be achieved by using Private VLANs - (PVLAN). PVLANs provide Layer 2 isolation between ports within the same VLAN. In a - PVLAN-enabled shared network, a user VM cannot reach other user VM though they can reach - the DHCP server and gateway, this would in turn allow users to control traffic within a - network and help them deploy multiple applications without communication between - application as well as prevent communication with other users’ VMs. - - - Isolate VMs in a shared networks by using Private VLANs. - - - Supported on KVM, XenServer, and VMware hypervisors. - - - PVLAN-enabled shared network can be a part of multiple networks of a guest VM. - - - - For further reading: - - - Understanding Private VLANs - - - Cisco Systems' Private VLANs: - Scalable Security in a Multi-Client Environment - - - Private VLAN (PVLAN) on vNetwork Distributed - Switch - Concept Overview (1010691) - - -
    -
    - Configuring Multiple IP Addresses on a Single NIC - (Supported on XenServer, KVM, and VMware hypervisors) - &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 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. -
    -
    - Adding Multiple IP Ranges - (Supported on KVM, xenServer, and VMware hypervisors) - &PRODUCT; 4.2 provides you with the flexibility to add guest IP ranges from different - subnets in Basic zones and security groups-enabled Advanced zones. For security - groups-enabled Advanced zones, it implies multiple subnets can be added to the same VLAN. - With the addition of this feature, you will be able to add IP address ranges from the same - subnet or from a different one when IP address are exhausted. This would in turn allows - you to employ higher number of subnets and thus reduce the address management - overhead. - Ensure that you manually configure the gateway of the new subnet before adding the IP - range. Note that &PRODUCT; supports only one gateway for a subnet; overlapping subnets are - not currently supported. - You can also delete IP ranges. This operation fails if an IP from the remove range is - in use. If the remove range contains the IP address on which the DHCP server is running, - &PRODUCT; acquires a new IP from the same subnet. If no IP is available in the subnet, the - remove operation fails. - - The feature can only be implemented on IPv4 addresses. - -
    -
    - Support for Multiple Networks in VMs - (Supported on XenServer, VMware and KVM hypervisors) - &PRODUCT; 4.2 provides you the ability to add and remove multiple networks to a VM. - You can remove a network from a VM and add a new network. You can also change the default - network of a VM. With this functionality, hybrid or traditional server loads can be - accommodated with ease. - For adding or removing a NIC to work on VMware, ensure that vm-tools are running on - guest VMs. -
    -
    - Global Server Load Balancing - &PRODUCT; 4.2 supports Global Server Load Balancing (GSLB) functionalities to provide - business continuity by load balancing traffic to an instance on active zones only in case - of zone failures . &PRODUCT; achieve this by extending its functionality of integrating - with NetScaler Application Delivery Controller (ADC), which also provides various GSLB - capabilities, such as disaster recovery and load balancing. The DNS redirection technique - is used to achieve GSLB in &PRODUCT;. In order to support this functionality, region level - services and service provider are introduced. A new service 'GSLB' is introduced as a - region level service. The GSLB service provider is introduced that will provider the GSLB - service. Currently, NetScaler is the supported GSLB provider in &PRODUCT;. GSLB - functionality works in an Active-Active data center environment. -
    -
    - Enhanced Load Balancing Services Using External Provider on Shared VLANs - Network services like Firewall, Load Balancing, and NAT are now supported in shared - networks created in an advanced zone. In effect, the following network services shall be - made available to a VM in a shared network: Source NAT, Static NAT, Port Forwarding, - Firewall and Load balancing. Subset of these service can be chosen while creating a - network offering for shared networks. Services available in a shared network is defined by - the network offering and the service chosen in the network offering. For example, if - network offering for a shared network has source NAT service enabled, a public IP shall be - provisioned and source NAT is configured on the firewall device to provide public access - to the VMs on the shared network. Static NAT, Port Forwarding, Load Balancing, and - Firewall services shall be available only on the acquired public IPs associated with a - shared network. - Additionally, Netscaler and Juniper SRX firewall device can be configured inline or - side-by-side mode. -
    -
    - Health Checks for Load Balanced Instances - - This feature is supported only on NetScaler version 10.0 and beyond. - - (NetScaler load balancer only) A load balancer rule distributes requests among a pool - of services (a service in this context means an application running on a virtual machine). - When creating a load balancer rule, you can specify a health check which will ensure that - the rule forwards requests only to services that are healthy (running and available). When - a health check is in effect, the load balancer will stop forwarding requests to any - resources that it has found to be unhealthy. If the resource later becomes available - again, the periodic health check (periodicity is configurable) will discover it and the - resource will once again be made available to the load balancer. - To configure how often the health check is performed by default, use the global - configuration setting healthcheck.update.interval. This default applies to all the health - check policies in the cloud. You can override this value for an individual health check - policy. -
    -
    -
    - Host and Virtual Machine Enhancements - The following new features expand the ways you can use hosts and virtual - machines. -
    - VMware DRS Support - The VMware vSphere Distributed Resources Scheduler (DRS) is supported. -
    -
    - Windows 8 and Windows Server 2012 as VM Guest OS - (Supported on XenServer, VMware, and KVM) - Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual - machines. The OS would be made available the same as any other, by uploading an ISO or a - template. The instructions for uploading ISOs and templates are given in the - Administrator's Guide. - - Limitation: When used with VMware hosts, this - feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch - 4. - - -
    -
    - Change Account Ownership of Virtual Machines - A root administrator can now change the ownership of any virtual machine from one - account to any other account. A domain or sub-domain administrator can do the same for VMs - within the domain from one account to any other account in the domain. -
    -
    - Private Pod, Cluster, or Host - Dedicating pod, cluster or host to a specific domain/account means that the - domain/account will have sole access to the dedicated pod, cluster or hosts such that - scalability, security and manageability within a domain/account can be improved. The - resources which belong to that tenant will be placed into that dedicated pod, cluster or - host. -
    -
    - Resizing Volumes - &PRODUCT; provides the ability to resize data disks; &PRODUCT; controls volume size by - using disk offerings. This provides &PRODUCT; administrators with the flexibility to - choose how much space they want to make available to the end users. Volumes within the - disk offerings with the same storage tag can be resized. For example, if you only want to - offer 10, 50, and 100 GB offerings, the allowed resize should stay within those limits. - That implies if you define a 10 GB, a 50 GB and a 100 GB disk offerings, a user can - upgrade from 10 GB to 50 GB, or 50 GB to 100 GB. If you create a custom-sized disk - offering, then you have the option to resize the volume by specifying a new, larger size. - Additionally, using the resizeVolume API, a data volume can be moved from a static disk - offering to a custom disk offering with the size specified. This functionality allows - those who might be billing by certain volume sizes or disk offerings to stick to that - model, while providing the flexibility to migrate to whatever custom size necessary. This - feature is supported on KVM, XenServer, and VMware hosts. However, shrinking volumes is - not supported on VMware hosts -
    -
    - VMware Volume Snapshot Improved Performance - When you take a snapshot of a data volume on VMware, &PRODUCT; will now use a more - efficient storage technique to improve performance. - Previously, every snapshot was immediately exported from vCenter to a mounted NFS - share and packaged into an OVA file format. This operation consumed time and resources. - Starting from 4.2, the original file formats (e.g., VMDK) provided by vCenter will be - retained. An OVA file will only be created as needed, on demand. - The new process applies only to newly created snapshots after upgrade to &PRODUCT; - 4.2. Snapshots that have already been taken and stored in OVA format will continue to - exist in that format, and will continue to work as expected. -
    -
    - Storage Migration: XenMotion and vMotion - (Supported on XenServer and VMware) - Storage migration allows VMs to be moved from one host to another, where the VMs are - not located on storage shared between the two hosts. It provides the option to live - migrate a VM’s disks along with the VM itself. It is now possible to migrate a VM from one - XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks are on - local storage, or even to migrate a VM’s disks from one storage repository to another, all - while the VM is running. -
    -
    - Configuring Usage of Linked Clones on VMware - (For ESX hypervisor in conjunction with vCenter) - In &PRODUCT; 4.2, the creation of VMs as full clones is allowed. In previous versions, - only linked clones were possible. - For a full description of clone types, refer to VMware documentation. In summary: A - full clone is a copy of an existing virtual machine which, once created, does not depend - in any way on the original virtual machine. A linked clone is also a copy of an existing - virtual machine, but it has ongoing dependency on the original. A linked clone shares the - virtual disk of the original VM, and retains access to all files that were present at the - time the clone was created. - A new global configuration setting has been added, vmware.create.full.clone. When the - administrator sets this to true, end users can create guest VMs only as full clones. The - default value is true for new installations. For customers upgrading from a previous - version of &PRODUCT;, the default value of vmware.create.full.clone is false. -
    -
    - VM Deployment Rules - Rules can be set up to ensure that particular VMs are not placed on the same physical - host. These "anti-affinity rules" can increase the reliability of applications by ensuring - that the failure of a single host can not take down the entire group of VMs supporting a - given application. See Affinity Groups in the &PRODUCT; 4.2 Administration Guide. -
    -
    - CPU and Memory Scaling for Running VMs - (Supported on VMware and XenServer) - You can now change the CPU and RAM values for a running virtual machine. In previous - versions of &PRODUCT;, this could only be done on a stopped VM. - It is not always possible to accurately predict the CPU and RAM requirements when you - first deploy a VM. You might need to increase or decrease these resources at any time - during the life of a VM. With the new ability to dynamically modify CPU and RAM levels, - you can change these resources for a running VM without incurring any downtime. - Dynamic CPU and RAM scaling can be used in the following cases: - - - New VMs that are created after the installation of &PRODUCT; 4.2. If you are - upgrading from a previous version of &PRODUCT;, your existing VMs created with - previous versions will not have the dynamic scaling capability. - - - User VMs on hosts running VMware and XenServer. - - - System VMs on VMware. - - - VM Tools or XenServer Tools must be installed on the virtual machine. - - - The new requested CPU and RAM values must be within the constraints allowed by the - hypervisor and the VM operating system. - - - To configure this feature, use the following new global configuration - variables: - - - enable.dynamic.scale.vm: Set to True to enable the feature. By default, the - feature is turned off. - - - scale.retry: How many times to attempt the scaling operation. Default = 2. - - -
    -
    - CPU and Memory Over-Provisioning - (Supported for XenServer, KVM, and VMware) - In &PRODUCT; 4.2, CPU and memory (RAM) over-provisioning factors can be set for each - cluster to change the number of VMs that can run on each host in the cluster. This helps - optimize the use of resources. By increasing the over-provisioning ratio, more resource - capacity will be used. If the ratio is set to 1, no over-provisioning is done. - In previous releases, &PRODUCT; did not perform memory over-provisioning. It performed - CPU over-provisioning based on a ratio configured by the administrator in the global - configuration setting cpu.overprovisioning.factor. Starting in 4.2, the administrator can - specify a memory over-provisioning ratio, and can specify both CPU and memory - over-provisioning ratios on a per-cluster basis, rather than only on a global - basis. - In any given cloud, the optimum number of VMs for each host is affected by such things - as the hypervisor, storage, and hardware configuration. These may be different for each - cluster in the same cloud. A single global over-provisioning setting could not provide the - best utilization for all the different clusters in the cloud. It had to be set for the - lowest common denominator. The new per-cluster setting provides a finer granularity for - better utilization of resources, no matter where the &PRODUCT; placement algorithm decides - to place a VM. -
    -
    - Kickstart Installation for Bare Metal Provisioning - &PRODUCT; 4.2 supports the kick start installation method for RPM-based Linux - operating systems on baremetal hosts in basic zones. Users can provision a baremetal host - managed by &PRODUCT; as long as they have the kick start file and corresponding OS - installation ISO ready. - Tested on CentOS 5.5, CentOS 6.2, CentOS 6.3, Ubuntu 12.04. - For more information, see the Baremetal Installation Guide. -
    -
    - Enhanced Bare Metal Support on Cisco UCS - You can now more easily provision new Cisco UCS server blades into &PRODUCT; for use - as bare metal hosts. The goal is to enable easy expansion of the cloud by leveraging the - programmability of the UCS converged infrastructure and &PRODUCT;’s knowledge of the cloud - architecture and ability to orchestrate. With this new feature, &PRODUCT; can - automatically understand the UCS environment, server profiles, etc. to make it easy to - deploy a bare metal OS on a Cisco UCS. -
    -
    - Changing a VM's Base Image - Every VM is created from a base image, which is a template or ISO which has been - created and stored in &PRODUCT;. Both cloud administrators and end users can create and - modify templates, ISOs, and VMs. - In &PRODUCT; 4.2, there is a new way to modify an existing VM. You can change an - existing VM from one base image to another. For example, suppose there is a template based - on a particular operating system, and the OS vendor releases a software patch. The - administrator or user naturally wants to apply the patch and then make sure existing VMs - start using it. Whether a software update is involved or not, it's also possible to simply - switch a VM from its current template to any other desired template. -
    -
    - Reset VM on Reboot - In &PRODUCT; 4.2, you can specify that you want to discard the root disk and create a - new one whenever a given VM is rebooted. This is useful for secure environments that need - a fresh start on every boot and for desktops that should not retain state. The IP address - of the VM will not change due to this operation. -
    -
    - Virtual Machine Snapshots for VMware - (VMware hosts only) In addition to the existing &PRODUCT; ability to snapshot - individual VM volumes, you can now take a VM snapshot to preserve all the VM's data - volumes as well as (optionally) its CPU/memory state. This is useful for quick restore of - a VM. For example, you can snapshot a VM, then make changes such as software upgrades. If - anything goes wrong, simply restore the VM to its previous state using the previously - saved VM snapshot. - The snapshot is created using the VMware native snapshot facility. The VM snapshot - includes not only the data volumes, but optionally also whether the VM is running or - turned off (CPU state) and the memory contents. The snapshot is stored in &PRODUCT;'s - primary storage. - VM snapshots can have a parent/child relationship. Each successive snapshot of the - same VM is the child of the snapshot that came before it. Each time you take an additional - snapshot of the same VM, it saves only the differences between the current state of the VM - and the state stored in the most recent previous snapshot. The previous snapshot becomes a - parent, and the new snapshot is its child. It is possible to create a long chain of these - parent/child snapshots, which amount to a "redo" record leading from the current state of - the VM back to the original. -
    -
    - Increased Userdata Size When Deploying a VM - You can now specify up to 32KB of userdata when deploying a virtual machine through - the &PRODUCT; UI or the deployVirtualMachine API call. -
    -
    - Set VMware Cluster Size Limit Depending on VMware Version - The maximum number of hosts in a vSphere cluster is determined by the VMware - hypervisor software. For VMware versions 4.2, 4.1, 5.0, and 5.1, the limit is 32 - hosts. - For &PRODUCT; 4.2, the global configuration setting vmware.percluster.host.max has - been removed. The maximum number of hosts in a VMware cluster is now determined by the - underlying hypervisor software. - - Best Practice: It is advisable for VMware clusters in &PRODUCT; to be smaller than - the VMware hypervisor's maximum size. A cluster size of up to 8 hosts has been found - optimal for most real-world situations. - -
    -
    - Limiting Resource Usage - Previously in &PRODUCT;, resource usage limit was imposed based on the resource count, - that is, restrict a user or domain on the basis of the number of VMs, volumes, or - snapshots used. In &PRODUCT; 4.2, a new set of resource types has been added to the - existing pool of resources (VMs, Volumes, and Snapshots) to support the customization - model—need-basis usage, such as large VM or small VM. The new resource types are now - broadly classified as CPU, RAM, Primary storage, and Secondary storage. &PRODUCT; 4.2 - allows the root administrator to impose resource usage limit by the following resource - types for Domain, Project and Accounts. - - - CPUs - - - Memory (RAM) - - - Primary Storage (Volumes) - - - Secondary Storage (Snapshots, Templates, ISOs) - - -
    -
    -
    - Monitoring, Maintenance, and Operations Enhancements - -
    - Publish and Subscribe for Event Notification - An event is essentially a significant or meaningful change in the state of both - virtual and physical resources associated with a cloud environment. In &PRODUCT; an event - could be a state change of virtual or psychical resources, an action performed by an user - (action events), or policy based events (alerts). In &PRODUCT; 4.2, a new event - notification framework has been added. This framework provides a means for the Management - Server components to publish and subscribe to &PRODUCT; events. Event notification is - achieved by implementing the concept of event bus abstraction in the Management Server. - A new event for state change, resource state change, is introduced as part of Event - notification framework. Every resource, such as user VM, volume, NIC, network, public IP, - snapshot, and template, is associated with a state machine and generates events as part of - the state change. That implies that a change in the state of a resource results in a state - change event, and the event is published in the corresponding state machine on the event - bus. All the &PRODUCT; events (alerts, action events, usage events) and the additional - category of resource state change events, are published on to the events bus. -
    -
    - Deleting and Archiving Events and Alerts - In addition to viewing a list of events and alerts in the UI, the administrator can - now delete and archive them. In order to support deleting and archiving alerts, the - following global parameters have been added: - - - alert.purge.delay: The alerts older than - specified number of days are purged. Set the value to 0 to never purge alerts - automatically. - - - alert.purge.interval: The interval in seconds to - wait before running the alert purge thread. The default is 86400 seconds (one - day). - - - - Archived alerts or events cannot be viewed in the UI, or by using the API. They are - maintained in the database for auditing or compliance purposes. - -
    -
    - Increased Granularity for Configuration Parameters - Some configuration parameters which were previously available only at the global level - of the cloud can now be set for smaller components of the cloud, such as at the zone - level. To set these parameters, look for the new Settings tab in the UI. You will find it - on the detail page for an account, cluster, zone, or primary storage. - The account level parameters are: remote.access.vpn.client.iprange, - allow.public.user.templates, use.system.public.ips, and - use.system.guest.vlans - The cluster level parameters are - cluster.storage.allocated.capacity.notificationthreshold, - cluster.storage.capacity.notificationthreshold, - cluster.cpu.allocated.capacity.notificationthreshold, - cluster.memory.allocated.capacity.notificationthreshold, - cluster.cpu.allocated.capacity.disablethreshold, - cluster.memory.allocated.capacity.disablethreshold, - cpu.overprovisioning.factor, mem.overprovisioning.factor, - vmware.reserve.cpu, and vmware.reserve.mem. - The zone level parameters are - pool.storage.allocated.capacity.disablethreshold, - pool.storage.capacity.disablethreshold, - storage.overprovisioning.factor, network.throttling.rate, - guest.domain.suffix, router.template.xen, - router.template.kvm, router.template.vmware, - router.template.hyperv, router.template.lxc, - enable.dynamic.scale.vm, use.external.dns, and - blacklisted.routes. -
    -
    - API Request Throttling - In &PRODUCT; 4.2, you can limit the rate at which API requests can be placed for each - account. This is useful to avoid malicious attacks on the Management Server, prevent - performance degradation, and provide fairness to all accounts. - If the number of API calls exceeds the threshold, an error message is returned for any - additional API calls. The caller will have to retry these API calls at another - time. - To control the API request throttling, use the following new global configuration - settings: - - - api.throttling.enabled - Enable/Disable API throttling. By default, this setting - is false, so API throttling is not enabled. - - - api.throttling.interval (in seconds) - Time interval during which the number of - API requests is to be counted. When the interval has passed, the API count is reset to - 0. - - - api.throttling.max - Maximum number of APIs that can be placed within the - api.throttling.interval period. - - - api.throttling.cachesize - Cache size for storing API counters. Use a value higher - than the total number of accounts managed by the cloud. One cache entry is needed for - each account, to store the running API total for that account within the current time - window. - - -
    -
    - Sending Alerts to External SNMP and Syslog Managers - In addition to showing administrator alerts on the Dashboard in the &PRODUCT; UI and - sending them in email, &PRODUCT; now can also send the same alerts to external SNMP or - Syslog management software. This is useful if you prefer to use an SNMP or Syslog manager - to monitor your cloud. - The supported protocol is SNMP version 2. -
    -
    - Changing the Default Password Encryption - Passwords are encoded when creating or updating users. The new default preferred - encoder, replacing MD5, is SHA256. It is more secure than MD5 hashing. If you take no - action to customize password encryption and authentication, SHA256 Salt will be - used. - If you prefer a different authentication mechanism, &PRODUCT; 4.2 provides a way for - you to determine the default encoding and authentication mechanism for admin and user - logins. Two new configurable lists have been introduced: userPasswordEncoders and - userAuthenticators. userPasswordEncoders allow you to configure the order of preference - for encoding passwords, and userAuthenticator allows you to configure the order in which - authentication schemes are invoked to validate user passwords. - The plain text user authenticator has been modified not to convert supplied passwords - to their md5 sums before checking them with the database entries. It performs a simple - string comparison between retrieved and supplied login passwords instead of comparing the - retrieved md5 hash of the stored password against the supplied md5 hash of the password, - because clients no longer hash the password. -
    -
    - Log Collection Utility cloud-bugtool - &PRODUCT; provides a command-line utility called cloud-bugtool to make it easier to - collect the logs and other diagnostic data required for troubleshooting. This is - especially useful when interacting with Citrix Technical Support. - You can use cloud-bugtool to collect the following: - - - Basic system and environment information and network configuration including IP - addresses, routing, and name resolver settings - - - Information about running processes - - - Management Server logs - - - System logs in /var/log/ - - - Dump of the cloud database - - - - cloud-bugtool collects information which might be considered sensitive and - confidential. Using the --nodb option to avoid the cloud database can - reduce this concern, though it is not guaranteed to exclude all sensitive data. - - -
    -
    - Snaphotting, Backups, Cloning and System VMs for RBD Primary Storage - - These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt - 0.9.14 on the KVM hypervisors. - - This release of &PRODUCT; will leverage the features of RBD format 2. This allows - snapshotting and backing up those snapshots. - Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they - are not RBD diffs. This because when restoring a backup of a snapshot it is not mandatory - that this backup is deployed on RBD again, it could also be a NFS Primary Storage. - Another key feature of RBD format 2 is cloning. With this release templates will be - copied to Primary Storage once and by using the cloning mechanism new disks will be cloned - from this parent template. This saves space and decreases deployment time for instances - dramatically. - Before this release, a NFS Primary Storage was still required for running the System - VMs from. The reason was a so called 'patch disk' that was generated by the - hypervisor which contained metadata for the System VM. The scripts generating this disk - didn't support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of - the patch disk a VirtIO serial console is used to pass meta information to System VMs. - This enabled the deployment of System VMs on RBD Primary Storage. +
    + Publish and Subscribe for Event Notification + An event is essentially a significant or meaningful change in the state of both + virtual and physical resources associated with a cloud environment. In &PRODUCT; an + event could be a state change of virtual or psychical resources, an action performed by + an user (action events), or policy based events (alerts). In &PRODUCT; 4.2, a new event + notification framework has been added. This framework provides a means for the + Management Server components to publish and subscribe to &PRODUCT; events. Event + notification is achieved by implementing the concept of event bus abstraction in the + Management Server. + A new event for state change, resource state change, is introduced as part of Event + notification framework. Every resource, such as user VM, volume, NIC, network, public + IP, snapshot, and template, is associated with a state machine and generates events as + part of the state change. That implies that a change in the state of a resource results + in a state change event, and the event is published in the corresponding state machine + on the event bus. All the &PRODUCT; events (alerts, action events, usage events) and the + additional category of resource state change events, are published on to the events + bus. +
    +
    + Deleting and Archiving Events and Alerts + In addition to viewing a list of events and alerts in the UI, the administrator can + now delete and archive them. In order to support deleting and archiving alerts, the + following global parameters have been added: + + + alert.purge.delay: The alerts older than + specified number of days are purged. Set the value to 0 to never purge alerts + automatically. + + + alert.purge.interval: The interval in seconds + to wait before running the alert purge thread. The default is 86400 seconds (one + day). + + + + Archived alerts or events cannot be viewed in the UI, or by using the API. They + are maintained in the database for auditing or compliance purposes. + +
    +
    + Increased Granularity for Configuration Parameters + Some configuration parameters which were previously available only at the global + level of the cloud can now be set for smaller components of the cloud, such as at the + zone level. To set these parameters, look for the new Settings tab in the UI. You will + find it on the detail page for an account, cluster, zone, or primary storage. + The account level parameters are: remote.access.vpn.client.iprange, + allow.public.user.templates, use.system.public.ips, and + use.system.guest.vlans + The cluster level parameters are + cluster.storage.allocated.capacity.notificationthreshold, + cluster.storage.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.notificationthreshold, + cluster.memory.allocated.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.disablethreshold, + cluster.memory.allocated.capacity.disablethreshold, + cpu.overprovisioning.factor, mem.overprovisioning.factor, + vmware.reserve.cpu, and vmware.reserve.mem. + The zone level parameters are + pool.storage.allocated.capacity.disablethreshold, + pool.storage.capacity.disablethreshold, + storage.overprovisioning.factor, network.throttling.rate, + guest.domain.suffix, router.template.xen, + router.template.kvm, router.template.vmware, + router.template.hyperv, router.template.lxc, + enable.dynamic.scale.vm, use.external.dns, and + blacklisted.routes. +
    +
    + API Request Throttling + In &PRODUCT; 4.2, you can limit the rate at which API requests can be placed for + each account. This is useful to avoid malicious attacks on the Management Server, + prevent performance degradation, and provide fairness to all accounts. + If the number of API calls exceeds the threshold, an error message is returned for + any additional API calls. The caller will have to retry these API calls at another + time. + To control the API request throttling, use the following new global configuration + settings: + + + api.throttling.enabled - Enable/Disable API throttling. By default, this setting + is false, so API throttling is not enabled. + + + api.throttling.interval (in seconds) - Time interval during which the number of + API requests is to be counted. When the interval has passed, the API count is reset + to 0. + + + api.throttling.max - Maximum number of APIs that can be placed within the + api.throttling.interval period. + + + api.throttling.cachesize - Cache size for storing API counters. Use a value + higher than the total number of accounts managed by the cloud. One cache entry is + needed for each account, to store the running API total for that account within the + current time window. + + +
    +
    + Sending Alerts to External SNMP and Syslog Managers + In addition to showing administrator alerts on the Dashboard in the &PRODUCT; UI and + sending them in email, &PRODUCT; now can also send the same alerts to external SNMP or + Syslog management software. This is useful if you prefer to use an SNMP or Syslog + manager to monitor your cloud. + The supported protocol is SNMP version 2. +
    +
    + Changing the Default Password Encryption + Passwords are encoded when creating or updating users. The new default preferred + encoder, replacing MD5, is SHA256. It is more secure than MD5 hashing. If you take no + action to customize password encryption and authentication, SHA256 Salt will be + used. + If you prefer a different authentication mechanism, &PRODUCT; 4.2 provides a way for + you to determine the default encoding and authentication mechanism for admin and user + logins. Two new configurable lists have been introduced: userPasswordEncoders and + userAuthenticators. userPasswordEncoders allow you to configure the order of preference + for encoding passwords, and userAuthenticator allows you to configure the order in which + authentication schemes are invoked to validate user passwords. + The plain text user authenticator has been modified not to convert supplied + passwords to their md5 sums before checking them with the database entries. It performs + a simple string comparison between retrieved and supplied login passwords instead of + comparing the retrieved md5 hash of the stored password against the supplied md5 hash of + the password, because clients no longer hash the password. +
    +
    + Log Collection Utility cloud-bugtool + &PRODUCT; provides a command-line utility called cloud-bugtool to make it easier to + collect the logs and other diagnostic data required for troubleshooting. This is + especially useful when interacting with Citrix Technical Support. + You can use cloud-bugtool to collect the following: + + + Basic system and environment information and network configuration including IP + addresses, routing, and name resolver settings + + + Information about running processes + + + Management Server logs + + + System logs in /var/log/ + + + Dump of the cloud database + + + + cloud-bugtool collects information which might be considered sensitive and + confidential. Using the --nodb option to avoid the cloud database can + reduce this concern, though it is not guaranteed to exclude all sensitive data. + + +
    +
    + Snaphotting, Backups, Cloning and System VMs for RBD Primary Storage + + These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt + 0.9.14 on the KVM hypervisors. + + This release of &PRODUCT; will leverage the features of RBD format 2. This allows + snapshotting and backing up those snapshots. + Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they + are not RBD diffs. This because when restoring a backup of a snapshot it is not + mandatory that this backup is deployed on RBD again, it could also be a NFS Primary + Storage. + Another key feature of RBD format 2 is cloning. With this release templates will be + copied to Primary Storage once and by using the cloning mechanism new disks will be + cloned from this parent template. This saves space and decreases deployment time for + instances dramatically. + Before this release, a NFS Primary Storage was still required for running the System + VMs from. The reason was a so called 'patch disk' that was generated by the hypervisor + which contained metadata for the System VM. The scripts generating this disk didn't + support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of the + patch disk a VirtIO serial console is used to pass meta information to System VMs. This + enabled the deployment of System VMs on RBD Primary Storage. +
    @@ -1223,7 +1239,7 @@ under the License. - + @@ -1237,10 +1253,10 @@ under the License. CLOUDSTACK-2709 + >CLOUDSTACK-3466 VM Migration across VMware clusters which are added with different switches - (Standard Swith,Vmware DVS, Cisco Nexus 1000v) is not supported.. + (Standard Switch,Vmware DVS, Cisco Nexus 1000v) is not supported. @@ -1248,7 +1264,7 @@ under the License. >CLOUDSTACK-4207 The following exception is observed when the Management Server is started - after upgrade from any older versions to &PRODUCT; 4.2. + after the upgrade from any older versions to &PRODUCT; 4.2. jsonParseException: The JsonDeserializer com.cloud.agent.transport.ArrayTypeAdaptor@2426e26f failed to deserialize json object @@ -1261,17 +1277,18 @@ under the License. CLOUDSTACK-2709 - Egress rules are are not supported on shared networks. + Egress rules are not supported on Shared networks. CLOUDSTACK-1747 - mvn deploydb only creates 4.0 DB, not 4.2 - Due to tooling changes between 4.2 and 4.2, CloudStack's database is created + The mvn deploydb command creates only 4.0 database, not 4.2 + database. + Due to tooling changes between 4.0 and 4.2, &PRODUCT; database is created by using the 4.0 schema and updated to the 4.2 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. + starts for the first time. Neglect the same schema if the management server has + not started yet. @@ -1280,8 +1297,9 @@ under the License. >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. + Enhance the error message that is displayed when trying to deploy a VM by + passing the static IPv4 addresses that are assigned to another VM or an IPv4 + address that is outside the IP range. @@ -1290,7 +1308,8 @@ under the License. >CLOUDSTACK-1236 - Warning while adding Xen 6.1 host [Unable to create local link network] + Warning while adding a XenSever 6.1 host. The warning displayed is Unable to + create local link network. @@ -1396,20 +1415,347 @@ under the License. console proxy does not support any keymaps besides us, jp + + CLOUDSTACK-4645 + There is no upgrade path from 4.1.1 to 4.2.0. + + + CLOUDSTACK-4641 + Create volume form snapshot command times out exactly after 1 hour in + case of KVM hosts. + + + CLOUDSTACK-4621 + Changing the management server's Ethernet interface or MAC address leaves + the system in unstable state. + + + CLOUDSTACK-4615 + (Baremetal) Baremetal agent is missing in the installer. + + + CLOUDSTACK-4598 + Long delays during deploying a VM; both network and deployment planner + are delayed. + + + CLOUDSTACK-4596 + The same IP range is allowed to be defined in different VLANs across + public and portable ranges. + + + CLOUDSTACK-4588 + (VMware) VM deployment failed while creating a volume with null pointer + exception. + + + CLOUDSTACK-4578 + (VMware) SSVM is not getting created if one host is down in a + cluster. + + + CLOUDSTACK-4551 + Migrating the data volume from NFS to local storage does not change the + underlying disk offering. + + + CLOUDSTACK-4550 + Migration does not work in the case of bridge naming while upgrading KVM + agents to version 4.2. + + + CLOUDSTACK-4549 + Deploying VMs from template fails if the template is created from a + snapshot. + + + CLOUDSTACK-4540 + (VMware) When deploying 30 parallel VMs , 16 VMs fails to get deployed + due to the following error: "VmDataCommand failed due to Exception: + java.lang.Exception Message: Timed out in waiting SSH execution + result". + + + CLOUDSTACK-4506 + In a mixed hypervisor setup, destroying a VM whose host has been removed, + throws a null pointer exception. The Root volume of that VM also is not deleted + from the primary memory. + + + CLOUDSTACK-4442 + Source NAT not applied when network starts up. + + + CLOUDSTACK-4405 + (KVM) Migration between existing hosts and new hosts + fails. + + + CLOUDSTACK-4402 + No options to delete the primary storage if the last host with which it + was associated is already removed. + + + CLOUDSTACK-4366 + (Ubuntu) Key translation fails for the Japanese keyboard for the Menu key + and Caps Lock buttons. + + + CLOUDSTACK-4351 + Host/Hypervisor System Requirements has misleading or premature note in + the documentation. + + + CLOUDSTACK-4348 + Regression truncation issues occurs when moving the cursor to the "plus" + buttons. + + + CLOUDSTACK-4300 + (KVM) System VMs are not coming up after 2.2.14 to 4.2 + upgrade. + + + CLOUDSTACK-4292 + The destroyedvm API failed with ArrayIndexexception while + expunging. + + + CLOUDSTACK-4247 + (VMWARE) Network read and write statistics always returns + zero. + + + CLOUDSTACK-4224 + Deleting UCS returns unknown API. + + + CLOUDSTACK-4220 + From 3.0.6 to 4.2 upgrade, Add VMWare DataCenter button is provided for + legacy zones. + + + CLOUDSTACK-4201 + The listServiceOfferings API does not take virtualmachineid parameter of + SystemVM to return the Service Offerings available for the VM to change a Service + Offering. + + + CLOUDSTACK-4200 + The listSystemVMs API and listRouters API fail to return hypervisor + property. + + + CLOUDSTACK-4148 + The usage statistics are not triggered for Shared network. + + + CLOUDSTACK-4139 + (VMWARE) Resizing the volumes fails if they are created from + snapshot. + + + CLOUDSTACK-4137 + (KVM): After removing a cluster, manage cluster will not bring KVM hosts + to UP state. Manually restart the cloud-agent on KVM hosts. + + + CLOUDSTACK-4128 + The System VMs start up does not check for existence of staging secondary + storage in a zone. + + + CLOUDSTACK-4099 + Update the systemvm templates in DevCloud2. + + + CLOUDSTACK-4095 + Region ID is displayed within the Database Transaction + code. + + + CLOUDSTACK-4072 + The mysql-connector-java rpm is required while upgrading from 2.2.14 to + 4.2. + + + CLOUDSTACK-4036 + The UI remains in processing state and the queryAsyncJobResult is being + called repeatedly for the scaleSystemVm API. + + + CLOUDSTACK-4016 + The listPublicIpAddresses lists the portable IP that was already + transferred to a different Isolated network. + + + CLOUDSTACK-3968 + Distributed Port groups are not deleted when guest networks are removed. + The user account of this network is removed from &PRODUCT; + + + CLOUDSTACK-3967 + No support for usage statistics collection at the portable IP + level + + + CLOUDSTACK-3953 + The usage statistics are not collected for GSLB rules. + + + CLOUDSTACK-3911 + No check available while adding public range in a zone to see whether the + same VLAN exists in a portable IP range. + + + CLOUDSTACK-3888 + The UI does not return the mode (Strict/Preferred) when listing the + ServiceOffering that uses ImplicitDedicationPlanner. + + + CLOUDSTACK-3808 + Attaching volumes does not work when root is at the zone-level primary + store and data at the cluster level or host level store. + + + CLOUDSTACK-3791 + Download template fails with a null pointer exception. + + + CLOUDSTACK-3788 + The weekly Snapshot is stuck in Allocated State. + + + CLOUDSTACK-3765 + Upgrading CloudPlatform 4.2 build on centos5.5 does not + work. + + + CLOUDSTACK-3737 + Uploaded volume is not getting deleted from secondary storage after + attaching it to a guest VM. + + + CLOUDSTACK-3658 + Several old object storage tables and columns are deprecated as a part of + 4.1 to 4.2 database upgrade. + + + CLOUDSTACK-3627 + Public IP interface (eth2) is not getting configured with Redundant + virtual router. The State is FAULT. + + + CLOUDSTACK-3608 + The guest_os_hypervisor table in the database has repeated mappings of + hypervisor and guest OS. + + + CLOUDSTACK-3583 + Stopping the Management server does not remove the PID. + + + CLOUDSTACK-3565 + Restarting libvirtd service leads to destroying the storage + pool. + + + CLOUDSTACK-3243 + Wrong NFS mount point is given in the documentation. + + + CLOUDSTACK-3138 + Flaws in the documentation for the upgrade from 3.0.2 to + 4.1.0. + + + CLOUDSTACK-2791 + Installation instruction is wrong. + + + CLOUDSTACK-1986 + Key translation fails for the following Japanese keyboard keys: ¥_,\ |, + Muhenkan, Henkan, Hiragana/Katakana, Kanji Key, and Caps Lock. + + + CLOUDSTACK-1775 + Events related to User/Domain/Account are not being generated expect for + the USER-DISABLE,DOMAIN-DELETE and ACCOUNT.DISABLE events. + + + CLOUDSTACK-732 + KVM snapshot is not supported. +
    - - Upgrade Instructions + + Upgrade Instructions for 4.2 This section contains upgrade instructions from prior versions of CloudStack to Apache CloudStack 4.2.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@cloudstack.apache.org or dev@cloudstack.apache.org. -
    +
    Upgrade from 4.x.x to 4.2.0 This section will guide you from &PRODUCT; 4.0.x versions to &PRODUCT; 4.2.0. Any steps that are hypervisor-specific will be called out with a note. @@ -1438,7 +1784,7 @@ under the License. url="http://cloudstack.apache.org/downloads.html" >http://cloudstack.apache.org/downloads.html for package repositories supplied by community members. You will need them for step - or step . + or step . Instructions for creating packages from the &PRODUCT; source are in the Installation Guide. @@ -1489,7 +1835,7 @@ under the License. If you are using Ubuntu, follow this procedure to upgrade your packages. If not, - skip to step . + skip to step . Community Packages This section assumes you're using the community supplied packages for &PRODUCT;. @@ -1515,7 +1861,7 @@ under the License. Now update your apt package list: $ sudo apt-get update - + 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. @@ -1642,7 +1988,7 @@ service cloudstack-agent restart - + If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If not, skip to step . @@ -1651,7 +1997,7 @@ service cloudstack-agent restart If you've created your own packages and yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -1673,7 +2019,7 @@ gpgcheck=0 If you're using your own package repository, change this line to read as appropriate for your 4.2.0 repository. - + Now that you have the repository configured, it's time to install the cloudstack-management package by upgrading the older cloud-client package. @@ -1751,7 +2097,7 @@ Done restarting router(s).
    -
    +
    Upgrade from 3.0.2 to 4.2.0 This section will guide you from Citrix CloudStack 3.0.2 to Apache CloudStack 4.2.0. Sections that are hypervisor-specific will be called out with a note. @@ -1760,7 +2106,7 @@ Done restarting router(s). 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 . + linkend="stopping-usage-servers"/>. In each zone that includes VMware hosts, you need to add a new system VM template. @@ -1846,7 +2192,7 @@ Done restarting router(s). - + Stop all Usage Servers if running. Run this on all Usage Server hosts. # service cloud-usage stop @@ -2341,7 +2687,7 @@ service cloudstack-agent start issues are seen, try clearing your browser cache and reloading the UI page.
    -
    +
    Upgrade from 2.2.14 to 4.2.0 @@ -2565,7 +2911,7 @@ service cloudstack-agent restart If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If - not, skip to step . + not, skip to step . Community Packages This section assumes you're using the community supplied packages for &PRODUCT;. @@ -2628,7 +2974,7 @@ service cloudstack-agent start - + 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 @@ -3033,7 +3379,7 @@ service cloudstack-agent start
    - API Changes from 4.1 to 4.2 + API Changes in 4.2
    Added API Commands in 4.2
    @@ -9493,7 +9839,7 @@ service cloudstack-agent start
    - Upgrade Instructions + Upgrade Instructions for 4.1 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 @@ -9526,7 +9872,7 @@ service cloudstack-agent start 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 + by community members. You will need them for step or step . Instructions for creating packages from the &PRODUCT; source are in the Installation @@ -9576,7 +9922,7 @@ service cloudstack-agent start PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to 4.1. - + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, skip to step . @@ -9585,7 +9931,7 @@ service cloudstack-agent start 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 @@ -9610,7 +9956,7 @@ service cloudstack-agent start dependencies you need. $ sudo apt-get install cloudstack-management - + You will need to manually install the cloudstack-agent package: $ sudo apt-get install cloudstack-agent @@ -9657,7 +10003,7 @@ service cloudstack-agent restart If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If - not, skip to step . + not, skip to step . Community Packages This section assumes you're using the community supplied packages for &PRODUCT;. @@ -9691,7 +10037,7 @@ gpgcheck=0 cloud-client package. $ sudo yum upgrade cloud-client - + For KVM hosts, you will need to upgrade the cloud-agent package, similarly installing the new version as cloudstack-agent. @@ -9720,7 +10066,7 @@ service cloudstack-agent start - + Once you've upgraded the packages on your management servers, you'll need to restart the system VMs. Make sure port 8096 is open in your local host firewall to do this. @@ -9750,7 +10096,7 @@ Done restarting router(s).
    -
    +
    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. @@ -9759,7 +10105,7 @@ Done restarting router(s). 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 . + linkend="stopping-usage-servers"/>. In each zone that includes VMware hosts, you need to add a new system VM template. @@ -9845,7 +10191,7 @@ Done restarting router(s). - + Stop all Usage Servers if running. Run this on all Usage Server hosts. # service cloud-usage stop @@ -9868,16 +10214,16 @@ Done restarting router(s). the community provided yum/apt repositories to gain access to the &PRODUCT; binaries. - + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, - skip to step . + 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 @@ -9896,13 +10242,13 @@ Done restarting router(s). Now update your apt package list: $ sudo apt-get update - + 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: $ sudo apt-get install cloudstack-agent @@ -9947,16 +10293,16 @@ service cloudstack-agent restart - + If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If - not, skip to step . + 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 yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -9977,13 +10323,13 @@ gpgcheck=0 If you're using your own package repository, change this line to read as appropriate for your 4.1.0 repository. - + Now that you have the repository configured, it's time to install the cloudstack-management package by upgrading the older cloud-client package. $ sudo yum upgrade cloud-client - + For KVM hosts, you will need to upgrade the cloud-agent package, similarly installing the new version as cloudstack-agent. @@ -10012,7 +10358,7 @@ service cloudstack-agent start - + 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 @@ -10464,16 +10810,16 @@ service cloudstack-agent start the community provided yum/apt repositories to gain access to the &PRODUCT; binaries. - + If you are using Ubuntu, follow this procedure to upgrade your packages. If not, - skip to step . + 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 @@ -10492,13 +10838,13 @@ service cloudstack-agent start Now update your apt package list: $ sudo apt-get update - + 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 - + On KVM hosts, you will need to manually install the cloudstack-agent package: $ sudo apt-get install cloudstack-agent @@ -10543,16 +10889,16 @@ service cloudstack-agent restart - + If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If - not, skip to step . + 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 yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -10573,13 +10919,13 @@ gpgcheck=0 If you're using your own package repository, change this line to read as appropriate for your 4.1.0 repository. - + Now that you have the repository configured, it's time to install the cloudstack-management package by upgrading the older cloud-client package. $ sudo yum upgrade cloud-client - + For KVM hosts, you will need to upgrade the cloud-agent package, similarly installing the new version as cloudstack-agent. @@ -10608,7 +10954,7 @@ service cloudstack-agent start - + 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 From bff5a64ed2860ac4a01e5e829937744fd0428397 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 12 Sep 2013 18:06:30 +0530 Subject: [PATCH 164/251] build fixed, global parameter and manage cloud were sections, changed to chapter as per adminguide.xml --- docs/en-US/global-config.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en-US/global-config.xml b/docs/en-US/global-config.xml index 59373de55b1..237614d3f85 100644 --- a/docs/en-US/global-config.xml +++ b/docs/en-US/global-config.xml @@ -1,5 +1,5 @@ - %BOOK_ENTITIES; ]> @@ -21,7 +21,7 @@ specific language governing permissions and limitations under the License. --> -
    + Setting Configuration Parameters
    About Configuration Parameters @@ -339,4 +339,4 @@
    -
    + From 237f8ec702a7bf3ba2a3215caf421797fcf9c263 Mon Sep 17 00:00:00 2001 From: venkataswamybabu budumuru Date: Thu, 12 Sep 2013 18:23:05 +0530 Subject: [PATCH 165/251] Signed-off-by: venkataswamybabu budumuru Skipped the test "test_createSharedNetwork_usedVlan" as there is another test (test_createSharedNetwork_usedVlan2) which is covering the righ scenario. moreover this test is creating issues for couple of other tests due to incompleteness --- test/integration/component/test_shared_networks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/component/test_shared_networks.py b/test/integration/component/test_shared_networks.py index fec14a1514a..a1aeca87cd7 100644 --- a/test/integration/component/test_shared_networks.py +++ b/test/integration/component/test_shared_networks.py @@ -1709,7 +1709,8 @@ class TestSharedNetworks(cloudstackTestCase): ip_range = list(netaddr.iter_iprange(unicode(self.services["network"]["startip"]), unicode(self.services["network"]["endip"]))) if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") - + + @unittest.skip("skipped - This is a redundant case and also this is causing issue for rest fo the cases ") @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_usedVlan(self): """ Test Shared Network with used vlan 01 """ From 1a20cb8270746283281a6f877a3e1f1cc4167310 Mon Sep 17 00:00:00 2001 From: venkataswamybabu budumuru Date: Thu, 12 Sep 2013 18:33:41 +0530 Subject: [PATCH 166/251] Signed-off-by: venkataswamybabu budumuru Corrected Indentation issues. (cherry picked from commit 2ea66124f224eab2fb48b5e62005358726e9e12f) --- test/integration/component/test_shared_networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/component/test_shared_networks.py b/test/integration/component/test_shared_networks.py index a1aeca87cd7..88bb018d404 100644 --- a/test/integration/component/test_shared_networks.py +++ b/test/integration/component/test_shared_networks.py @@ -1710,7 +1710,7 @@ class TestSharedNetworks(cloudstackTestCase): if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") - @unittest.skip("skipped - This is a redundant case and also this is causing issue for rest fo the cases ") + @unittest.skip("skipped - This is a redundant case and also this is causing issue for rest fo the cases ") @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_usedVlan(self): """ Test Shared Network with used vlan 01 """ From cb71b5c1c2fb7097e431440902b4a9f5a78137d8 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 12 Sep 2013 19:53:14 +0530 Subject: [PATCH 167/251] removed the occurrence of 4.1 from RN as per Animesh --- docs/en-US/Book_Info_Release_Notes_4-0.xml | 4 +- docs/en-US/Release_Notes.xml | 6046 +------------------- 2 files changed, 3 insertions(+), 6047 deletions(-) diff --git a/docs/en-US/Book_Info_Release_Notes_4-0.xml b/docs/en-US/Book_Info_Release_Notes_4-0.xml index 9655986cc99..2749b9a6cd7 100644 --- a/docs/en-US/Book_Info_Release_Notes_4-0.xml +++ b/docs/en-US/Book_Info_Release_Notes_4-0.xml @@ -19,12 +19,12 @@ under the License. --> - Version 4.1.0 Release Notes + Version 4.2.0 Release Notes Apache CloudStack - Release notes for the Apache CloudStack 4.1.0 release. + Release notes for the Apache CloudStack 4.2.0 release. diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 96cf6e63b39..497acacbbb6 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -56,7 +56,7 @@ under the License. We hope you enjoy working with &PRODUCT;!
    - Version 4.2.0 + What's New in 4.2.0
    &PRODUCT; 4.2 includes the following new features.
    @@ -5291,6048 +5291,4 @@ service cloudstack-agent start
    - - 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. -
    - Localization - The 4.1.0 release adds partial User Interface (UI) support for Catalan, Chinese, - French, German, Italian, Japanese, Korean, Norwegian, Portuguese, Russian, and Spanish. - Not all languages are complete. - The 4.1.0 release also adds documentation translations for Chinese, Chinese (Taiwan), - Italian, Japanese, Korean, and Portuguese. -
    -
    - Added Region Support - CLOUDSTACK-241: This feature adds a "region" construct that spans several - management servers. The objective of this feature is to add AWS EC2 like Regions - implementation into CloudStack. Regions are dispersed and located in separate geographic - areas. Availability Zones (or Zones in CloudStack) are distinct locations within a Region - that are engineered to be isolated from failures in other Zones and provide inexpensive, - low latency network connectivity to other Zones in the same Region. - Regions are expected to add the following benefits - - - Higher availability of the services: users can deploy services across AZs and even - if one of the AZ goes down the services are still available to the end-user through - VMs deployed in other zones. - - - Higher availability of the Management Server (MS): Since each MS Cluster only - manages a single Region, if that MS Cluster goes down, only that particular Region is - impacted. Admin should be able to access all the other Regions. - - - Scalability: The scalability limit of CloudStack dramatically improves, as the - scalability limit of MS Cluster is limited to a single Region. - - - Object Store: With Regions construct, CloudStack would also allow users to define - Object Store (Secondary Storage) across AZs. This helps users easily deploy VMs in - different AZs using the same template, offerings. - - - Geographical Grouping: Regions allow admins to group AZs (that have low latency - and are geographically located nearby) into a broader region construct. - - - Currently the Region feature is exposed in the API, but does not have a UI - component. -
    -
    - Support for EC2 Query API - CLOUDSTACK-197: This introduces a query API for the AWS APIs that are currently - only supported by SOAP. The AWS Java SDK and AWS PHP SDK should now be supported by the - AWSAPI in CloudStack. - Supported Query APIs in 4.1.0: - - - AllocateAddress - - - AssociateAddress - - - AttachVolume - - - AuthorizeSecurityGroupIngress - - - CreateImage - - - CreateKeyPair - - - CreateSecurityGroup - - - CreateSnapshot - - - CreateTags - - - CreateVolume - - - DeleteKeyPair - - - DeleteSecurityGroup - - - DeleteSnapshot - - - DeleteTags - - - DeleteVolume - - - DeregisterImage - - - DescribeAddresses - - - DescribeAvailabilityZones - - - DescribeImageAttribute - - - DescribeImages - - - DescribeInstanceAttribute - - - DescribeInstances - - - DescribeKeyPairs - - - DescribeSecurityGroups - - - DescribeSnapshots - - - DescribeTags - - - DescribeVolumes - - - DetachVolume - - - DisassociateAddress - - - GetPasswordData - - - ImportkeyPair - - - ModifyImageAttribute - - - RebootInstances - - - RegisterImage - - - ReleaseAddress - - - ResetImageAttribute - - - RevokeSecurityGroupIngress - - - RunInstances - - - StartInstances - - - StopInstances - - - TerminateInstances - - - See the Feature Specification for more information on the Query API support. -
    -
    - Auto-Completing Shell for CloudStack (CloudMonkey) - CLOUDSTACK-132: Adds a auto-completing shell and command-line tool for - &PRODUCT; written in Python, called CloudMonkey. - CloudMonkey includes the following features: - - - Usable as a command line tool and interactive shell. - - - All commands are lowercase unlike API. - - - Api Discovery using sync feature, with build time api precaching for failsafe - sync. - - - Raw api execution support. - - - Auto-completion via double tab. - - - Reverse search using Ctrl+R - - - Emacs compatible key bindings. - - - Output that's "pipeable" to other *nix programs. - - - Unix shell execution. - - - Support to handle asynchronous jobs using user defined blocking or non-blocking - way. - - - Tabular or JSON output with filtering of table columns. - - - Colored output. - - - API parameter value completion (based on predication, fuzzy results may fail - sometimes). - - - CloudMonkey has a few requirements above and beyond CloudStack, and does not need to - be run on the same machine as a management server. If you wish to run - CloudMonkey you'll need Python 2.5 or later, - readline, Pygments, and - prettytable. CloudMonkey can be installed with - pip: - $ pip install cloudmonkey - See the Developer's Guide and the CloudStack - wiki for the latest information on CloudMonkey - installation and use. -
    -
    - API Discovery Service - CLOUDSTACK-926: CloudStack has more than 300 APIs and more are added in each - major release. CloudStack admins can enable or disable APIs, or add plugins which provide - more APIs. The API Discovery Service is a plugin which will help users discover the APIs - available to them on a CloudStack Management Server. - The discovery service implements a method called listApis which - will return information about APIs for a user. It currently accepts an apiName to list api - information of that particular API. The method ensures that user can only list APIs they - are entitled to. - All CloudStack APIs are implemented by annotated command class and PluggableService is - a contract implemented by all the components such as the Management Server and all the - plugins which provide an API. During load time, API discovery service asks all the - pluggable services to return list of API cmd classes from whose fields and annotations it - gathers information about each API, the information consists of name, description, - parameter name, parameter description, etc. - For more information on the implementation of the API Discovery Service for 4.1.0, see - the CloudStack - wiki. -
    -
    - Events Framework - CLOUDSTACK-820: The Events Framework provides a mechanism to publish and - subscribe to events in &PRODUCT;. -
    -
    - Additional VMX Settings - ### -
    -
    - L3 Router Functionality in Nicira Nvp Plugin - ### -
    -
    - Persistent Networks without Running VM - ### -
    -
    - Add/Remove Network on VM - ### -
    -
    - Resize Volumes Feature - ### -
    -
    - Autoscale - ### -
    -
    - API Request Throttling - CLOUDSTACK-618: Limits the number of API requests per second that can be placed - against a management server to avoid DoS attacks via API requests. - The throttling is controlled by the api.throttling.enabled, - api.throttling.interval, and api.throttling.max - configuration settings. Note that api.throttling.enabled is set to - false by default. -
    -
    - S3 Backed Secondary Storage - CLOUDSTACK-509: This enhancement backs NFS secondary storage with an - S3-compatible object store. Periodically, a reaper thread synchronizes the templates, - ISOs, and snapshots stored on a NFS secondary storage mount with a configured S3 object - store. In addition to permitting the use of commodity or IaaS storage solutions for static - assets, it provides a means of automatically synchronizing template and ISO assets across - multiple zones. - See the &PRODUCT; - wiki for more information on this feature, currently the documentation is - incomplete. -
    -
    - User and Domain Admin Can Create API Key and Secret - CLOUDSTACK-437: This feature adds the ability for domain admins and users to - create their own API Key and Secret. Domain admins can create keys for themselves, - subdomain admins, and for regular users, but not for other domain admins. -
    -
    - Support Inline Mode for F5 and SRX - CLOUDSTACK-306: For &PRODUCT; deployments using the Juniper SRX (firewall) and - F5 Big IP (load balancer), &PRODUCT; 4.1.0 supports putting the firewall in front of the - load balancer, making the firewall device the gateway and putting the load balancer behind - the public network. -
    -
    - Egress Firewall Rules for Guest Networks - CLOUDSTACK-299: This feature allows users to create egress (exit) traffic rules - from private networks to public networks (e.g. from your internal - network to the public Internet). By default all traffic is blocked from internal networks - to the public networks, this allows you to open ports as necessary. - Egress traffic rules are suppored only on virtual routers at this time, physical - devices are not supported. -
    -
    - Reset SSH Key to Access VM - CLOUDSTACK-297: &PRODUCT; 4.1.0 introduces a new API - resetSSHKeyForVirtualMachine, that can allow them to set or reset the - SSH keypair assigned to a virtual machine. -
    -
    -
    - 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. - Approximately 470 bugs were resolved or closed in the 4.1.0 cycle. - - - - - - - - Defect - - - Description - - - - - - - CLOUDSTACK-46 - - - Remnants of mycloud remain. - - - - - CLOUDSTACK-70 - - - Improve Network Restart Behaviour for Basic Zone: Restarting Network - Fail - - - - - CLOUDSTACK-94 - - - "API command, listIsos documentation clarity - - - - - CLOUDSTACK-95 - - - IP address allocation not working when a user tries to allocate IP addresses - in a Project - - - - - CLOUDSTACK-97 - - - Vmware network labels are ignored when creating a Zone using basic - networking - - - - - CLOUDSTACK-108 - - - VM should not be allowed to be deployed on two Isolated Networks of an Account - that were created from DefaultNetworkOfferingwithSourceNATService - - - - - CLOUDSTACK-118 - - - Status of host resorce stuck in "ErrorInMaintenance" - - - - - CLOUDSTACK-119 - - - Move Agent-Simulator in to the hypervisor plugin model - - - - - CLOUDSTACK-130 - - - Clarify docs on tags parameter in API reference - - - - - CLOUDSTACK-152 - - - Routes on the User VM are programmed incorrectly on a VM present on both - Isolated and Shared Guest Network - - - - - CLOUDSTACK-178 - - - Expose name parameter of VM in list Vm view. - - - - - CLOUDSTACK-198 - - - vpn:failto add VPN Users deletes all the existing Vpn user - - - - - CLOUDSTACK-222 - - - Admin UI prompts to restart Management server with cancel edit - operation - - - - - CLOUDSTACK-225 - - - API Docs: Request params repeated with different description - - - - - CLOUDSTACK-226 - - - UpdatePhysicalNetworkcommand failed due to java.sql.BatchUpdateException ; - Tried to extend the existing Guest VLAN Range of one physical network into the - Guest VLAN range of the other physical network - - - - - CLOUDSTACK-227 - - - ReconnectHostCmd: NullPointerException: Unable to get host Information for - XenServer 6.0.2 host - on intentionally changing the traffic labels on the - physical network - - - - - CLOUDSTACK-228 - - - UI provides an option to reconnect a disconnected host - ServerApiException is - thrown on an attempt - - - - - CLOUDSTACK-232 - - - Zone infrastructure chart -- disable resource total display - - - - - CLOUDSTACK-235 - - - Network rate can be set in 2 places. Clarify docs on how this works - - - - - CLOUDSTACK-249 - - - Add host id to failed VM deploy alerts - - - - - CLOUDSTACK-250 - - - Incorrect description of maintenance mode in admin guide - - - - - CLOUDSTACK-256 - - - "vpn:As an admin user, not able to delete VPN user which is present in a - regular user's network. - - - - - CLOUDSTACK-271 - - - updatePhysicalNetwork dies with an NPE when the vlan range is empty - - - - - CLOUDSTACK-274 - - - Two error codes mapped to same value in API - - - - - CLOUDSTACK-275 - - - hostid not always a UUID - - - - - CLOUDSTACK-277 - - - Message during CloudStack management server Installation: cannot access - /usr/share/cloud/bridge/lib: No such file or directory - - - - - CLOUDSTACK-279 - - - deleteProject fails when executed by the regular user (works fine for - root/domain admin) - - - - - CLOUDSTACK-284 - - - listVirtualMachines does not return deleted machines when zone is - specified - - - - - CLOUDSTACK-290 - - - 3.0.0 template also needed for 2.2.14 to 3.0.5 direct upgrade. - - - - - CLOUDSTACK-293 - - - "We do awful, hacky things in our spec file for client" - - - - - CLOUDSTACK-304 - - - Add synchronization for createSnapshot command per host basis - - - - - CLOUDSTACK-309 - - - iptables rules being deleted from wrong VM after a migration - - - - - CLOUDSTACK-318 - - - Adding XenServer Host Fails - 6.0.2 fails with 4.0.0 - - - - - CLOUDSTACK-320 - - - "sessionKey query parameter should be case-insensitive, now only sessionkey is - accepted" - - - - - CLOUDSTACK-322 - - - During upgrade displays error - a foreign key constraint fails - (`cloud/#sql-f34_6e`.. - - - - - CLOUDSTACK-332 - - - "count" property in list* API response should be equal to how many entries in - database, not how many objects in API response - - - - - CLOUDSTACK-333 - - - When Datacenter name in VCenter has spaces Primary Storage (VMFS) discovery - will fail - - - - - CLOUDSTACK-335 - - - KVM VPC load balancer not working - - - - - CLOUDSTACK-336 - - - listZones doesn't honour paging - - - - - CLOUDSTACK-343 - - - "Document what tools and packages are required to build, package and install - CloudStack 4.0 - - - - - CLOUDSTACK-346 - - - Cannot add Vmware cluster with class loader conflict exception - - - - - CLOUDSTACK-347 - - - listNetworks API: return vlan information only when the caller is ROOT - admin - - - - - CLOUDSTACK-348 - - - deleteNetwork does not clean up network resource count correctly - - - - - CLOUDSTACK-354 - - - Display of storage statistics is wrong - - - - - CLOUDSTACK-355 - - - "Fix ""count"" in a bunch of API commands - - - - - CLOUDSTACK-357 - - - "ISOs can be deleted while still attached to a running VM, and they - subsequently cannot be detached from a running VM - - - - - CLOUDSTACK-359 - - - PropagateResourceEventCommand failes in cluster configuration - - - - - CLOUDSTACK-361 - - - Wrong creation of guest networks on a KVM host in Multiple Physical Networks - with guest traffic - - - - - CLOUDSTACK-364 - - - Docs point to download.cloud.com for AWS API script - - - - - CLOUDSTACK-368 - - - OVM - cannot create guest VM - - - - - CLOUDSTACK-369 - - - ASF 4.0 - unable to support XenServer 6.1 host - - - - - CLOUDSTACK-373 - - - "static NAT and Firewall is not working on external firewall device SRX, it - needs to be implemented - - - - - CLOUDSTACK-377 - - - provide deployment config access to marvin's testcase - - - - - CLOUDSTACK-378 - - - mavenize marvin on master - - - - - CLOUDSTACK-390 - - - Install Guide: Section 4.5.7 (Prepare the System VM Template): Links go to - cloud.com - - - - - CLOUDSTACK-397 - - - Install Guide: Section 11.1 (Guest Traffic): Diagram is the wrong - diagram - - - - - CLOUDSTACK-398 - - - Install Guide: Section 11.17.3 (Using VPN with Mac OSX): Not complete - - - - - CLOUDSTACK-404 - - - Update docs on the usage of cloud-setup-database - - - - - CLOUDSTACK-412 - - - Data truncation: Out of range value for column 'ram' at row - - - - - CLOUDSTACK-415 - - - restartNetwork call causes VM to be unreachable when Nicira based SDN is - used - - - - - CLOUDSTACK-416 - - - XCP 1.6beta2 (61002c) - can't add a host - - - - - CLOUDSTACK-417 - - - Handle password server securely to run on port 8080 on VR - - - - - CLOUDSTACK-424 - - - Updated userdata not propagating to the VR - - - - - CLOUDSTACK-427 - - - Change hardcoded step number references to dynamic link - - - - - CLOUDSTACK-428 - - - Storage capacity shown in UI is incorrect - - - - - CLOUDSTACK-435 - - - Vmware network labels are ignored when creating a Zone using basic - networking - - - - - CLOUDSTACK-441 - - - Running mgmt server using jetty fails to start api server - - - - - CLOUDSTACK-446 - - - "Host going to alert state, if you are adding already added host - - - - - CLOUDSTACK-448 - - - SSVM bootstrap failure on XenServer hosts with E3 CPU - - - - - CLOUDSTACK-456 - - - License tag in SPEC isn't what RPM is expecting - - - - - CLOUDSTACK-459 - - - [Optional Public IP assignment for EIP with Basic Zone] Associate IP Checkbox - in Create Network Offering Dialog is Displayed When Elastic LB is Selected - - - - - CLOUDSTACK-462 - - - A few corrections to make to the 4.0.0 installation guide - - - - - CLOUDSTACK-464 - - - "Regression in AWSAPI docs, entire sections removed - - - - - CLOUDSTACK-465 - - - French language file quotes are dropping javascript syntax error - - - - - CLOUDSTACK-467 - - - Developer's Guide points to cloud.com for API reference - - - - - CLOUDSTACK-479 - - - UpdateVirtualMachine api fails to propagate userdata to domr - - - - - CLOUDSTACK-481 - - - Installation Guide Doc Error - - - - - CLOUDSTACK-493 - - - 2.2.x-3.0 DB upgrade support for Advance SG enabled network - - - - - CLOUDSTACK-499 - - - cloudmonkey CLI can't accept complex parameter - - - - - CLOUDSTACK-500 - - - Passwd-server iptables rules are dropped on domr on fresh start or on - reboot. - - - - - CLOUDSTACK-501 - - - Apidocs and marvin does not know how to handle Autoscaling docs. - - - - - CLOUDSTACK-504 - - - Duplicate guest password scripts in codebase. - - - - - CLOUDSTACK-507 - - - fix api docs for listSSHKeyPair - - - - - CLOUDSTACK-508 - - - CLVM copies template to primary storage unnecessarily. - - - - - CLOUDSTACK-510 - - - Add button not visible when adding public IPs to physical network. - - - - - CLOUDSTACK-514 - - - Marvin and Cloudmonkey don't work when an API target uses https or an - alternate path. - - - - - CLOUDSTACK-518 - - - API refactoring -- change @Parameter annotation and remove the @IdentityMapper - annotation. - - - - - CLOUDSTACK-520 - - - Dependency jar names mismatch with install-non-oss.sh - - - - - CLOUDSTACK-521 - - - Build will hung up when doing test for TestAgentShell - - - - - CLOUDSTACK-522 - - - Log requests in cloudmonkey's log file. - - - - - CLOUDSTACK-527 - - - List API performance optimization by using DB views and removing UUID - conversion. - - - - - CLOUDSTACK-534 - - - Failed to add host - - - - - CLOUDSTACK-536 - - - remove citrix cloudpatform from 4.0 build - CloudStack is ASF project. - - - - - CLOUDSTACK-539 - - - Cropped Text in UI under Quick View. - - - - - CLOUDSTACK-552 - - - ]Quick view details for a volume displays scroll bar in place of name of the - volume when the name of the volume has more no of characters. - - - - - CLOUDSTACK-553 - - - "SRX - When adding SRX device make "Public Network" - default to "untrusted" - and "Private Network" - default to "trusted" as un-editable fields. - - - - - CLOUDSTACK-556 - - - Erratic window behavior in Quick View tooltip. - - - - - CLOUDSTACK-559 - - - source code import problem - - - - - CLOUDSTACK-560 - - - Usage server doesn't work in 4.0.0 due to missing db changes - - - - - CLOUDSTACK-572 - - - SG Enabled Advanced Zone - Not able to deploy a VM in an account specific - shared network - - - - - CLOUDSTACK-573 - - - "NPE at - ""com.cloud.network.NetworkManagerImpl.networkOfferingIsConfiguredForExternalNetworking(NetworkManagerImpl.java:4345)"" - when create network from the network offering having NULL provider for the - service - - - - - CLOUDSTACK-578 - - - The already deleted same hostname is not deleted from /etc/hosts of - vRouter - - - - - CLOUDSTACK-584 - - - "typos in - ""Apache_CloudStack-4.0.0-incubating-CloudStack_Nicira_NVP_Guide-en-US"" - - - - - CLOUDSTACK-590 - - - Incorrect Network Gateways Assigned to System VM - - - - - CLOUDSTACK-592 - - - "API bloat, unknown apis cmd classes - - - - - CLOUDSTACK-593 - - - "2 guest network, auto create vlan error - - - - - CLOUDSTACK-596 - - - DeployVM command takes a lot of time to return job id. - - - - - CLOUDSTACK-599 - - - DhcpEntryCommand fails on Router VM on CS4.0 and vSphere5 with Advanced - Network Zone. - - - - - CLOUDSTACK-600 - - - When rebooting KVM local storage VM host, libvirt definitions deleted - - - - - CLOUDSTACK-605 - - - Host physical CPU is incorrectly calculated for Vmware host - - - - - CLOUDSTACK-606 - - - Starting VM fails with 'ConcurrentOperationException' in a clustered MS - scenario - - - - - CLOUDSTACK-614 - - - "ListTemplates API is not returning ""Enable SSH Key"" attribute for any given - template - - - - - CLOUDSTACK-617 - - - Unable to edit a Sub domain - - - - - CLOUDSTACK-639 - - - API Refactoring: Adapters for ACL - - - - - CLOUDSTACK-648 - - - The normal users could change their own login password. - - - - - CLOUDSTACK-660 - - - Network Traffic Labels are not functional in Marvin - - - - - CLOUDSTACK-683 - - - Image Is Missing in the Accessing VM Section - - - - - CLOUDSTACK-689 - - - RVR: Stop pending flag is not cleared when user start the disconnected router - from another host - - - - - CLOUDSTACK-691 - - - A warning dialog box shows after reloading the welcome page. - - - - - CLOUDSTACK-693 - - - Adding a VPC virtual router to a NiciraNVP enabled network fails. - - - - - CLOUDSTACK-694 - - - "Create a new VPC network offering with "connectivity" option needed for SDN - networking) is not allowed / VPC support for SDN networks - - - - - CLOUDSTACK-717 - - - cloudmonkey fails to parse/print response. - - - - - CLOUDSTACK-720 - - - Fail to load a png image when accessing the web console. - - - - - CLOUDSTACK-721 - - - Bytes sent/received in user statistics is empty (CloudStack 4.0) - - - - - CLOUDSTACK-725 - - - UI: Error when the Egress rules tab is selected for a network. - - - - - CLOUDSTACK-734 - - - api_refactoring: CreateAccountCmd fails to send response due to NPE in service - layer - - - - - CLOUDSTACK-735 - - - Integration smoke tests: Fix expunge vm test on api_refactoring - - - - - CLOUDSTACK-736 - - - Integration smoke tests: Fix check for vm name for the deployvm smoke - test. - - - - - CLOUDSTACK-793 - - - "Create cloudmonkey-helper, a plugin that helps autodiscover and sync api info - via an api over some endpoint - - - - - CLOUDSTACK-798 - - - Move usage related cmd classes from cloud-server to cloud-api - - - - - CLOUDSTACK-799 - - - [Load Test] Check router statistics falls behind in gathering stats by more - than 2 times the set value - - - - - CLOUDSTACK-819 - - - Create Account/User API logging password in access log - - - - - CLOUDSTACK-863 - - - Non-printable characters (ASCII control character) such as %00 or %0025 are - getting stored in raw/non encoded form in the database - - - - - CLOUDSTACK-870 - - - Client UI: Wrong character encoding for some language - - - - - CLOUDSTACK-928 - - - [Simulator] Latency for Agent Commands - change unit of wait from seconds to - milliseconds - - - - - CLOUDSTACK-938 - - - s2s VPN trouble - - - - - CLOUDSTACK-959 - - - Missing sub-sections in document section System Service Offering - - - - - CLOUDSTACK-968 - - - marvin: vlan should be an attribute of the physical_network and not the - zone - - - - - CLOUDSTACK-977 - - - Document how to use openvswitch with KVM hypervisor - - - - - CLOUDSTACK-978 - - - TypeError: instance.displayname is undefined while adding VM's to the LB - rule - - - - - CLOUDSTACK-985 - - - Different MAC address for RvR caused issue in short term network outage - - - - - CLOUDSTACK-987 - - - Sections missing in Working With Snapshots - - - - - CLOUDSTACK-993 - - - "admin"" user is not getting created when management server is started. - - - - - CLOUDSTACK-995 - - - Not able to add the KVM host - - - - - CLOUDSTACK-1002 - - - Not able to start VM - - - - - CLOUDSTACK-1006 - - - need to disable service libvirt-guests in CentOS packaging RPMs, or in - installation docs - - - - - CLOUDSTACK-1008 - - - "Egress"" tab should not be presented in the UI for Shared Networks - - - - - CLOUDSTACK-1010 - - - Host count and Secondary storage count always shows 1 in UI - - - - - CLOUDSTACK-1011 - - - KVM host getting disconnected in cluster environment - - - - - CLOUDSTACK-1013 - - - running cloudstack overwrites default public/private ssh key - - - - - CLOUDSTACK-1014 - - - Merge ManagementServer and ManagementServerEx - - - - - CLOUDSTACK-1016 - - - Not able to deploy VM - - - - - CLOUDSTACK-1021 - - - the vlan is not creat to right nic. when i creat multi guest network - - - - - CLOUDSTACK-1024 - - - Regression: Unable to add Xenserver host with latest build. - - - - - CLOUDSTACK-1027 - - - "Update SSL certificate" button should properly reflect its - functionality - - - - - CLOUDSTACK-1029 - - - Enter the token to specified project is malfunctioned - - - - - CLOUDSTACK-1037 - - - "Make cloudmonkey awesome-er: Online help docs and api discovery, better - colored output, parameter value autocompletion - - - - - CLOUDSTACK-1050 - - - No Documentation on Adding a Load Balancer Rule - - - - - CLOUDSTACK-1051 - - - API dispatcher unable to find objectVO corresponding to - DeleteTemplatecmd - - - - - CLOUDSTACK-1055 - - - "The overlay still exists when the ""Recurring Snapshots"" dialog is canceled - by pressing esc key. - - - - - CLOUDSTACK-1056 - - - S3 secondary storage fails to upload systemvm template due to KVMHA - directory - - - - - CLOUDSTACK-1057 - - - regression of changeServiceForVirtualMachine API - fails to find service - offering by serviceOfferingId parameter - - - - - CLOUDSTACK-1063 - - - "SG Enabled Advanced Zone - "Add Guest Networks" - When user tries to add a - guest Network with scope as "Account" he should NOT be presented with "Offering - for shared security group enabled" - - - - - CLOUDSTACK-1064 - - - A type error occurs when trying to add account/register template... - - - - - CLOUDSTACK-1068 - - - Names in VR list is useless - - - - - CLOUDSTACK-1070 - - - javelin: NPE on executing registerIso API - - - - - CLOUDSTACK-1071 - - - Netscaler element is not getting loaded as part of LoadBalancing Service - Providers - - - - - CLOUDSTACK-1078 - - - Not able to start System Vms on Rhel 6.3 KVM host - - - - - CLOUDSTACK-1079 - - - Deploying AWSAPI with mvn -pl :cloud-awsapi jetty:run fail - - - - - CLOUDSTACK-1082 - - - UI doesn't throw any error message when trying to delete ip range from a - network that is in use. - - - - - CLOUDSTACK-1083 - - - listUsageRecords api: removed project results in NPE - - - - - CLOUDSTACK-1087 - - - Update the Developer Guide for ASFCS 4.1 Release - - - - - CLOUDSTACK-1088 - - - EnableStaticNat error will clear the data in database - - - - - CLOUDSTACK-1094 - - - Ipv6 - hostname/hostname --fqdn does not return the name of the VM. But i am - able to reach the Vm using their names - - - - - CLOUDSTACK-1095 - - - Ipv6 - dhclient command needs to be run manually on the Vms to get the Ipv6 - address - - - - - CLOUDSTACK-1100 - - - Expunge thread is not kicked off based on global configuration if the global - setting is less than 60 seconds - - - - - CLOUDSTACK-1103 - - - "IpV6 - listNetwork() command does not retrun gateway,netmask,cidr - - - - - CLOUDSTACK-1104 - - - Ipv6 - listVlanIpRanges() returns error 530 - - - - - CLOUDSTACK-1105 - - - "IpV6 - listVirtualMachines() does not return netmask, - gateway,ipaddress. - - - - - CLOUDSTACK-1107 - - - Ipv6 - Unable to extend Ip range for a Ipv6 network using craeteVlanIpRange() - command - Error code 530 returned - - - - - CLOUDSTACK-1108 - - - Ipv6 - Not able to restart Networks - - - - - CLOUDSTACK-1109 - - - "Ipv6 - Unable to expunge User Vms that are "Destroyed". - - - - - CLOUDSTACK-1111 - - - Ipv6 - listRouters() does not return guestipaddress/ - - - - - CLOUDSTACK-1112 - - - "Errors in "Prepare the System VM Template" - - - - - CLOUDSTACK-1113 - - - "Ipv6 - Not able to deploy a new VM in this network because of "Unable to - allocate Unique Ipv6 address" - - - - - CLOUDSTACK-1114 - - - unable to execute listegressfirewallrules API due invalid value id - - - - - CLOUDSTACK-1115 - - - In multiple shared network unable to login with default nic - KVM - - - - - CLOUDSTACK-1123 - - - ListStoragePools API broken by refactor - - - - - CLOUDSTACK-1138 - - - "Providing invalid values for gateway, netmask etc in the zoneWizard blocks - the VLAN container to load, throwing an error - - - - - CLOUDSTACK-1139 - - - "After the Vm is "Expunged" we see the entry still being present in the router - in /etc/dhcphosts.txt - - - - - CLOUDSTACK-1141 - - - "Ipv6 - After network restart (and reboot router), we do not see the existing - vms dnsentries not being programmed in the router. - - - - - CLOUDSTACK-1152 - - - Missing tag in host-add.xml - - - - - CLOUDSTACK-1153 - - - "Ipv6 - Vm deployment fails with "n must be positive" error. - - - - - CLOUDSTACK-1154 - - - Account/Users related API failed due to RegionService inject exception. - - - - - CLOUDSTACK-1157 - - - No API Documentation on Listing Custom User Templates Using CS4 API - - - - - CLOUDSTACK-1160 - - - References to version=3.0.3|4|5|6 in API classes needs to be removed. - - - - - CLOUDSTACK-1161 - - - Differences between 4.1 and master in - ongoing-config-of-external-firewalls-lb.xml - - - - - CLOUDSTACK-1163 - - - Failed with NPE while creating firewall rule - - - - - CLOUDSTACK-1168 - - - Create firewall rule broke - - - - - CLOUDSTACK-1173 - - - ConsoleProxyResource instantiation exception. - - - - - CLOUDSTACK-1174 - - - Snapshots related SQL error. - - - - - CLOUDSTACK-1176 - - - Issue with snapshots(create/list) - - - - - CLOUDSTACK-1181 - - - mvn deploy db failing with NPE - - - - - CLOUDSTACK-1190 - - - Make APIChecker interface throw a single sensible exception. - - - - - CLOUDSTACK-1200 - - - "Unknown column 'vm_instance.disk_offering_id' in table vm_instance, db - exception shown in MS log - - - - - CLOUDSTACK-1201 - - - "Failed to create ssh key for user "cloud" - /var/lib/cloud/management/.ssh/id_rsa and failed to start management server - - - - - CLOUDSTACK-1202 - - - Fail to install KVM cloud-agent. - - - - - CLOUDSTACK-1203 - - - Fail to create advance zone with SG enabled when UI allows SG enabled - option. - - - - - CLOUDSTACK-1204 - - - Fail to create advance zone due to fail to add host - - - - - CLOUDSTACK-1205 - - - Ipv6 - Ubuntu 12.10 guest Vms loses default route (after it expiration time ~ - 30 mts) when ipv6.autoconfig parameters are disabled except for - net.ipv6.conf.lo.autoconf which is enabled. - - - - - CLOUDSTACK-1206 - - - Failure in Copy of System template - - - - - CLOUDSTACK-1210 - - - Make all pluggable services return list of api cmd classes - - - - - CLOUDSTACK-1216 - - - UUID is null for admin and failed to register user key with 4.0 - - - - - CLOUDSTACK-1218 - - - "IPv6: Shared Network - After network restart with clean option, router is - assigned a different address. Name resolution for the existing guest Vms in the - network fails. - - - - - CLOUDSTACK-1219 - - - Ipv6 - Provide better error messages when deploying a Vm with Ip an address - that is outside the network's ip range / if the ip address already is assigned to - another Vm - - - - - CLOUDSTACK-1220 - - - Ipv6 - Better error message when deploy Vm fails to get a free Ip - address - - - - - CLOUDSTACK-1222 - - - API rate limit configs: removed double quote in upgrade script - - - - - CLOUDSTACK-1223 - - - Exception while starting jetty server: - org.springframework.beans.factory.BeanCreationException Error creating bean with - name 'apiServer' - - - - - CLOUDSTACK-1224 - - - Volume snapshot creation failing - - - - - CLOUDSTACK-1226 - - - Error while running Cloudstack-setup-database - - - - - CLOUDSTACK-1228 - - - Unable to Create System Vm's in the VMware Hypervisor setup - - - - - CLOUDSTACK-1229 - - - Incorrect SQL syntax to insert api limit related configuration items in - upgrade path script. - - - - - CLOUDSTACK-1231 - - - cloud-install-sys-tmplt failed due to missing path - - - - - CLOUDSTACK-1232 - - - "Ipv6 - Guest Vms are not able to get Ipaddress when executing dhclient - command when using ""/96"" network. - - - - - CLOUDSTACK-1233 - - - Veewee configuration files are inappropriately identified as ASLv2 licensed - file - - - - - CLOUDSTACK-1234 - - - Unable to start KVM agent with 4.1 build. - - - - - CLOUDSTACK-1237 - - - "Register Template fails with ""Cannot find template adapter for - XenServer"" - - - - - CLOUDSTACK-1239 - - - Unable to registerISO :unhandled exception executing api command: - registerIso - - - - - CLOUDSTACK-1240 - - - Unable to registerTemplate : Cannot find template adapter for - XenServer. - - - - - CLOUDSTACK-1241 - - - Network apply rules logic is broken. - - - - - CLOUDSTACK-1242 - - - [F5-SRX-InlineMode] Failed to create LB rule with F5-SRX inlinemode - deployment - - - - - CLOUDSTACK-1243 - - - Failed to cleanup account :java.lang.NullPointerException - - - - - CLOUDSTACK-1244 - - - fail to push sysmvm.iso onto xen host - - - - - CLOUDSTACK-1246 - - - "[ ALU beta CS 4.1 build2] ""Guest network"" missing in Add Zone wizard ( step - 3, Setup Network \ Physical Network) - - - - - CLOUDSTACK-1251 - - - Baremetal zone doesn't need primary/secondary storage in UI wizard. - - - - - CLOUDSTACK-1252 - - - Failed to download default template in VMware. - - - - - CLOUDSTACK-1260 - - - Failed to register template: Unable to find template adapter - - - - - CLOUDSTACK-1261 - - - Cannot find template adapter for XenServer. - - - - - CLOUDSTACK-1262 - - - "Failed to Prepare Secondary Storage in VMware, - - - - - CLOUDSTACK-1265 - - - logrotate dnsmasq configuration is wrong - - - - - CLOUDSTACK-1267 - - - KVM's cloudstack-agent service doesn't log (log4j) - - - - - CLOUDSTACK-1269 - - - Failed to start CPVM java.lang.NullPointerException Unable to start - SSVM - - - - - CLOUDSTACK-1272 - - - Autoscale: createAutoScaleVmProfile fails due to unable to retrieve Service - Offering ip - - - - - CLOUDSTACK-1274 - - - UpdateNetworkCmd throws NP - - - - - CLOUDSTACK-1276 - - - Remove autoscanning for 4.1 - - - - - CLOUDSTACK-1277 - - - ApiResponseHelper.createUserVmResponse failed to populate password field set - from UserVm object - - - - - CLOUDSTACK-1278 - - - Improper permissions on injectkeys.sh - - - - - CLOUDSTACK-1288 - - - [F5-SRX-InlineMode] classCastException during network restart with cleanup - option true - - - - - CLOUDSTACK-1289 - - - [F5-SRX-InlineMode] Usage stats are not generated for Juniper SRX Firewall in - inlinemode - - - - - CLOUDSTACK-1290 - - - listNetoworks API takes too long to respond - - - - - CLOUDSTACK-1292 - - - "[F5-SRX-InlineMode] Update network from SRX,F5 as service provideds to VR as - service provider does not delete firewall rules from SRX - - - - - CLOUDSTACK-1295 - - - NPE in usage parsers due to missing @Component inject - - - - - CLOUDSTACK-1299 - - - Errors in 4.5.5 section of installation guide - - - - - CLOUDSTACK-1300 - - - section in wrong order in installation guide - - - - - CLOUDSTACK-1303 - - - Ipv6 - java.lang.NullPointerException when executing listnetworks() and - deployVirtualMachine() after extending the Ipv4 range of a dual stack - network - - - - - CLOUDSTACK-1307 - - - Noticed NPE when we put host in maintenance mode in clustered management - setup - - - - - CLOUDSTACK-1310 - - - ASF-build-master-nonoss-rhel63 - create advance zone FAIL - - CreatePhysicalNetworkCmd FAIL - MySQLIntegrityConstraintViolationException: - Duplicate entry '200-Public' for key 'physical_network_id' - - - - - CLOUDSTACK-1312 - - - "Fix rolling upgrades from 4.0 to 4.1 in 4.1 release, fix db schemas to be - same as 4.0 - - - - - CLOUDSTACK-1313 - - - Working with Volumes Section Is Missing - - - - - CLOUDSTACK-1315 - - - [F5-SRX-InlineMode] Network implement failed with Run time Exception during - network upgrade from VR to SRX-F5 - - - - - CLOUDSTACK-1319 - - - createCustomerVpnGateway response gives TypeError: - json.createvpncustomergatewayresponse is undefined - - - - - CLOUDSTACK-1320 - - - Routers naming convention is changed to hostname. - - - - - CLOUDSTACK-1321 - - - [Site-to-Site VPN] No events are generated in case of status change in site to - site vpn connection - - - - - CLOUDSTACK-1326 - - - KVM - Failed to start cloud agent from SSVM - - - - - CLOUDSTACK-1328 - - - console view unable to connect - CPVM SSVM guest VM - - - - - CLOUDSTACK-1329 - - - "API listRouters response returns hostname instead of Virtual Routers, UI - displays host entry for each VR - - - - - CLOUDSTACK-1330 - - - ec2-run-instances - When -n option is used to deploy multiple Vms API returns - error even though few of the Vms have been deployed successfully - - - - - CLOUDSTACK-1331 - - - Upgrade fails for a 2.2.14 Zone having multiple guest networks using - network_tags and Public Vlan - - - - - CLOUDSTACK-1332 - - - IPV6 - Router and guest Vms should be able to use an IPV6 address for external - DNS entry - - - - - CLOUDSTACK-1334 - - - vmware.root.disk.controller doesn't work - - - - - CLOUDSTACK-1337 - - - Zone to zone template/ISO copy fails and template/ISO download also - fail - - - - - CLOUDSTACK-1338 - - - Deploy VM failed using IS - - - - - CLOUDSTACK-1339 - - - ASF 4.1: Management server becomes unresponsive - - - - - CLOUDSTACK-1341 - - - URL for the KEYs file is wrong in the installation guide - - - - - CLOUDSTACK-1342 - - - Document installation and usage of cloudmonkey for 4.1 docs - - - - - CLOUDSTACK-1343 - - - Porting Baremetal related UI changes to ACS - - - - - CLOUDSTACK-1344 - - - Typo in use.external.dns setting description - - - - - CLOUDSTACK-1345 - - - BigSwitch plugin introduces 'VNS' isolation in UI without backend - implementation - - - - - CLOUDSTACK-1346 - - - "Check to see if external devices are used in the network, is hardcoded for - specific devices - - - - - CLOUDSTACK-1347 - - - "Not able to delete network. Error - "Unable to insert queue item into - database, DB is full?" - - - - - CLOUDSTACK-1348 - - - API/UI: zoneObj is undefined. - - - - - CLOUDSTACK-1349 - - - "VPC network Adding Network ACls, PF rules - Unable to insert queue item into - database, DB is full? PF rules and NW Acls in Add state in DB - - - - - CLOUDSTACK-1350 - - - Management server Stop and start causes previously downloaded ISOs and - templates to redownload & reinstall. - - - - - CLOUDSTACK-1353 - - - KVM 6.3 snapshot Scheduling snapshot failed due to - java.lang.NullPointerException - - - - - CLOUDSTACK-1357 - - - "Autoscale: Provisioned VMs from Netscaler not being added to lb vserver, - provserver fails with provserver_err_asynctaskpoll - - - - - CLOUDSTACK-1360 - - - The clusterid field of the createStoragePool API command should be documented - as required. - - - - - CLOUDSTACK-1367 - - - NPE noticed in logs while AgentMonitor is monitoring the host ping - interval - - - - - CLOUDSTACK-1368 - - - Shared network - Not able to delete network because of - java.lang.NullPointerException - - - - - CLOUDSTACK-1369 - - - "Ipv6 - In dual Stack network, guest VM does not have the Ipv6 address of the - router programmed in /etc/resolv.conf for DNS resolution. - - - - - CLOUDSTACK-1370 - - - DeployVM Fail - VPC or non-VPC network - - - - - CLOUDSTACK-1375 - - - deploydb failing with acs master - - - - - CLOUDSTACK-1376 - - - Unable to migrate VM due to internal error process exited while connecting to - monitor - - - - - CLOUDSTACK-1377 - - - HA fail - when host is shutdown, VMs and SSVMs are not failover to second host - in cluster. - - - - - CLOUDSTACK-1382 - - - vm deploy fails with Error "cannot find DeployPlannerSelector for vm" - - - - - CLOUDSTACK-1383 - - - Deploying basic zone on 4.1 fails in NPE - - - - - CLOUDSTACK-1386 - - - BASIC zone SSVM fail to start due to exception - - - - - CLOUDSTACK-1388 - - - UI - ListUsers doesnt display any User except the Default Root Admin - User - - - - - CLOUDSTACK-1391 - - - EventBus is not getting injected after javelin merge - - - - - CLOUDSTACK-1394 - - - [F5-SRX-InlineMode] Failure in static nat configuration on SRX does not result - in LB configuration error in CS during LB rule configuration - - - - - CLOUDSTACK-1397 - - - Static Nat configuration is failing with NPE - - - - - CLOUDSTACK-1399 - - - Unhandled exception executing api command: stopVirtualMachine - - - - - CLOUDSTACK-1402 - - - listRouters API response doesn't return linklocal IP and public IP - details - - - - - CLOUDSTACK-1403 - - - Storage and console-proxy related error - - - - - CLOUDSTACK-1411 - - - Issues with VMWare Hypervisor host_ids not updated when ESX(i) crashes in - instance table - - - - - CLOUDSTACK-1414 - - - Redundant router: BACKUP switch cancelled due to lock timeout after a glitch - in network. - - - - - CLOUDSTACK-1417 - - - When invalid values are passed to createNetwork(), error message does not - indicate the parameter name that has invalid values. - - - - - CLOUDSTACK-1418 - - - As regular user, we are not allowed to deploy VM on a shared network. - - - - - CLOUDSTACK-1419 - - - Apache-ify and apply trademark logos in the UI - - - - - CLOUDSTACK-1420 - - - Ensure trademarks are properly attributed in publican brand - - - - - CLOUDSTACK-1423 - - - Unable to launch UI [HTTP Status 404]. - - - - - CLOUDSTACK-1425 - - - unhandled exception executing api command: migrateVirtualMachine & - recoverVirtualMachine - - - - - CLOUDSTACK-1427 - - - Failed to delete Guestnetwork which has LB with Netscaler - - - - - CLOUDSTACK-1428 - - - [UI] Instance which are created without display name are not visible when - added to LB - - - - - CLOUDSTACK-1429 - - - single account is unable to use same vnet across multiple physical - network - - - - - CLOUDSTACK-1436 - - - 4.1 management server fails to start from RPM build artifact - - - - - CLOUDSTACK-1443 - - - As domain admin we are allowed to create shared network - - - - - CLOUDSTACK-1446 - - - [UI]VPC Router type should be of type vpc and not system - - - - - CLOUDSTACK-1447 - - - [UI]Persistent Status is not displayed for VPC Tier - - - - - CLOUDSTACK-1449 - - - listAccounts and listProjectAccounts API lists all the users not - account-specific users for each account returned - - - - - CLOUDSTACK-1451 - - - Getting EntityExistsException while creating more than one project in CS - 4.1 - - - - - CLOUDSTACK-1452 - - - Public IP's are assigned to private interface with VPC Restart [PF/LB rules - are not functional - - - - - CLOUDSTACK-1461 - - - "Ipv6 - From a Vm that that is part of 2 networks, non default network - router's details should not get programmed in the DNS entries of the guest - VM. - - - - - CLOUDSTACK-1463 - - - IPV6 - Ubuntu 12.10 - Multiple Nic - IPV6 address is assigned automatically - for 1 nic only. Need to do a manual dhclient request to get the ipv6 for other - nic. - - - - - CLOUDSTACK-1464 - - - "IPV6 - Multi nic - Ubuntu 1210 -When Vm is stopped and started/ rebooted, i - get multiple global IPV6 addresses being allocated for one of the nics. - - - - - CLOUDSTACK-1465 - - - List Zones returns null under create instance when logged is as user - - - - - CLOUDSTACK-1467 - - - Failed to create Volume for the System VMs - - - - - CLOUDSTACK-1469 - - - kvm agent: agent service fails to start up - - - - - CLOUDSTACK-1470 - - - unhandled exception executing api command: deployVirtualMachine - - - - - CLOUDSTACK-1472 - - - AssignVirtualMachine API with wrong Virtual Instance ID failed with NPE - - - - - CLOUDSTACK-1473 - - - deleteDomain is failing with NPE - - - - - CLOUDSTACK-1481 - - - "IPV6 - When Vm is part of 1 dual network and 1 ipv6 network, name resolution - using fqdn fails for the ipv6 network. - - - - - CLOUDSTACK-1482 - - - IPV6 - We are not allowed to create a shared IPV6 network with a VLAN which - already is associated with a IPV4 network - - - - - CLOUDSTACK-1484 - - - API Throttling : api.throttling.enabled, Global setting missing - - - - - CLOUDSTACK-1485 - - - Add Baremetal Provider back to 4.1 branch - - - - - CLOUDSTACK-1487 - - - cloudstack-setup-agent fails to set private.network.device on KVM host - add - - - - - CLOUDSTACK-1488 - - - "Ipv6 - When Vm is deployed as part of multiple networks, one of the IPV6 - address assigned to guest VM is lost. - - - - - CLOUDSTACK-1490 - - - 4.1 deb management fails to start due to tomcat dep problem - - - - - CLOUDSTACK-1496 - - - List API Performance: listAccounts failing with OOME for high values of - pagesize (>1000) - - - - - CLOUDSTACK-1499 - - - ListAPI Performance for few APIs not as good as it was before API - optimization - - - - - CLOUDSTACK-1503 - - - listHypervisor API not getting fired when logged in as User - - - - - CLOUDSTACK-1505 - - - Unknown column 'domain.region_id' in 'field list' - - - - - CLOUDSTACK-1509 - - - Failed to implement network elements and resources while provisioning for - persistent network(createVlanIpRange to an account - - - - - CLOUDSTACK-1511 - - - [UI] Instances NIC details does not have Network Name - - - - - CLOUDSTACK-1512 - - - [UI] Wrong message[message.configure.all.traffic.types] when trying to create - zone with mulitple physical networks without providing the traffic label - - - - - CLOUDSTACK-1515 - - - None of the cloudstack packges are marked for upgrade when tried to upgrade - from.4.0/4.0.1 to 4.1 - - - - - CLOUDSTACK-1516 - - - Create documentation in languages that have translations available - - - - - CLOUDSTACK-1517 - - - Check UI in languages available - - - - - CLOUDSTACK-1521 - - - Redundant router: Services are not stopped when switch to BACKUP state - - - - - CLOUDSTACK-1526 - - - Template registration fails in the VMware Setup - - - - - CLOUDSTACK-1531 - - - vmware create volume from snapshot will missing date - - - - - CLOUDSTACK-1537 - - - Restart network with clean up set to true causes Autoscaled LB rule to get - mangled and unusable - - - - - CLOUDSTACK-1541 - - - NPE while deleting snapshot :Unexpected exception while executing - org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotCmd - - - - - CLOUDSTACK-1542 - - - unhandled exception while creating project - - - - - CLOUDSTACK-1544 - - - The description and the response format for the deleteUser command are - incorrect - - - - - CLOUDSTACK-1550 - - - createaccountresponse returns more than the user you requested for - creation - - - - - CLOUDSTACK-1553 - - - AWS Regions-Not able to list accounts from the 2nd region after - user/account/domain details have been manually synced up from first region - - - - - CLOUDSTACK-1555 - - - "AWS Regions - userapikey and usersecretkey parameters are not returned in the - response of addRegion, updateRegion listRegion api calls.. - - - - - CLOUDSTACK-1557 - - - EC2 REST API : cloudbridge database is missing on the CloudStack - Installation - - - - - CLOUDSTACK-1562 - - - Replace the short-cut solution of supportting @DB with the formal one - - - - - CLOUDSTACK-1565 - - - "Used Master Branch System VM Template: Default Route on the System VMs - (SSVM,CPVM and VR) is missing - - - - - CLOUDSTACK-1566 - - - Baremetal API addBaremetalPxePingServer fail to add PXE PING server to - deployment causing create instance with PING style image to fail - - - - - CLOUDSTACK-1569 - - - "AWS Regions - Not able to Edit domain/account/user from a region that is not - the owner region.""The content of elements must consist of well-formed character - data or markup."" - error message presented to the user. - - - - - CLOUDSTACK-1571 - - - "AWS Regions - When deleting domain/account/user from a region that is not the - owner, the request is not being forwarded to the owner region. - - - - - CLOUDSTACK-1574 - - - updateResourceCount API is failed saying to specify valida resource type even - after parsing the valid resource type - - - - - CLOUDSTACK-1583 - - - AWS Regions - RabbitMQ Server did not recieve any event notification during - account creation - - - - - CLOUDSTACK-1587 - - - Basic zone - CPVM fail to go to running state, Exception while trying to start - secondary storage vm - - - - - CLOUDSTACK-1588 - - - AWS Regions - When registerUserKeys() is called for a user from a region that - is not the owner, it is handled by this region. - - - - - CLOUDSTACK-1600 - - - Typo in dpkg-buildpackage command - - - - - CLOUDSTACK-1604 - - - deploy VM failed when global setting "vm.allocation.algorithm" is set to - "userdispersing - - - - - CLOUDSTACK-1615 - - - "VMware Cluster discovery fails with if ESXi version is 5.0 Update 1, build - 721882 - - - - - CLOUDSTACK-1620 - - - Cannot provision CentOS 6 VMs on XenServer 6.1 - - - - - CLOUDSTACK-1621 - - - listProjectInvitations fails with NPE for valid request - - - - - CLOUDSTACK-1624 - - - API is not returning response in details:UI is also not returning any - output - - - - - CLOUDSTACK-1625 - - - NPE with updateResourceCount when && is passed thru API - - - - - CLOUDSTACK-1630 - - - 4.0.x cloud-aws-api not properly obsoleted - - - - - CLOUDSTACK-1631 - - - 4.1 RPM packaging broken - - - - - CLOUDSTACK-1636 - - - AWS Regions - Remove the concept of having an owner region for - domain/account/user objects - - - - - CLOUDSTACK-1642 - - - Add support CentOS 6.4 - - - - - CLOUDSTACK-1648 - - - Unable to add KVM host. - - - - - CLOUDSTACK-1649 - - - vmware vm os type error - - - - - CLOUDSTACK-1651 - - - agent scripts still pointing to /var/log/cloud - - - - - CLOUDSTACK-1656 - - - NicResponses in a UserVmResponse are not preserving the natural order - - - - - CLOUDSTACK-1663 - - - AWS Regions - Events - There are no events being generated when a new domain - is added/edited - - - - - CLOUDSTACK-1664 - - - Action Events are not logged due to spring change - - - - - CLOUDSTACK-1665 - - - AWS Regions - Events - There are no events being generated when a new user is - added/edited/enabled/deleted/password changes/api & secret keys are - generated - - - - - CLOUDSTACK-1666 - - - KVM VPC NetworkUsage does not work - - - - - CLOUDSTACK-1668 - - - IP conflict in VPC tier - - - - - CLOUDSTACK-1671 - - - AWS Regions - Events - Domain Delete event does not include the UUID of the - domain that was deleted - - - - - CLOUDSTACK-1674 - - - AWS Regions - Events - Account Deletion event does not include the UUID of the - account deleted - - - - - CLOUDSTACK-1681 - - - Upgrade instructions mention incorrect name and description of systemvm-vmware - template in registering template section - - - - - CLOUDSTACK-1684 - - - "api.throttling.enabled configuration setting should be set to ""false"" in - Config.java - - - - - CLOUDSTACK-1688 - - - AWS Regions - Domain admin user is not able to use getUser() command to fetch - user details - - - - - CLOUDSTACK-1690 - - - NPE from API server when starting mgmt server - - - - - CLOUDSTACK-1694 - - - Issues to start/access Management Server after upgrade from 4.0 to 4.1 - - - - - CLOUDSTACK-1697 - - - Six DB tables are not available with upgraded setup(4.0 to 4.1) when compare - to 4.1 newly installation - - - - - CLOUDSTACK-1706 - - - Failed to deploy VM with error "cannot find DeployPlannerSelector" - - - - - CLOUDSTACK-1709 - - - AWS Regions - As part of adding a new region, project related entries should - not be synced from accounts table. - - - - - CLOUDSTACK-1710 - - - AWS Regions - As part of adding a new region,default_zone_id column for the - account entries should not be synced. - - - - - CLOUDSTACK-1711 - - - AWS Regions - Include all the details of the API call made in the Events - payload when changes in Admin/Account/User objects are made. - - - - - CLOUDSTACK-1713 - - - EC2 REST API: AWS API Installation Problem - - - - - CLOUDSTACK-1714 - - - Doc section has wrong title: Setting Zone VLAN and Running VM Maximum - - - - - CLOUDSTACK-1715 - - - "Missing ""host"" config setting in docs on management server load - balancing - - - - - CLOUDSTACK-1716 - - - "AWS Regions - listRegions(),removeRegions(),updateRegions() should accept - UUID value instead of id. - - - - - CLOUDSTACK-1718 - - - AWS Regions - removeRegion() response returns updateregionresponse - - - - - CLOUDSTACK-1719 - - - EC2 REST API: AWS APIs are not getting translated on the CloudStack Management - Server - - - - - CLOUDSTACK-1720 - - - Have an upgrade path from 4.0.x to 4.1 and 4.0.x to 4.2.0 - - - - - CLOUDSTACK-1729 - - - Ensure adapter execution order in runtime - - - - - CLOUDSTACK-1733 - - - [ACS41][UI] Add guest network is missing ip range fields and missing network - offering - - - - - CLOUDSTACK-1736 - - - Ubuntu 12.04 cloud-setup-management Failed to configure CloudStack Management - Server - - - - - CLOUDSTACK-1738 - - - StatsCollector is not running - - - - - CLOUDSTACK-1740 - - - Failed to view console - - - - - CLOUDSTACK-1746 - - - Cloudstack Usage Server won't start - - - - - CLOUDSTACK-1747 - - - "mvn deploydb only creates 4.0 DB, not 4.1 - - - - - CLOUDSTACK-1750 - - - injectkeys script fails on OSX because cp does not have a -b option (backup of - destination file - - - - - CLOUDSTACK-1761 - - - Available local storage disk capacity incorrectly reported in KVM to - manager - - - - - CLOUDSTACK-1764 - - - ListTemplateCommand failed with java.lang.NumberFormatException and failed to - create default template. - - - - - CLOUDSTACK-1772 - - - the change in vnc listening port will cause live migration doesn't - work. - - - - - CLOUDSTACK-1773 - - - Disable baremetal functionality - - - - - CLOUDSTACK-1776 - - - NPE on listSecondaryStorageHostsInAllZones in Upgraded setup from 4.0 to - 4.1.0 - - - - - CLOUDSTACK-1785 - - - Redundant Router test cases failing during automation run. - - - - - CLOUDSTACK-1789 - - - Unable to download templates to Primary Storage if a host is in - maintenance. - - - - - CLOUDSTACK-1791 - - - Volumes with storage tags can't be attached. - - - - - CLOUDSTACK-1792 - - - "AWS Regions - RuntimeException while executing listAccounts(), when the - encryption keys are set to different values between regions. - - - - - CLOUDSTACK-1793 - - - L10n docs don't build in chinese, portuguese and japanese - - - - - CLOUDSTACK-1795 - - - Customize AOP to fully support legacy CloudStack @DB and @ActionEvent - semantics. - - - - - CLOUDSTACK-1796 - - - Japanese docs don't build. - - - - - CLOUDSTACK-1802 - - - Upgrade 4.0 -> 4.1 - Not able to start management server becasue of missing - /etc/cloudstack/management/tomcat6.conf file - - - - - CLOUDSTACK-1804 - - - Upgrade 4.0 -> 4.1 - DB upgrade fails - - - - - CLOUDSTACK-1805 - - - com.mysql.jdbc.exceptions.jdbc4.CommunicationsException seen after long time - of inactivity resulting in not being able to log in to the management - server - - - - - CLOUDSTACK-1810 - - - listTemplate API with templatefilter=featured|community is not returning any - lists - - - - - CLOUDSTACK-1811 - - - "Upgrade 4.0->4.1 - When upgrade scripts fail, component loading continues and - management server starts. - - - - - CLOUDSTACK-1812 - - - create physical network fails while creating basic zone - - - - - CLOUDSTACK-1825 - - - EC2 REST API: AWS APIs fail to execute due to BeanCreationException: Error - creating bean with name 'SAclDaoImpl' - - - - - CLOUDSTACK-1826 - - - "Storage migration not working, seemingly due to uuid vs id - - - - - CLOUDSTACK-1827 - - - Redundant router - When VR Master was stopped failover to VR Backup did not - occur. - - - - - CLOUDSTACK-1834 - - - "Events are not generated for registerUserKeys(), Enabling account and Editing - account. - - - - - CLOUDSTACK-1836 - - - License header failures for ja-JP .po translation file - - - - - CLOUDSTACK-1839 - - - Upgrade 4.0 -> 4.1 - Upgraded DB has lot more keys and indexes for many tables - compare to the fresh installed 4.1 DB - - - - - CLOUDSTACK-1841 - - - ASF 4.0 to 4.1 Upgrade: Missing Few Global Configuration parameters on the - Upgraded Setup. - - - - - CLOUDSTACK-1842 - - - ASF 4.0 to 4.1 Upgrade: Missing Ubuntu 12.04 Guest OS Types on the Upgraded - Setup. - - - - - CLOUDSTACK-1844 - - - Upgrade 4.0 -> 4.1 - KVM host agent.properties is not restored as part of - upgrading the binaries from 4.0 to 4.1. - - - - - CLOUDSTACK-1845 - - - KVM - storage migration often fails - - - - - CLOUDSTACK-1846 - - - "KVM - storage pools can silently fail to be unregistered, leading to failure - to register later. - - - - - CLOUDSTACK-1848 - - - Cloudstack Packages are not got updated with scenario 4.0 to 4.1 upgrade where - MS is on Ubuntu 12.04. - - - - - CLOUDSTACK-1856 - - - Upgrade 4.0 -> 4.1 - Fresh install of 4.1 has 3 parameters missing in - db.properties compared to an upgraded 4.0 setup - - - - - CLOUDSTACK-1873 - - - "Installation : JasyptPBEStringDecryptionCLI missing, failed to decrypt db - password - - - - - CLOUDSTACK-1874 - - - AWS Regions - Account table in cloud_usage DB has region_id - - - - - CLOUDSTACK-1876 - - - External Devices - network offering for external devices is not returned in - API listNetworkOfferings when creating instances. - - - - - CLOUDSTACK-1877 - - - Failed to connect to DB while starting Ubuntu management server after - upgrading the packages from 4.0 to 4.1.0 - - - - - CLOUDSTACK-1882 - - - “HTTP Status 404 。 The requested resource () is not available. - - - - - CLOUDSTACK-1890 - - - listProjects is not listing state in the response - - - - - CLOUDSTACK-1900 - - - "Upgrade 4.0 -> 4.1, We do not have a copy of db.properties that comes from a - 4.1 installation saved anywhere. - - - - - CLOUDSTACK-1929 - - - ASF 4.1 cloudstack agent fail to install in KVM host CENTOS 6.3 OS: - qemu-kvm-0.12.1.2-3.295.el6.10.x86_64 requires libusbredirparser.so.0 - - - - - CLOUDSTACK-1934 - - - NPE with listSupportedNetworkServices after upgrade from 4.0 to 4.1 (Ubuntu - MS) - - - - - CLOUDSTACK-1935 - - - Cloud utilities are not renamed to Cloudstack after upgrade from 4.0 to 4.1 - [Ubutnu MS] - - - - - CLOUDSTACK-1936 - - - On CentOS, after a upgrade from 4.0.1 to 4.1 on a cloud node (cloud-agent), - the new cloustack-agent isn't add as a service (chkconfig) - - - - - CLOUDSTACK-1951 - - - centos packaging: cloud-install-sys-tmplt can't find jasypt jar. - - - - - CLOUDSTACK-1971 - - - VM deployed to incorrect primary storage. - - - - - CLOUDSTACK-1972 - - - VM deployed to incorrect primary storage. - - - - - CLOUDSTACK-1978 - - - openvswitch - unable to start console session for SSVM CPVM user VM - - - - - CLOUDSTACK-1980 - - - "[4.1]cloudstack-setup-bridge, cloudstack-setup-encryption & - cloudstack-sysvmadm utilities are not available in Ubuntu 12.04 Management - Server. - - - - - CLOUDSTACK-1987 - - - Deleted service offerings owned by a domain show up to domain user. - - - - - CLOUDSTACK-1988 - - - AWS API using SOAP client - User Registration fails - - - - - CLOUDSTACK-1989 - - - "Query service offering by ID returns no result, but querying all returns - service offering - - - - - CLOUDSTACK-2003 - - - Deleting domain while deleted account is cleaning up leaves VMs expunging - forever due to 'Failed to update resource count - - - - - CLOUDSTACK-2007 - - - Release Notes failing to build on jenkins.cs. - - - - - -
    -
    - Known Issues in 4.1.0 - - - - - - - - Issue ID - - - Description - - - - - - CLOUDSTACK-2709 - - Egress rules are are not supported on shared networks. - - - - 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 - - - - - -
    -
    - - Upgrade Instructions for 4.1 - 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@cloudstack.apache.org or dev@cloudstack.apache.org. -
    - Upgrade from 4.0.x to 4.1.0 - This section will guide you from &PRODUCT; 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. - 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 - - - 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 - - - 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. - - - After upgrading to 4.1, API clients are expected to send plain text passwords for - login and user creation, instead of MD5 hash. Incase, api client changes are not - acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default - authenticator (1st entry in the userAuthenticators adapter list is default) - -<!-- Security adapters --> -<bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> - <property name="Adapters"> - <list> - <ref bean="PlainTextUserAuthenticator"/> - <ref bean="MD5UserAuthenticator"/> - <ref bean="LDAPUserAuthenticator"/> - </list> - </property> -</bean> - - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to - 4.1. - - - 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 - - - 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: - $ 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: - -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 - - - - - If you are using CentOS or RHEL, 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 yum repository, substitute your own URL for - the ones used in these examples. - - - - The first order of business will be to change the yum repository 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/yum.repos.d/cloudstack.repo on any - systems that have &PRODUCT; packages installed. - This file should have content similar to the following: - -[apache-cloudstack] -name=Apache CloudStack -baseurl=http://cloudstack.apt-get.eu/rhel/4.0/ -enabled=1 -gpgcheck=0 - - If you are using the community provided package repository, change the baseurl - to http://cloudstack.apt-get.eu/rhel/4.1/ - If you're using your own package repository, change this line to read as - appropriate for your 4.1.0 repository. - - - Now that you have the repository configured, it's time to install the - cloudstack-management package by upgrading the older - cloud-client package. - $ sudo yum upgrade cloud-client - - - For KVM hosts, you will need to upgrade the cloud-agent - package, similarly installing the new version as - cloudstack-agent. - $ sudo yum upgrade cloud-agent - During the installation of cloudstack-agent, the RPM will - copy your agent.properties, - log4j-cloud.xml, and - environment.properties from - /etc/cloud/agent to - /etc/cloudstack/agent. - - - 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: - -service cloud-agent stop -killall jsvc -service cloudstack-agent start - - - - - - Once you've upgraded the packages on your management servers, you'll need to restart - the system VMs. Make sure port 8096 is open in your local host firewall 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. - - -
    -
    - 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. - - - - 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.1 - - - Description - systemvm-vmware-4.1 - - - 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. - - - 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 - - - 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: - $ 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: - -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 - - - - - If you are using CentOS or RHEL, 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 yum repository, substitute your own URL for - the ones used in these examples. - - - - The first order of business will be to change the yum repository 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/yum.repos.d/cloudstack.repo on any - systems that have &PRODUCT; packages installed. - This file should have content similar to the following: - -[apache-cloudstack] -name=Apache CloudStack -baseurl=http://cloudstack.apt-get.eu/rhel/4.0/ -enabled=1 -gpgcheck=0 - - If you are using the community provided package repository, change the baseurl - to http://cloudstack.apt-get.eu/rhel/4.1/ - If you're using your own package repository, change this line to read as - appropriate for your 4.1.0 repository. - - - Now that you have the repository configured, it's time to install the - cloudstack-management package by upgrading the older - cloud-client package. - $ sudo yum upgrade cloud-client - - - For KVM hosts, you will need to upgrade the cloud-agent - package, similarly installing the new version as - cloudstack-agent. - $ sudo yum upgrade cloud-agent - During the installation of cloudstack-agent, the RPM will - copy your agent.properties, - log4j-cloud.xml, and - environment.properties from - /etc/cloud/agent to - /etc/cloudstack/agent. - - - 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: - -service cloud-agent stop -killall jsvc -service cloudstack-agent start - - - - - - 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. - - - - After upgrading to 4.1, API clients are expected to send plain text passwords for - login and user creation, instead of MD5 hash. Incase, api client changes are not - acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default - authenticator (1st entry in the userAuthenticators adapter list is default) - -<!-- Security adapters --> -<bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> - <property name="Adapters"> - <list> - <ref bean="PlainTextUserAuthenticator"/> - <ref bean="MD5UserAuthenticator"/> - <ref bean="LDAPUserAuthenticator"/> - </list> - </property> -</bean> - - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to - 4.1. - - - Start the first Management Server. Do not start any other Management Server nodes - yet. - # service cloudstack-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 cloudstack-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 cloudstack-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. - - - - 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 - - - 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 - - - On KVM hosts, 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: - -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 - - - - - If you are using CentOS or RHEL, 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 yum repository, substitute your own URL for - the ones used in these examples. - - - - The first order of business will be to change the yum repository 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/yum.repos.d/cloudstack.repo on any - systems that have &PRODUCT; packages installed. - This file should have content similar to the following: - -[apache-cloudstack] -name=Apache CloudStack -baseurl=http://cloudstack.apt-get.eu/rhel/4.0/ -enabled=1 -gpgcheck=0 - - If you are using the community provided package repository, change the baseurl - to http://cloudstack.apt-get.eu/rhel/4.1/ - If you're using your own package repository, change this line to read as - appropriate for your 4.1.0 repository. - - - Now that you have the repository configured, it's time to install the - cloudstack-management package by upgrading the older - cloud-client package. - $ sudo yum upgrade cloud-client - - - For KVM hosts, you will need to upgrade the cloud-agent - package, similarly installing the new version as - cloudstack-agent. - $ sudo yum upgrade cloud-agent - During the installation of cloudstack-agent, the RPM will - copy your agent.properties, - log4j-cloud.xml, and - environment.properties from - /etc/cloud/agent to - /etc/cloudstack/agent. - - - 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: - -service cloud-agent stop -killall jsvc -service cloudstack-agent start - - - - - - 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 - - - - - - After upgrading to 4.1, API clients are expected to send plain text passwords for - login and user creation, instead of MD5 hash. Incase, api client changes are not - acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default - authenticator (1st entry in the userAuthenticators adapter list is default) - -<!-- Security adapters --> -<bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> - <property name="Adapters"> - <list> - <ref bean="PlainTextUserAuthenticator"/> - <ref bean="MD5UserAuthenticator"/> - <ref bean="LDAPUserAuthenticator"/> - </list> - </property> -</bean> - - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to - 4.1. - - - 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 cloudstack-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 cloudstack-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 cloudstack-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." - - - - -
    -
    - From d58044ccbaa49d011d4e5b72f8e96a8619b224fd Mon Sep 17 00:00:00 2001 From: frank Date: Thu, 12 Sep 2013 14:14:23 -0700 Subject: [PATCH 168/251] Add Baremetal agent package back to RPM spec file --- packaging/centos63/cloud.spec | 16 ++ .../networkservice/BaremetalDhcpElement.java | 11 +- .../BaremetalDhcpManagerImpl.java | 7 +- python/bindir/cloud-setup-baremetal | 217 ------------------ 4 files changed, 23 insertions(+), 228 deletions(-) delete mode 100755 python/bindir/cloud-setup-baremetal diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 2b814f871df..5f8a2a50d16 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -162,6 +162,19 @@ Group: System Environment/Libraries %description awsapi Apache Cloudstack AWS API compatibility wrapper + +%package baremetal-agent +Summary: CloudStack baremetal agent +Requires: tftp-server +Requires: xinetd +Requires: syslinux +Requires: chkconfig +Requires: dhcp +Requires: httpd +Group: System Environment/Libraries +%description baremetal-agent +The CloudStack baremetal agent + %prep echo Doing CloudStack build @@ -236,6 +249,7 @@ install -D client/target/utilities/bin/cloud-set-guest-sshkey ${RPM_BUILD_ROOT}% install -D client/target/utilities/bin/cloud-setup-databases ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-databases install -D client/target/utilities/bin/cloud-setup-encryption ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-encryption install -D client/target/utilities/bin/cloud-setup-management ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-management +install -D client/target/utilities/bin/cloud-setup-baremetal ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-baremetal install -D client/target/utilities/bin/cloud-sysvmadm ${RPM_BUILD_ROOT}%{_bindir}/%{name}-sysvmadm install -D client/target/utilities/bin/cloud-update-xenserver-licenses ${RPM_BUILD_ROOT}%{_bindir}/%{name}-update-xenserver-licenses @@ -603,6 +617,8 @@ fi %{_defaultdocdir}/%{name}-awsapi-%{version}/LICENSE %{_defaultdocdir}/%{name}-awsapi-%{version}/NOTICE +%files baremetal-agent +%attr(0755,root,root) %{_bindir}/cloudstack-setup-baremetal %changelog * Fri Oct 03 2012 Hugo Trippaers 4.1.0 diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java index 34e83027da7..f01deb7430b 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java @@ -65,8 +65,8 @@ public class BaremetalDhcpElement extends AdapterBase implements DhcpServiceProv static { Capability cap = new Capability(BaremetalDhcpManager.BAREMETAL_DHCP_SERVICE_CAPABITLITY); Map baremetalCaps = new HashMap(); - baremetalCaps.put(cap, null); - baremetalCaps.put(Capability.DhcpAccrossMultipleSubnets, Boolean.TRUE.toString()); + baremetalCaps.put(cap, null); + baremetalCaps.put(Capability.DhcpAccrossMultipleSubnets, Boolean.TRUE.toString()); capabilities = new HashMap>(); capabilities.put(Service.Dhcp, baremetalCaps); } @@ -82,11 +82,8 @@ public class BaremetalDhcpElement extends AdapterBase implements DhcpServiceProv } private boolean canHandle(DeployDestination dest, TrafficType trafficType, GuestType networkType) { - Pod pod = dest.getPod(); - if (pod != null && dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { - SearchCriteriaService sc = SearchCriteria2.create(BaremetalDhcpVO.class); - sc.addAnd(sc.getEntity().getPodId(), Op.EQ, pod.getId()); - return sc.find() != null; + if (dest.getDataCenter().getNetworkType() == NetworkType.Basic && trafficType == TrafficType.Guest) { + return true; } return false; diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java index 9d18478626b..2f4ffcb6993 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpManagerImpl.java @@ -136,14 +136,13 @@ public class BaremetalDhcpManagerImpl extends ManagerBase implements BaremetalDh public boolean addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException { Long zoneId = profile.getVirtualMachine().getDataCenterId(); - Long podId = profile.getVirtualMachine().getPodIdToDeployIn(); - List hosts = _resourceMgr.listAllUpAndEnabledHosts(Type.BaremetalDhcp, null, podId, zoneId); + List hosts = _resourceMgr.listAllUpAndEnabledHosts(Type.BaremetalDhcp, null, null, zoneId); if (hosts.size() == 0) { - throw new CloudRuntimeException("No external Dhcp found in zone " + zoneId + " pod " + podId); + throw new CloudRuntimeException("No external Dhcp found in zone " + zoneId); } if (hosts.size() > 1) { - throw new CloudRuntimeException("Something wrong, more than 1 external Dhcp found in zone " + zoneId + " pod " + podId); + throw new CloudRuntimeException("Something wrong, more than 1 external Dhcp found in zone " + zoneId); } HostVO h = hosts.get(0); diff --git a/python/bindir/cloud-setup-baremetal b/python/bindir/cloud-setup-baremetal deleted file mode 100755 index 03ea0864213..00000000000 --- a/python/bindir/cloud-setup-baremetal +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -import sys, os -from subprocess import PIPE, Popen -import logging -import traceback -from os.path import exists, join -from signal import alarm, signal, SIGALRM, SIGKILL - -class CloudRuntimeException(Exception): - def __init__(self, errMsg): - self.errMsg = errMsg - def __str__(self): - return self.errMsg -def formatExceptionInfo(maxTBlevel=5): - cla, exc, trbk = sys.exc_info() - excTb = traceback.format_tb(trbk, maxTBlevel) - msg = str(exc) + "\n" - for tb in excTb: - msg += tb - return msg - -class bash: - def __init__(self, args, timeout=600): - self.args = args - logging.debug("execute:%s"%args) - self.timeout = timeout - self.process = None - self.success = False - self.run() - - def run(self): - class Alarm(Exception): - pass - def alarm_handler(signum, frame): - raise Alarm - - try: - self.process = Popen(self.args, shell=True, stdout=PIPE, stderr=PIPE) - if self.timeout != -1: - signal(SIGALRM, alarm_handler) - alarm(self.timeout) - - try: - self.stdout, self.stderr = self.process.communicate() - if self.timeout != -1: - alarm(0) - except Alarm: - os.kill(self.process.pid, SIGKILL) - raise CloudRuntimeException("Timeout during command execution") - - self.success = self.process.returncode == 0 - except: - raise CloudRuntimeException(formatExceptionInfo()) - -# if not self.success: -# raise CloudRuntimeException(self.getStderr()) - - def isSuccess(self): - return self.success - - def getStdout(self): - return self.stdout.strip("\n") - - def getLines(self): - return self.stdout.split("\n") - - def getStderr(self): - return self.stderr.strip("\n") - - -def initLoging(logFile=None): - try: - if logFile is None: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(filename=logFile, level=logging.DEBUG) - except: - logging.basicConfig(level=logging.DEBUG) - -def writeProgressBar(msg, result=None): - if msg is not None: - output = "%-80s"%msg - elif result is True: - output = "[ \033[92m%-2s\033[0m ]\n"%"OK" - elif result is False: - output = "[ \033[91m%-6s\033[0m ]\n"%"FAILED" - sys.stdout.write(output) - sys.stdout.flush() - -def printError(msg): - sys.stderr.write(msg) - sys.stderr.write("\n") - sys.stderr.flush() - -def printMsg(msg): - sys.stdout.write(msg+"\n") - sys.stdout.flush() - -def checkRpm(pkgName): - chkPkg = bash("rpm -q %s"%pkgName) - writeProgressBar("Checking %s"%pkgName, None) - if not chkPkg.isSuccess(): - writeProgressBar(None, False) - printError("%s is not found, please make sure it is installed. You may try 'yum install %s'\n"%(pkgName, pkgName)) - return False - else: - writeProgressBar(None, True) - return True - -def checkEnv(): - writeProgressBar("Checking is root") - ret = bash("whoami") - if ret.getStdout() != "root": - writeProgressBar(None, False) - printError("This script must run as root") - return False - else: - writeProgressBar(None, True) - - pkgList = ['tftp-server', 'syslinux', 'xinetd', 'chkconfig', 'dhcp'] - for pkg in pkgList: - if not checkRpm(pkg): - return False - return True - -def exitIfFail(ret): - if not ret: sys.exit(1) - -def bashWithResult(cmd): - writeProgressBar("Executing '%s'"%cmd) - ret = bash(cmd) - if not ret.isSuccess(): - writeProgressBar(None, False) - writeProgressBar(ret.getStderr() + '\n') - return False - else: - writeProgressBar(None, True) - return True - -def configurePxeStuff(): - stuff = ['tftp', 'xinetd', 'dhcpd'] - cmds = ['chkconfig --level 345 %s on' % i for i in stuff] - cmds.append('/etc/init.d/xinetd restart') - - for cmd in cmds: - if not bashWithResult(cmd): return False - - chkIptable = bash('chkconfig --list iptables') - if 'on' in chkIptable.getStdout(): - printMsg("Detected iptables is running, need to open tftp port 69") - if not bashWithResult('iptables -I INPUT 1 -p udp --dport 69 -j ACCEPT'): return False - if not bashWithResult('/etc/init.d/iptables save'): return False - - return True - -def getTftpRootDir(tftpRootDirList): - tftpRoot = bash("cat /etc/xinetd.d/tftp | grep server_args") - if not tftpRoot.isSuccess(): - printError("Cannot get tftp root directory from /etc/xinetd.d/tftp, here may be something wrong with your tftp-server, try reinstall it\n") - return False - tftpRootDir = tftpRoot.getStdout() - index = tftpRootDir.find("/") - if index == -1: - printError("Wrong server_arg in /etc/xinetd.d/tftp (%s)"%tftpRootDir) - return False - tftpRootDir = tftpRootDir[index:] - tftpRootDirList.append(tftpRootDir) - return True - -def preparePING(tftpRootDir): - pingFiles = ['boot.msg', 'initrd.gz', 'kernel', 'pxelinux.0'] - pingDir = "/usr/share/PING" - - for f in pingFiles: - path = join(pingDir, f) - if not exists(path): - printError("Cannot find %s, please make sure PING-3.01 is installed"%path) - return False - if not bashWithResult("cp -f %s %s"%(path, tftpRootDir)): return False - - if not bashWithResult("mkdir -p %s/pxelinux.cfg"%tftpRootDir): return False - - return True - - -if __name__ == "__main__": - initLoging("/tmp/cloud-setup-baremetal.log") - tftpRootDirList = [] - - exitIfFail(checkEnv()) - exitIfFail(configurePxeStuff()) - exitIfFail(getTftpRootDir(tftpRootDirList)) - - tftpRootDir = tftpRootDirList[0].strip() - exitIfFail(preparePING(tftpRootDir)) - printMsg("") - printMsg("Setup BareMetal PXE server successfully") - printMsg("TFTP root directory is: %s\n"%tftpRootDir) - sys.exit(0) - From 4c1a20fe6650a7f75673df92a26925d4bfa1815d Mon Sep 17 00:00:00 2001 From: frank Date: Thu, 12 Sep 2013 14:47:37 -0700 Subject: [PATCH 169/251] Add Baremetal agent package back to RPM spec file add missing file in d58044ccbaa49d011d4e5b72f8e96a8619b224fd --- setup/bindir/cloud-setup-baremetal.in | 232 ++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 setup/bindir/cloud-setup-baremetal.in diff --git a/setup/bindir/cloud-setup-baremetal.in b/setup/bindir/cloud-setup-baremetal.in new file mode 100644 index 00000000000..367e38943e2 --- /dev/null +++ b/setup/bindir/cloud-setup-baremetal.in @@ -0,0 +1,232 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import sys, os +from subprocess import PIPE, Popen +import logging +import traceback +from os.path import exists, join +from signal import alarm, signal, SIGALRM, SIGKILL + +class CloudRuntimeException(Exception): + def __init__(self, errMsg): + self.errMsg = errMsg + def __str__(self): + return self.errMsg +def formatExceptionInfo(maxTBlevel=5): + cla, exc, trbk = sys.exc_info() + excTb = traceback.format_tb(trbk, maxTBlevel) + msg = str(exc) + "\n" + for tb in excTb: + msg += tb + return msg + +class bash: + def __init__(self, args, timeout=600): + self.args = args + logging.debug("execute:%s"%args) + self.timeout = timeout + self.process = None + self.success = False + self.run() + + def run(self): + class Alarm(Exception): + pass + def alarm_handler(signum, frame): + raise Alarm + + try: + self.process = Popen(self.args, shell=True, stdout=PIPE, stderr=PIPE) + if self.timeout != -1: + signal(SIGALRM, alarm_handler) + alarm(self.timeout) + + try: + self.stdout, self.stderr = self.process.communicate() + if self.timeout != -1: + alarm(0) + except Alarm: + os.kill(self.process.pid, SIGKILL) + raise CloudRuntimeException("Timeout during command execution") + + self.success = self.process.returncode == 0 + except: + raise CloudRuntimeException(formatExceptionInfo()) + +# if not self.success: +# raise CloudRuntimeException(self.getStderr()) + + def isSuccess(self): + return self.success + + def getStdout(self): + return self.stdout.strip("\n") + + def getLines(self): + return self.stdout.split("\n") + + def getStderr(self): + return self.stderr.strip("\n") + + +def initLoging(logFile=None): + try: + if logFile is None: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(filename=logFile, level=logging.DEBUG) + except: + logging.basicConfig(level=logging.DEBUG) + +def writeProgressBar(msg, result=None): + if msg is not None: + output = "%-80s"%msg + elif result is True: + output = "[ \033[92m%-2s\033[0m ]\n"%"OK" + elif result is False: + output = "[ \033[91m%-6s\033[0m ]\n"%"FAILED" + sys.stdout.write(output) + sys.stdout.flush() + +def printError(msg): + sys.stderr.write(msg) + sys.stderr.write("\n") + sys.stderr.flush() + +def printMsg(msg): + sys.stdout.write(msg+"\n") + sys.stdout.flush() + +def checkRpm(pkgName): + chkPkg = bash("rpm -q %s"%pkgName) + writeProgressBar("Checking %s"%pkgName, None) + if not chkPkg.isSuccess(): + writeProgressBar(None, False) + printError("%s is not found, please make sure it is installed. You may try 'yum install %s'\n"%(pkgName, pkgName)) + return False + else: + writeProgressBar(None, True) + return True + +def checkEnv(): + writeProgressBar("Checking is root") + ret = bash("whoami") + if ret.getStdout() != "root": + writeProgressBar(None, False) + printError("This script must run as root") + return False + else: + writeProgressBar(None, True) + + pkgList = ['tftp-server', 'syslinux', 'xinetd', 'chkconfig', 'dhcp'] + for pkg in pkgList: + if not checkRpm(pkg): + return False + return True + +def exitIfFail(ret): + if not ret: sys.exit(1) + +def bashWithResult(cmd): + writeProgressBar("Executing '%s'"%cmd) + ret = bash(cmd) + if not ret.isSuccess(): + writeProgressBar(None, False) + writeProgressBar(ret.getStderr() + '\n') + return False + else: + writeProgressBar(None, True) + return True + +def configurePxeStuff(): + stuff = ['tftp', 'xinetd', 'dhcpd'] + cmds = ['chkconfig --level 345 %s on' % i for i in stuff] + cmds.append('/etc/init.d/xinetd restart') + + for cmd in cmds: + if not bashWithResult(cmd): return False + + chkIptable = bash('chkconfig --list iptables') + if 'on' in chkIptable.getStdout(): + printMsg("Detected iptables is running, need to open tftp port 69") + if not bashWithResult('iptables -I INPUT 1 -p udp --dport 69 -j ACCEPT'): return False + if not bashWithResult('/etc/init.d/iptables save'): return False + + return True + +def getTftpRootDir(tftpRootDirList): + tftpRoot = bash("cat /etc/xinetd.d/tftp | grep server_args") + if not tftpRoot.isSuccess(): + printError("Cannot get tftp root directory from /etc/xinetd.d/tftp, here may be something wrong with your tftp-server, try reinstall it\n") + return False + tftpRootDir = tftpRoot.getStdout() + index = tftpRootDir.find("/") + if index == -1: + printError("Wrong server_arg in /etc/xinetd.d/tftp (%s)"%tftpRootDir) + return False + tftpRootDir = tftpRootDir[index:] + tftpRootDirList.append(tftpRootDir) + return True + +def preparePING(tftpRootDir): + pingFiles = ['boot.msg', 'initrd.gz', 'kernel', 'pxelinux.0'] + pingDir = "/usr/share/PING" + + for f in pingFiles: + path = join(pingDir, f) + if not exists(path): + printError("Cannot find %s, please make sure PING-3.01 is installed"%path) + return False + if not bashWithResult("cp -f %s %s"%(path, tftpRootDir)): return False + + if not bashWithResult("mkdir -p %s/pxelinux.cfg"%tftpRootDir): return False + + return True + +def prepareSyslinux(tftpRootDir): + pkg = bash('rpm -ql syslinux | grep "/pxelinux.0$"') + if not pkg.isSuccess(): + printError('cannot find pxelinux.0 on system. is syslinux installed?') + return False + + pkg = pkg.getStdout() + cp = "cp -f %s %s" % (pkg, tftpRootDir) + if not bashWithResult(cp): + return False + + return True + + + +if __name__ == "__main__": + initLoging("/tmp/cloud-setup-baremetal.log") + tftpRootDirList = [] + + exitIfFail(checkEnv()) + exitIfFail(configurePxeStuff()) + exitIfFail(getTftpRootDir(tftpRootDirList)) + + tftpRootDir = tftpRootDirList[0].strip() + #exitIfFail(preparePING(tftpRootDir)) + exitIfFail(prepareSyslinux(tftpRootDir)) + printMsg("") + printMsg("Setup BareMetal PXE server successfully") + printMsg("TFTP root directory is: %s\n"%tftpRootDir) + sys.exit(0) + From 52a45ce56e4607cda8bf365b7f6a8d26535f5163 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 12 Sep 2013 16:02:20 -0700 Subject: [PATCH 170/251] CLOUDSTACK-4650: change volume state during snapshot only --- .../snapshot/XenserverSnapshotStrategy.java | 21 +++++++++++++++---- .../storage/volume/VolumeServiceImpl.java | 11 ---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 93d6a3ca5b3..a58ababbed9 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.storage.snapshot; import javax.inject.Inject; +import com.cloud.storage.Volume; import com.cloud.utils.db.DB; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; @@ -253,11 +254,23 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { } try { - SnapshotResult result = snapshotSvr.takeSnapshot(snapshot); - if (result.isFailed()) { - s_logger.debug("Failed to take snapshot: " + result.getResult()); - throw new CloudRuntimeException(result.getResult()); + VolumeInfo volumeInfo = snapshot.getBaseVolume(); + volumeInfo.stateTransit(Volume.Event.SnapshotRequested); + SnapshotResult result = null; + try { + result = snapshotSvr.takeSnapshot(snapshot); + if (result.isFailed()) { + s_logger.debug("Failed to take snapshot: " + result.getResult()); + throw new CloudRuntimeException(result.getResult()); + } + } finally { + if (result != null && result.isSuccess()) { + volumeInfo.stateTransit(Volume.Event.OperationSucceeded); + } else { + volumeInfo.stateTransit(Volume.Event.OperationFailed); + } } + snapshot = result.getSnashot(); DataStore primaryStore = snapshot.getDataStore(); diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 2c592b25635..c93dcee8def 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -1297,22 +1297,11 @@ public class VolumeServiceImpl implements VolumeService { @Override public SnapshotInfo takeSnapshot(VolumeInfo volume) { - VolumeObject vol = (VolumeObject) volume; - boolean result = vol.stateTransit(Volume.Event.SnapshotRequested); - if (!result) { - s_logger.debug("Failed to transit state"); - } SnapshotInfo snapshot = null; try { snapshot = snapshotMgr.takeSnapshot(volume); } catch (Exception e) { s_logger.debug("Take snapshot: " + volume.getId() + " failed", e); - } finally { - if (snapshot != null) { - vol.stateTransit(Volume.Event.OperationSucceeded); - } else { - vol.stateTransit(Volume.Event.OperationFailed); - } } return snapshot; From 85c5f51fd05d94d1a4712ba4092a71fa1fee8b7a Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Thu, 12 Sep 2013 09:41:21 -0700 Subject: [PATCH 171/251] Revert "CLOUDSTACK-4645: There is no upgrade path from 4.1.1 to 4.2.0" This reverts commit 116705744135d11e13d4f5e37a1c4449108f8cff. Description: The fix can be much simpler - will check it in in subsequent commit. --- .../cloud/upgrade/DatabaseUpgradeChecker.java | 101 ++++++------------ .../cloud/upgrade/dao/Upgrade410to411.java | 77 ------------- ...rade411to420.java => Upgrade410to420.java} | 14 +-- ...leanup.sql => schema-410to420-cleanup.sql} | 0 ...chema-411to420.sql => schema-410to420.sql} | 0 5 files changed, 41 insertions(+), 151 deletions(-) delete mode 100644 engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java rename engine/schema/src/com/cloud/upgrade/dao/{Upgrade411to420.java => Upgrade410to420.java} (99%) rename setup/db/db/{schema-411to420-cleanup.sql => schema-410to420-cleanup.sql} (100%) rename setup/db/db/{schema-411to420.sql => schema-410to420.sql} (100%) diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index 84ea7f67ff9..723205869f7 100755 --- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -61,8 +61,7 @@ import com.cloud.upgrade.dao.Upgrade306to307; import com.cloud.upgrade.dao.Upgrade307to410; import com.cloud.upgrade.dao.Upgrade30to301; import com.cloud.upgrade.dao.Upgrade40to41; -import com.cloud.upgrade.dao.Upgrade410to411; -import com.cloud.upgrade.dao.Upgrade411to420; +import com.cloud.upgrade.dao.Upgrade410to420; import com.cloud.upgrade.dao.UpgradeSnapshot217to224; import com.cloud.upgrade.dao.UpgradeSnapshot223to224; import com.cloud.upgrade.dao.VersionDao; @@ -89,140 +88,108 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.1.8", new DbUpgrade[] { new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.1.9", new DbUpgrade[] { new Upgrade218to22(), new Upgrade221to222(), new UpgradeSnapshot217to224(), new Upgrade222to224(), new Upgrade218to224DomainVlans(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.1", new DbUpgrade[] { new Upgrade221to222(), new UpgradeSnapshot223to224(), new Upgrade222to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.2", new DbUpgrade[] { new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.3", new DbUpgrade[] { new Upgrade222to224(), new UpgradeSnapshot223to224(), new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.4", new DbUpgrade[] { new Upgrade224to225(), new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.5", new DbUpgrade[] { new Upgrade225to226(), new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.6", new DbUpgrade[] { new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.7", new DbUpgrade[] { new Upgrade227to228(), new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.8", new DbUpgrade[] { new Upgrade228to229(), new Upgrade229to2210(), new Upgrade2210to2211(), - new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30() + , new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.9", new DbUpgrade[] { new Upgrade229to2210(), new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.10", new DbUpgrade[] { new Upgrade2210to2211(), new Upgrade2211to2212(), new Upgrade2212to2213(), - new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.11", new DbUpgrade[] { new Upgrade2211to2212(), new Upgrade2212to2213(), new Upgrade2213to2214(), - new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.12", new DbUpgrade[] { new Upgrade2212to2213(), new Upgrade2213to2214(), new Upgrade2214to30(), - new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.13", new DbUpgrade[] { new Upgrade2213to2214(), new Upgrade2214to30(), new Upgrade30to301(), - new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("2.2.14", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("3.0.0", new DbUpgrade[] { new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.0", new DbUpgrade[] { new Upgrade30to301(), new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("3.0.1", new DbUpgrade[] { new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.1", new DbUpgrade[] { new Upgrade301to302(), new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("3.0.2", new DbUpgrade[] { new Upgrade302to40(), new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.2", new DbUpgrade[] { new Upgrade302to40(), new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("4.0.0", new DbUpgrade[] { new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("4.0.0", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("4.0.1", new DbUpgrade[] { new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("4.0.1", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); - _upgradeMap.put("4.0.2", new DbUpgrade[] { new Upgrade40to41(), - new Upgrade410to411(), new Upgrade411to420() }); - - _upgradeMap.put("4.1.0", new DbUpgrade[] { new Upgrade410to411(), new Upgrade411to420() }); - - _upgradeMap.put("4.1.1", new DbUpgrade[] { new Upgrade411to420() }); + _upgradeMap.put("4.0.2", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); + _upgradeMap.put("4.1.0", new DbUpgrade[] { new Upgrade410to420() }); + //CP Upgrades - _upgradeMap.put("3.0.3", new DbUpgrade[] { new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.3", new DbUpgrade[] { new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); - _upgradeMap.put("3.0.4", new DbUpgrade[] { new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.4", new DbUpgrade[] { new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); - _upgradeMap.put("3.0.5", new DbUpgrade[] { new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.5", new DbUpgrade[] { new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); - _upgradeMap.put("3.0.6", new DbUpgrade[] { new Upgrade306to307(), new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.6", new DbUpgrade[] { new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); - _upgradeMap.put("3.0.7", new DbUpgrade[] { new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + _upgradeMap.put("3.0.7", new DbUpgrade[] { new Upgrade307to410(), new Upgrade410to420() }); _upgradeMap.put("2.2.15", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), new Upgrade410to420()}); _upgradeMap.put("2.2.16", new DbUpgrade[] { new Upgrade2214to30(), new Upgrade30to301(), new Upgrade301to302(), - new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), - new Upgrade410to411(), new Upgrade411to420() }); + new Upgrade302to303(), new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(),new Upgrade307to410(), new Upgrade410to420()}); } protected void runScript(Connection conn, File file) { diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.java deleted file mode 100644 index 916e8303111..00000000000 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to411.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 com.cloud.upgrade.dao; - -import java.io.File; -import java.sql.Connection; -import java.sql.Date; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; -import org.apache.log4j.Logger; - -import com.cloud.deploy.DeploymentPlanner; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.network.vpc.NetworkACL; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; - -public class Upgrade410to411 implements DbUpgrade { - final static Logger s_logger = Logger.getLogger(Upgrade410to411.class); - - @Override - public String[] getUpgradableVersionRange() { - return new String[] { "4.1.0", "4.1.1" }; - } - - @Override - public String getUpgradedVersion() { - return "4.1.1"; - } - - @Override - public boolean supportsRollingUpgrade() { - return false; - } - - @Override - public File[] getPrepareScripts() { - // Do nothing. - return null; - } - - @Override - public void performDataMigration(Connection conn) { - // Nothing to be done - } - - @Override - public File[] getCleanupScripts() { - return null; - } -} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java similarity index 99% rename from engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java rename to engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 7400664fc90..8ff07dfa9dd 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade411to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -45,13 +45,13 @@ import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -public class Upgrade411to420 implements DbUpgrade { - final static Logger s_logger = Logger.getLogger(Upgrade411to420.class); +public class Upgrade410to420 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade410to420.class); @Override public String[] getUpgradableVersionRange() { - return new String[] { "4.1.1", "4.2.0" }; + return new String[] { "4.1.0", "4.2.0" }; } @Override @@ -66,9 +66,9 @@ public class Upgrade411to420 implements DbUpgrade { @Override public File[] getPrepareScripts() { - String script = Script.findScript("", "db/schema-411to420.sql"); + String script = Script.findScript("", "db/schema-410to420.sql"); if (script == null) { - throw new CloudRuntimeException("Unable to find db/schema-411to420.sql"); + throw new CloudRuntimeException("Unable to find db/schema-410to420.sql"); } return new File[] { new File(script) }; @@ -973,9 +973,9 @@ public class Upgrade411to420 implements DbUpgrade { @Override public File[] getCleanupScripts() { - String script = Script.findScript("", "db/schema-411to420-cleanup.sql"); + String script = Script.findScript("", "db/schema-410to420-cleanup.sql"); if (script == null) { - throw new CloudRuntimeException("Unable to find db/schema-411to420-cleanup.sql"); + throw new CloudRuntimeException("Unable to find db/schema-410to420-cleanup.sql"); } return new File[] { new File(script) }; diff --git a/setup/db/db/schema-411to420-cleanup.sql b/setup/db/db/schema-410to420-cleanup.sql similarity index 100% rename from setup/db/db/schema-411to420-cleanup.sql rename to setup/db/db/schema-410to420-cleanup.sql diff --git a/setup/db/db/schema-411to420.sql b/setup/db/db/schema-410to420.sql similarity index 100% rename from setup/db/db/schema-411to420.sql rename to setup/db/db/schema-410to420.sql From 4aa7b1990e43f966b79e3e15801db8ccacbf298b Mon Sep 17 00:00:00 2001 From: Vijayendra Bhamidipati Date: Thu, 12 Sep 2013 09:04:56 -0700 Subject: [PATCH 172/251] CLOUDSTACK-4645: There is no upgrade path from 4.1.1 to 4.2.0 Description: Simpler fix to support upgrade path from 4.1.1 to 4.2.0 by adding a 4.1.1 string in the db update version range. Commit # d1642a489ce76e055d60b2caf3ccfe4bb136b745 introduced a duplicate user_vm_view view in the schema-410to420.sql script. Removing the first occurrence of that view, since whatever QA has been testing until now would have used the duplicated view that gets created after the first one. --- .../cloud/upgrade/DatabaseUpgradeChecker.java | 2 + .../cloud/upgrade/dao/Upgrade410to420.java | 2 +- setup/db/db/schema-410to420.sql | 180 +----------------- 3 files changed, 4 insertions(+), 180 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index 723205869f7..c64be67f0d5 100755 --- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -173,6 +173,8 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { _upgradeMap.put("4.0.2", new DbUpgrade[] { new Upgrade40to41(), new Upgrade410to420() }); _upgradeMap.put("4.1.0", new DbUpgrade[] { new Upgrade410to420() }); + + _upgradeMap.put("4.1.1", new DbUpgrade[] { new Upgrade410to420() }); //CP Upgrades _upgradeMap.put("3.0.3", new DbUpgrade[] { new Upgrade303to304(), new Upgrade304to305(), new Upgrade305to306(), new Upgrade306to307(), new Upgrade307to410(), new Upgrade410to420() }); diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 8ff07dfa9dd..3d4ba58d1de 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -51,7 +51,7 @@ public class Upgrade410to420 implements DbUpgrade { @Override public String[] getUpgradableVersionRange() { - return new String[] { "4.1.0", "4.2.0" }; + return new String[] { "4.1.0", "4.1.1" }; } @Override diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 6e9fe72b63e..f7f10b97ef7 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -722,185 +722,7 @@ CREATE TABLE `cloud`.`service_offering_details` ( CONSTRAINT `fk_service_offering_details__service_offering_id` FOREIGN KEY (`service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE, CONSTRAINT UNIQUE KEY `uk_service_offering_id_name` (`service_offering_id`, `name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -DROP VIEW IF EXISTS `cloud`.`user_vm_view`; -CREATE VIEW `cloud`.`user_vm_view` AS - select - vm_instance.id id, - vm_instance.name name, - user_vm.display_name display_name, - user_vm.user_data user_data, - account.id account_id, - account.uuid account_uuid, - account.account_name account_name, - account.type account_type, - domain.id domain_id, - domain.uuid domain_uuid, - domain.name domain_name, - domain.path domain_path, - projects.id project_id, - projects.uuid project_uuid, - projects.name project_name, - instance_group.id instance_group_id, - instance_group.uuid instance_group_uuid, - instance_group.name instance_group_name, - vm_instance.uuid uuid, - vm_instance.last_host_id last_host_id, - vm_instance.vm_type type, - vm_instance.vnc_password vnc_password, - vm_instance.limit_cpu_use limit_cpu_use, - vm_instance.created created, - vm_instance.state state, - vm_instance.removed removed, - vm_instance.ha_enabled ha_enabled, - vm_instance.hypervisor_type hypervisor_type, - vm_instance.instance_name instance_name, - vm_instance.guest_os_id guest_os_id, - guest_os.uuid guest_os_uuid, - vm_instance.pod_id pod_id, - host_pod_ref.uuid pod_uuid, - vm_instance.private_ip_address private_ip_address, - vm_instance.private_mac_address private_mac_address, - vm_instance.vm_type vm_type, - data_center.id data_center_id, - data_center.uuid data_center_uuid, - data_center.name data_center_name, - data_center.networktype data_center_type, - data_center.is_security_group_enabled security_group_enabled, - host.id host_id, - host.uuid host_uuid, - host.name host_name, - vm_template.id template_id, - vm_template.uuid template_uuid, - vm_template.name template_name, - vm_template.display_text template_display_text, - vm_template.enable_password password_enabled, - iso.id iso_id, - iso.uuid iso_uuid, - iso.name iso_name, - iso.display_text iso_display_text, - service_offering.id service_offering_id, - disk_offering.uuid service_offering_uuid, - service_offering.cpu cpu, - service_offering.speed speed, - service_offering.ram_size ram_size, - disk_offering.name service_offering_name, - storage_pool.id pool_id, - storage_pool.uuid pool_uuid, - storage_pool.pool_type pool_type, - volumes.id volume_id, - volumes.uuid volume_uuid, - volumes.device_id volume_device_id, - volumes.volume_type volume_type, - security_group.id security_group_id, - security_group.uuid security_group_uuid, - security_group.name security_group_name, - security_group.description security_group_description, - nics.id nic_id, - nics.uuid nic_uuid, - nics.network_id network_id, - nics.ip4_address ip_address, - nics.ip6_address ip6_address, - nics.ip6_gateway ip6_gateway, - nics.ip6_cidr ip6_cidr, - nics.default_nic is_default_nic, - nics.gateway gateway, - nics.netmask netmask, - nics.mac_address mac_address, - nics.broadcast_uri broadcast_uri, - nics.isolation_uri isolation_uri, - vpc.id vpc_id, - vpc.uuid vpc_uuid, - networks.uuid network_uuid, - networks.name network_name, - networks.traffic_type traffic_type, - networks.guest_type guest_type, - user_ip_address.id public_ip_id, - user_ip_address.uuid public_ip_uuid, - user_ip_address.public_ip_address public_ip_address, - ssh_keypairs.keypair_name keypair_name, - resource_tags.id tag_id, - resource_tags.uuid tag_uuid, - resource_tags.key tag_key, - resource_tags.value tag_value, - resource_tags.domain_id tag_domain_id, - resource_tags.account_id tag_account_id, - resource_tags.resource_id tag_resource_id, - resource_tags.resource_uuid tag_resource_uuid, - resource_tags.resource_type tag_resource_type, - resource_tags.customer tag_customer, - async_job.id job_id, - async_job.uuid job_uuid, - async_job.job_status job_status, - async_job.account_id job_account_id, - affinity_group.id affinity_group_id, - affinity_group.uuid affinity_group_uuid, - affinity_group.name affinity_group_name, - affinity_group.description affinity_group_description - from - `cloud`.`user_vm` - inner join - `cloud`.`vm_instance` ON vm_instance.id = user_vm.id - and vm_instance.removed is NULL - inner join - `cloud`.`account` ON vm_instance.account_id = account.id - inner join - `cloud`.`domain` ON vm_instance.domain_id = domain.id - left join - `cloud`.`guest_os` ON vm_instance.guest_os_id = guest_os.id - left join - `cloud`.`host_pod_ref` ON vm_instance.pod_id = host_pod_ref.id - left join - `cloud`.`projects` ON projects.project_account_id = account.id - left join - `cloud`.`instance_group_vm_map` ON vm_instance.id = instance_group_vm_map.instance_id - left join - `cloud`.`instance_group` ON instance_group_vm_map.group_id = instance_group.id - left join - `cloud`.`data_center` ON vm_instance.data_center_id = data_center.id - left join - `cloud`.`host` ON vm_instance.host_id = host.id - left join - `cloud`.`vm_template` ON vm_instance.vm_template_id = vm_template.id - left join - `cloud`.`vm_template` iso ON iso.id = user_vm.iso_id - left join - `cloud`.`service_offering` ON vm_instance.service_offering_id = service_offering.id - left join - `cloud`.`disk_offering` ON vm_instance.service_offering_id = disk_offering.id - left join - `cloud`.`volumes` ON vm_instance.id = volumes.instance_id - left join - `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id - left join - `cloud`.`security_group_vm_map` ON vm_instance.id = security_group_vm_map.instance_id - left join - `cloud`.`security_group` ON security_group_vm_map.security_group_id = security_group.id - left join - `cloud`.`nics` ON vm_instance.id = nics.instance_id - left join - `cloud`.`networks` ON nics.network_id = networks.id - left join - `cloud`.`vpc` ON networks.vpc_id = vpc.id - left join - `cloud`.`user_ip_address` ON user_ip_address.vm_id = vm_instance.id - left join - `cloud`.`user_vm_details` ON user_vm_details.vm_id = vm_instance.id - and user_vm_details.name = 'SSH.PublicKey' - left join - `cloud`.`ssh_keypairs` ON ssh_keypairs.public_key = user_vm_details.value - left join - `cloud`.`resource_tags` ON resource_tags.resource_id = vm_instance.id - and resource_tags.resource_type = 'UserVm' - left join - `cloud`.`async_job` ON async_job.instance_id = vm_instance.id - and async_job.instance_type = 'VirtualMachine' - and async_job.job_status = 0 - left join - `cloud`.`affinity_group_vm_map` ON vm_instance.id = affinity_group_vm_map.instance_id - left join - `cloud`.`affinity_group` ON affinity_group_vm_map.affinity_group_id = affinity_group.id; - + DROP VIEW IF EXISTS `cloud`.`affinity_group_view`; CREATE VIEW `cloud`.`affinity_group_view` AS select From 3919b9fd2d51b56bf5b3bdb71d24cdb496ff2044 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 12 Sep 2013 16:29:49 -0700 Subject: [PATCH 173/251] CLOUDSTACK-4128: UI > zone wizard > secondary storage step > check if S3 secondary storage (region-wide) exists. If yes, (1)Provider dropdown has only one option "S3". (2)Name input field is loaded with the existing S3 secondary storage name and disabled. (3)Create NFS Secondary Staging Store checkbox is checked and disabled. (4)NFS Server field and Path field are required. --- ui/scripts/ui-custom/zoneWizard.js | 1 + ui/scripts/zoneWizard.js | 233 +++++++++++++++++------------ 2 files changed, 142 insertions(+), 92 deletions(-) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index bb33929f5ba..cf52107ed33 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -1006,6 +1006,7 @@ args.action({ data: data, + wizard: $wizard, startFn: $wizard.data('startfn'), uiSteps: $.map( $wizard.find('.steps > div'), diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 0ecddee3fdf..7b2679062b1 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -1792,27 +1792,43 @@ provider: { label: 'Provider', select: function(args) { - $.ajax({ - url: createURL('listStorageProviders'), - data: { - type: 'image' - }, - success: function(json) { - var objs = json.liststorageprovidersresponse.dataStoreProvider; - var items = [{ id: '', description: ''}]; - if (objs != null) { - for (var i = 0; i < objs.length; i++) { - items.push({ - id: objs[i].name, - description: objs[i].name - }); - } - } + var storageproviders = []; + $.ajax({ + url: createURL('listImageStores'), + data: { + provider: 'S3' + }, + async: true, + success: function(json) { + var s3stores = json.listimagestoresresponse.imagestore; + if(s3stores != null && s3stores.length > 0) { + storageproviders.push({ id: 'S3', description: 'S3'}); + } else { + $.ajax({ + url: createURL('listStorageProviders'), + data: { + type: 'image' + }, + async: false, + success: function(json) { + var objs = json.liststorageprovidersresponse.dataStoreProvider; + storageproviders.push({ id: '', description: ''}); + if (objs != null) { + for (var i = 0; i < objs.length; i++) { + storageproviders.push({ + id: objs[i].name, + description: objs[i].name + }); + } + } + } + }); + } args.response.success({ - data: items - }); - - args.$select.change(function() { + data: storageproviders + }); + + args.$select.change(function() { var $form = $(this).closest('form'); var $fields = $form.find('.field'); @@ -1874,6 +1890,14 @@ $fields.filter('[rel=key]').hide(); } else if ($(this).val() == "S3") { $fields.filter('[rel=name]').css('display', 'inline-block'); + + if(s3stores != null && s3stores.length > 0) { + $fields.filter('[rel=name]').find('input').val(s3stores[0].name); + $fields.filter('[rel=name]').find('input').attr("disabled", "disabled"); + } else { + //$fields.filter('[rel=name]').find('input').val(""); + $fields.filter('[rel=name]').find('input').removeAttr("disabled"); + } //NFS $fields.filter('[rel=zoneid]').hide(); @@ -1881,19 +1905,36 @@ $fields.filter('[rel=path]').hide(); //S3 - $fields.filter('[rel=accesskey]').css('display', 'inline-block'); - $fields.filter('[rel=secretkey]').css('display', 'inline-block'); - $fields.filter('[rel=bucket]').css('display', 'inline-block'); - $fields.filter('[rel=endpoint]').css('display', 'inline-block'); - $fields.filter('[rel=usehttps]').css('display', 'inline-block'); - $fields.filter('[rel=connectiontimeout]').css('display', 'inline-block'); - $fields.filter('[rel=maxerrorretry]').css('display', 'inline-block'); - $fields.filter('[rel=sockettimeout]').css('display', 'inline-block'); + if(s3stores != null && s3stores.length > 0) { + $fields.filter('[rel=accesskey]').hide(); + $fields.filter('[rel=secretkey]').hide(); + $fields.filter('[rel=bucket]').hide(); + $fields.filter('[rel=endpoint]').hide(); + $fields.filter('[rel=usehttps]').hide(); + $fields.filter('[rel=connectiontimeout]').hide(); + $fields.filter('[rel=maxerrorretry]').hide(); + $fields.filter('[rel=sockettimeout]').hide(); - $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); - $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); + $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); + $fields.filter('[rel=createNfsCache]').find('input').attr("disabled", "disabled"); + $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); + } else { + $fields.filter('[rel=accesskey]').css('display', 'inline-block'); + $fields.filter('[rel=secretkey]').css('display', 'inline-block'); + $fields.filter('[rel=bucket]').css('display', 'inline-block'); + $fields.filter('[rel=endpoint]').css('display', 'inline-block'); + $fields.filter('[rel=usehttps]').css('display', 'inline-block'); + $fields.filter('[rel=connectiontimeout]').css('display', 'inline-block'); + $fields.filter('[rel=maxerrorretry]').css('display', 'inline-block'); + $fields.filter('[rel=sockettimeout]').css('display', 'inline-block'); + + $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); + $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); + } //Swift $fields.filter('[rel=url]').hide(); @@ -1929,11 +1970,10 @@ $fields.filter('[rel=username]').css('display', 'inline-block'); $fields.filter('[rel=key]').css('display', 'inline-block'); } - }); - - args.$select.change(); - } - }); + }); + args.$select.change(); + } + }); } }, @@ -2055,7 +2095,9 @@ } }, - action: function(args) { + action: function(args) { + var $wizard = args.wizard; + var advZoneConfiguredVirtualRouterCount = 0; //for multiple physical networks in advanced zone. Each physical network has 2 virtual routers: regular one and VPC one. var success = args.response.success; @@ -4108,62 +4150,69 @@ }); } }); - } else if (args.data.secondaryStorage.provider == 'S3') { - $.extend(data, { - provider: args.data.secondaryStorage.provider, - 'details[0].key': 'accesskey', - 'details[0].value': args.data.secondaryStorage.accesskey, - 'details[1].key': 'secretkey', - 'details[1].value': args.data.secondaryStorage.secretkey, - 'details[2].key': 'bucket', - 'details[2].value': args.data.secondaryStorage.bucket, - 'details[3].key': 'usehttps', - 'details[3].value': (args.data.secondaryStorage.usehttps != null && args.data.secondaryStorage.usehttps == 'on' ? 'true' : 'false') - }); + } else if (args.data.secondaryStorage.provider == 'S3') { + if($wizard.find('form[rel=secondaryStorage]').find('div[rel=name]').find('input').attr("disabled") == "disabled") { //Name textbox is disabled (and populated with S3 image setore name) when S3 image store exists. In this case, do not call addImageStore to create S3 image store. + complete({ + data: args.data + }); + } else { //Name textbox is not disabled when S3 image store does not exist. In this case, call addImageStore to create S3 image store. + $.extend(data, { + provider: args.data.secondaryStorage.provider, + 'details[0].key': 'accesskey', + 'details[0].value': args.data.secondaryStorage.accesskey, + 'details[1].key': 'secretkey', + 'details[1].value': args.data.secondaryStorage.secretkey, + 'details[2].key': 'bucket', + 'details[2].value': args.data.secondaryStorage.bucket, + 'details[3].key': 'usehttps', + 'details[3].value': (args.data.secondaryStorage.usehttps != null && args.data.secondaryStorage.usehttps == 'on' ? 'true' : 'false') + }); - var index = 4; - if (args.data.secondaryStorage.endpoint != null && args.data.secondaryStorage.endpoint.length > 0) { - data['details[' + index.toString() + '].key'] = 'endpoint'; - data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.endpoint; - index++; - } - if (args.data.secondaryStorage.connectiontimeout != null && args.data.secondaryStorage.connectiontimeout.length > 0) { - data['details[' + index.toString() + '].key'] = 'connectiontimeout'; - data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.connectiontimeout; - index++; - } - if (args.data.secondaryStorage.maxerrorretry != null && args.data.secondaryStorage.maxerrorretry.length > 0) { - data['details[' + index.toString() + '].key'] = 'maxerrorretry'; - data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.maxerrorretry; - index++; - } - if (args.data.secondaryStorage.sockettimeout != null && args.data.secondaryStorage.sockettimeout.length > 0) { - data['details[' + index.toString() + '].key'] = 'sockettimeout'; - data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.sockettimeout; - index++; - } - $.ajax({ - url: createURL('addImageStore'), - data: data, - success: function(json) { - g_regionsecondaryenabled = true; - - complete({ - data: $.extend(args.data, { - returnedSecondaryStorage: json.addimagestoreresponse.secondarystorage - }) - }); - }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - error('addSecondaryStorage', errorMsg, { - fn: 'addSecondaryStorage', - args: args - }); + var index = 4; + if (args.data.secondaryStorage.endpoint != null && args.data.secondaryStorage.endpoint.length > 0) { + data['details[' + index.toString() + '].key'] = 'endpoint'; + data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.endpoint; + index++; } - }); - - if (args.data.secondaryStorage.createNfsCache == 'on') { + if (args.data.secondaryStorage.connectiontimeout != null && args.data.secondaryStorage.connectiontimeout.length > 0) { + data['details[' + index.toString() + '].key'] = 'connectiontimeout'; + data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.connectiontimeout; + index++; + } + if (args.data.secondaryStorage.maxerrorretry != null && args.data.secondaryStorage.maxerrorretry.length > 0) { + data['details[' + index.toString() + '].key'] = 'maxerrorretry'; + data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.maxerrorretry; + index++; + } + if (args.data.secondaryStorage.sockettimeout != null && args.data.secondaryStorage.sockettimeout.length > 0) { + data['details[' + index.toString() + '].key'] = 'sockettimeout'; + data['details[' + index.toString() + '].value'] = args.data.secondaryStorage.sockettimeout; + index++; + } + $.ajax({ + url: createURL('addImageStore'), + data: data, + success: function(json) { + g_regionsecondaryenabled = true; + + complete({ + data: $.extend(args.data, { + returnedSecondaryStorage: json.addimagestoreresponse.secondarystorage + }) + }); + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + error('addSecondaryStorage', errorMsg, { + fn: 'addSecondaryStorage', + args: args + }); + } + }); + } + + //NFS Cache + if ($wizard.find('form[rel=secondaryStorage]').find('div[rel=createNfsCache]').find("input[type=checkbox]").is(':checked') == true) { var zoneid = args.data.secondaryStorage.nfsCacheZoneid; var nfs_server = args.data.secondaryStorage.nfsCacheNfsServer; var path = args.data.secondaryStorage.nfsCachePath; From a096c966725cbe083c8c537beb43b3928750b3ca Mon Sep 17 00:00:00 2001 From: Min Chen Date: Thu, 12 Sep 2013 18:52:40 -0700 Subject: [PATCH 174/251] CLOUDSTACK-4661:[DB Upgrade] SecondaryStorage entry in host table before upgrade is not marked as removed after migrating them to image_store table. --- engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 3d4ba58d1de..3c753e90156 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -2238,6 +2238,11 @@ public class Upgrade410to420 implements DbUpgrade { storeInsert.setDate(9, nfs_created); storeInsert.executeUpdate(); } + + s_logger.debug("Marking NFS secondary storage in host table as removed"); + pstmt = conn.prepareStatement("UPDATE `cloud`.`host` SET removed = now() WHERE type = 'SecondaryStorage' and removed is null"); + pstmt.executeUpdate(); + pstmt.close(); } catch (SQLException e) { String msg = "Unable to migrate secondary storages." + e.getMessage(); From cae8b39b43fd9055243268b7b0ad4ccdc4971379 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 12 Sep 2013 19:11:13 -0700 Subject: [PATCH 175/251] CLOUDSTACK-4662: UI > Network > IP Address > select a portable IP Address > Port Forwarding > VM grid - select a VM - dropdown of VM NIC IP appears - dropdown option value is networkID + VmGuestIp, split it and pass only VmGuestIp to API call. --- ui/scripts/network.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 0afa3608177..a09e56515aa 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -3842,10 +3842,20 @@ virtualmachineid: args.itemData[0].id, openfirewall: false }; + + if (args.context.ipAddresses[0].isportable) { + var subselect = args.itemData[0]._subselect.split(','); + //var networkid = subselect[0]; + var vmguestip = subselect[1]; - if (args.itemData[0]._subselect && args.itemData[0]._subselect != -1) { + //data.networkid = networkid; + + if (parseInt(vmguestip) !== -1) { + data.vmguestip = vmguestip; + } + } else if (args.itemData[0]._subselect && args.itemData[0]._subselect != -1) { data.vmguestip = args.itemData[0]._subselect; - } + } if ('vpc' in args.context) { //from VPC section if (args.data.tier == null) { From 23c759ec356d90801b7117cbb4b44e730bcad374 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Fri, 13 Sep 2013 00:32:45 -0400 Subject: [PATCH 176/251] Automation - Portable Ip feature test cases Signed-off-by: venkataswamybabu budumuru --- .../integration/component/test_portable_ip.py | 1309 +++++++++++++++++ tools/marvin/marvin/integration/lib/base.py | 4 +- tools/marvin/marvin/integration/lib/common.py | 35 + 3 files changed, 1347 insertions(+), 1 deletion(-) create mode 100644 test/integration/component/test_portable_ip.py diff --git a/test/integration/component/test_portable_ip.py b/test/integration/component/test_portable_ip.py new file mode 100644 index 00000000000..55de60d76ce --- /dev/null +++ b/test/integration/component/test_portable_ip.py @@ -0,0 +1,1309 @@ +# 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. +""" Tests for Portable public IP Ranges feature +""" +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.cloudstackException import cloudstackAPIException +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from netaddr import * +from marvin.remoteSSHClient import remoteSSHClient + +from nose.plugins.attrib import attr + +class Services: + """Test Multiple IP Ranges + """ + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 200, # in MHz + "memory": 256, # In MBs + }, + "network_offering": { + "name": 'Network offering portable ip', + "displaytext": 'Network offering-VR services', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'VirtualRouter', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "network": { + "name": "Test Network - Portable IP", + "displaytext": "Test Network - Portable IP", + }, + "disk_offering": { + "displaytext": "Small Disk", + "name": "Small Disk", + "disksize": 1 + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP", + "cidr" : '0.0.0.0/0', + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + } + +class TestCreatePortablePublicIpRanges(cloudstackTestCase): + """Test Create Portable IP Ranges + """ + + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestCreatePortablePublicIpRanges, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + + cls._cleanup = [] + 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, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_create_portable_ip_range(self): + """Test create new portable ip range + """ + # 1. Create new portable ip range with root admin api + # 2. Portable ip range should be created successfully + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + self.debug(portable_ip_range_services) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(portable_ip_range_services["startip"]), + str(portable_ip_range_services["endip"]))) + + #create new portable ip range + new_portable_ip_range = PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (new_portable_ip_range.startip, + new_portable_ip_range.endip, + new_portable_ip_range.id)) + + self.cleanup.append(new_portable_ip_range) + + return + + @attr(tags=["advanced"]) + def test_create_portable_ip_range_non_root_admin(self): + """Test create new portable ip range with non admin root account + """ + # 1. Create new portable ip range with non root admin api client + # 2. Portable ip range should not be created + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.api_client_user = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Trying to create portable ip range with non root-admin api client, should raise exception") + with self.assertRaises(Exception): + PortablePublicIpRange.create(self.api_client_user, + portable_ip_range_services) + + return + + @attr(tags=["advanced"]) + def test_create_portable_ip_range_invalid_region(self): + """Test create portable ip range with invalid region id""" + + # 1. Try to create new portable ip range with invalid region id + # 2. Portable ip range creation should fail + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = -1 + + #create new portable ip range + self.debug("Trying to create portable ip range with wrong region id") + + with self.assertRaises(Exception): + PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + return + +class TestDeletePortablePublicIpRanges(cloudstackTestCase): + """Test delete Portable IP Ranges + """ + + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDeletePortablePublicIpRanges, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + + cls._cleanup = [] + 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() + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(portable_ip_range_services["startip"]), + str(portable_ip_range_services["endip"]))) + + #create new portable ip range + self.portable_ip_range = PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (self.portable_ip_range.startip, + self.portable_ip_range.endip, + self.portable_ip_range.id)) + + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_delete_portable_ip_range(self): + """Test delete ip range + """ + # 1. Try to delete the created range with root admin api client + # 2. Portable range should be deleted successfully + + self.debug("Deleting portable ip range with root-admin api") + + self.portable_ip_range.delete(self.apiclient) + + self.debug("Deleted portable ip range") + + return + + @attr(tags=["advanced"]) + def test_delete_portable_ip_range_non_root_admin(self): + """Test delete ip range - non admin root + """ + # 1. Try to delete the created range with non root admin api client + # 2. Portable range deletion should fail + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.cleanup.append(self.account) + + self.api_client_user = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + + with self.assertRaises(Exception): + self.portable_ip_range.delete(self.api_client_user) + + self.portable_ip_range.delete(self.apiclient) + return + + @attr(tags=["advanced"]) + def test_delete_portable_ip_range_in_use(self): + """Test delete ip range + """ + # 1. Associate a portable ip + # 2. Try to delete the portable ip range with root admin api client + # 3. Portable ip range should not be deleted unless currently used ip is disassociated + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.cleanup.append(self.account) + + self.debug( + "Creating n/w offering" + ) + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + conservemode=False + ) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + self.debug("Creating network") + + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + + self.debug("Created network with id: %s" % self.network.id) + self.debug("Associating public ip address with network: %s with isportable=True" % self.network.id) + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("Associated public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + with self.assertRaises(Exception): + self.debug("Trying to Delete portable ip range with root-admin api, this should fail") + self.portable_ip_range.delete(self.apiclient) + + self.debug("Deleting portable ip range failed") + self.debug("Disassociating portable ip") + portableip.delete(self.apiclient) + + self.debug("Deleting portable ip range") + self.portable_ip_range.delete(self.apiclient) + + return + +class TestListPortablePublicIpRanges(cloudstackTestCase): + """Test List Portable IP Ranges + """ + + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestListPortablePublicIpRanges, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + + cls._cleanup = [] + 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() + + #create new portable ip range + self.portable_ip_range_services = get_portable_ip_range_services(self.config) + + if self.portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + self.portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(self.portable_ip_range_services["startip"]), + str(self.portable_ip_range_services["endip"]))) + + #create new portable ip range + self.portable_ip_range = PortablePublicIpRange.create(self.apiclient, + self.portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (self.portable_ip_range.startip, + self.portable_ip_range.endip, + self.portable_ip_range.id)) + + self.cleanup = [self.portable_ip_range, ] + return + + def tearDown(self): + try: + #Clean up, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_list_portable_ip_range(self): + """Test list portable ip ranges + """ + # 1. Create new portable ip range + # 2. Try to list ip ranges with root admin api client + # 3. Portable ip ranges should list properly + + list_portable_ip_range = PortablePublicIpRange.list(self.apiclient, + id=self.portable_ip_range.id) + + self.assertEqual( + isinstance(list_portable_ip_range, list), + True, + "List portable IP ranges should not return an empty response" + ) + + portable_ip_range = list_portable_ip_range[0] + + self.assertEqual(str(portable_ip_range.startip), str(self.portable_ip_range_services["startip"]), + "Listed startip not matching with the startip of created public ip range") + + self.assertEqual(str(portable_ip_range.endip), str(self.portable_ip_range_services["endip"]), + "Listed endip not matching with the endip of created public ip range") + + self.assertEqual(str(portable_ip_range.gateway), str(self.portable_ip_range_services["gateway"]), + "Listed gateway not matching with the gateway of created public ip range") + + self.assertEqual(str(portable_ip_range.netmask), str(self.portable_ip_range_services["netmask"]), + "Listed netmask not matching with the netmask of created public ip range") + return + + @attr(tags=["advanced"]) + def test_list_portable_ip_range_non_root_admin(self): + """Test list portable ip ranges with non admin root account + """ + # 1. Create new portable ip range + # 2. Try to list ip ranges with root non admin api client + # 3. Portable ip ranges listing should fail + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.cleanup.append(self.account) + + self.api_client_user = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + + self.debug("Trying to list portable ip ranges with non root-admin api, should raise exception") + with self.assertRaises(Exception): + PortablePublicIpRange.list(self.api_client_user, + id=self.portable_ip_range.id) + return + +class TestAssociatePublicIp(cloudstackTestCase): + """Test associate Portable IP/ non portable public ip + """ + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAssociatePublicIp, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = template.id + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id, + admin=True + ) + + cls.network_offering = NetworkOffering.create( + cls.api_client, + cls.services["network_offering"], + conservemode=False + ) + + # Enable Network offering + cls.network_offering.update(cls.api_client, state='Enabled') + + cls.network = Network.create( + cls.api_client, + cls.services["network"], + accountid=cls.account.name, + domainid=cls.account.domainid, + networkofferingid=cls.network_offering.id, + zoneid=cls.zone.id + ) + + cls._cleanup = [cls.account] + return + + @classmethod + def tearDownClass(cls): + try: + # Disable Network offering + cls.network_offering.update(cls.api_client, state='Disabled') + #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 = [] + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(portable_ip_range_services["startip"]), + str(portable_ip_range_services["endip"]))) + + #create new portable ip range + self.portable_ip_range = PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (self.portable_ip_range.startip, + self.portable_ip_range.endip, + self.portable_ip_range.id)) + + self.cleanup.append(self.portable_ip_range) + + return + + def tearDown(self): + try: + #Clean up, terminate the resources created + self.network_offering.update(self.apiclient, state='Disabled') + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_associate_ip_address(self): + """ Test assocoate public ip address + """ + + # 1. Create new portable ip range + # 2. Create a network and associate public ip without mentioning (isportable) + # 3. Create a network and associate public ip with isportable=False + # 4. Create a network and associate public ip with isPortable=True + # 5. All three public ip associations should succeed + + self.debug("Associating default public ip address with network: %s" % self.network.id) + publicipaddress = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated default public ip address: %s" % publicipaddress.ipaddress.ipaddress) + + + + self.debug("Associating public ip address with network: %s with isportable=False" % self.network.id) + publicipaddressnotportable = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=False + ) + + self.debug("Associated public ip address (not portable): %s" % publicipaddressnotportable.ipaddress.ipaddress) + publicipaddressnotportable.delete(self.apiclient) + + self.debug("Associating public ip address with network: %s with isportable=True" % self.network.id) + publicipaddressportable = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("Associated public ip address (portable): %s" % publicipaddressportable.ipaddress.ipaddress) + publicipaddressportable.delete(self.apiclient) + + return + + @attr(tags=["advanced"]) + def test_associate_ip_address_invalid_zone(self): + """ Test Associate IP with invalid zone id + """ + # 1. Create new portable ip range + # 2. try to associate a portable ip with invalid region id + # 3. IP association should fail + + self.debug("Trying to associate portable public ip with invalid zone id, this should fail") + + with self.assertRaises(Exception): + PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid = -1, + domainid=self.account.domainid, + regionid = self.region.id, + isportable=True + ) + self.debug("Associating ip address failed") + return + + @unittest.skip("SSH failing to portable ip, need to investigate the issue") + @attr(tags=["advanced"]) + def test_associate_ip_address_services_enable_disable(self): + """ Test enabling and disabling NAT, Firewall services on portable ip + """ + # 1. Create new portable ip range + # 2. Associate a portable ip + # 3. Enable NAT and Firewall rules on this portable ip + # 4. Disable NAT and Firewall rules created + # 5. Enabling and disabling ofthe rules should be successful + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + + self.cleanup.append(self.service_offering) + + try: + + self.debug("Deploying Virtual Machine") + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids = [self.network.id], + mode=self.services['mode'] + ) + self.debug("Created virtual machine instance: %s with ssh_ip: %s" % + (self.virtual_machine.id, self.virtual_machine.ssh_ip)) + + except Exception as e: + self.fail("Exception while deploying vm : %s" % e) + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + # Open up firewall port for SSH + self.debug("Opening firewall on the portable public ip") + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=portableip.ipaddress.id, + protocol=self.services["natrule"]["protocol"], + cidrlist=[self.services["natrule"]["cidr"]], + startport=self.services["natrule"]["publicport"], + endport=self.services["natrule"]["publicport"] + ) + + #Create NAT rule + self.debug("Creating NAT rule on the portable public ip") + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + portableip.ipaddress.id + ) + + try: + + self.debug("Trying to SSH to ip: %s" % portableip.ipaddress.ipaddress) + + remoteSSHClient( + portableip.ipaddress.ipaddress, + self.services['natrule']["publicport"], + self.virtual_machine.username, + self.virtual_machine.password + ) + except Exception as e: + self.fail("Exception while SSHing : %s" % e) + + self.debug("Deleting firewall rule") + fw_rule.delete(self.apiclient) + + self.debug("Deleting NAT rule") + nat_rule.delete(self.apiclient) + + self.debug("disassocoating portable ip: %s" % portableip.ipaddress.ipaddress) + portableip.delete(self.apiclient) + return + + @attr(tags=["advanced"]) + def test_associate_ip_address_no_free_ip(self): + """ Test assocoate public ip address + """ + + # 1. Create new portable ip range + # 2. Create a network and associate all available portbale public ips + # 5. Try to associate portable ip, it should fail + + associatedipaddresses = [] + + startip_int = int(IPAddress(self.portable_ip_range.startip)) + endip_int = int(IPAddress(self.portable_ip_range.endip)) + totalportableips = ((endip_int - startip_int) + 1) + + self.debug(totalportableips) + + for x in range(0, totalportableips): + + self.debug("Associating public ip address with network: %s with isportable=True" % self.network.id) + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + associatedipaddresses.append(portableip) + self.debug("Associated public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + self.debug("Trying to associate portable public ip when no free ips available, this should fail") + with self.assertRaises(Exception): + PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + + self.debug("Associating portable ip address failed") + + self.debug("Disassociating previously associated ip addresses") + + for x in range(0, totalportableips): + associatedipaddresses[x].delete(self.apiclient) + + return + +class TestDisassociatePublicIp(cloudstackTestCase): + """Test Disassociate Portable IP/ non portable IP + """ + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDisassociatePublicIp, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = template.id + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id, + admin=True + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls.network_offering = NetworkOffering.create( + cls.api_client, + cls.services["network_offering"], + conservemode=False + ) + + # Enable Network offering + cls.network_offering.update(cls.api_client, state='Enabled') + + cls.network = Network.create( + cls.api_client, + cls.services["network"], + accountid=cls.account.name, + domainid=cls.account.domainid, + networkofferingid=cls.network_offering.id, + zoneid=cls.zone.id + ) + + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + networkids = [cls.network.id], + mode=cls.services['mode'] + ) + + cls._cleanup = [ + cls.account, + cls.service_offering, + cls.network_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + # Disable Network offering + cls.network_offering.update(cls.api_client, state='Disabled') + #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 = [] + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(portable_ip_range_services["startip"]), + str(portable_ip_range_services["endip"]))) + + #create new portable ip range + new_portable_ip_range = PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (new_portable_ip_range.startip, + new_portable_ip_range.endip, + new_portable_ip_range.id)) + + self.cleanup.append(new_portable_ip_range) + + return + + def tearDown(self): + try: + #Clean up, terminate the resources created + self.network_offering.update(self.apiclient, state='Disabled') + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_disassociate_ip_address_no_services(self): + """ Test disassociating portable ip + """ + # 1. Create new portable ip range + # 2. Associate a portable ip + # 3. Disassociate the portable ip with root admin api client + # 4. Disassociating should be successful + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + try: + self.debug("Disassociating portable ip: %s with id: %s" % + (portableip.ipaddress.ipaddress, portableip.ipaddress.id) + ) + + portableip.delete(self.apiclient) + + except Exception as e: + raise Exception("Exception while disassociating portable ip: %s" % e) + + return + + @attr(tags=["advanced"]) + def test_disassociate_ip_address_services_enabled(self): + """ Test disassociating portable ip + """ + # 1. Create new portable ip range + # 2. Associate a portable ip + # 3. Enable NAT and Firewall services on this portable IP + # 4. Disassociate the portable ip with root admin api client + # 5. Disassociating should be successful + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + # Open up firewall port for SSH + self.debug("Opening firewall on the portable public ip") + FireWallRule.create( + self.apiclient, + ipaddressid=portableip.ipaddress.id, + protocol=self.services["natrule"]["protocol"], + cidrlist=[self.services["natrule"]["cidr"]], + startport=self.services["natrule"]["publicport"], + endport=self.services["natrule"]["publicport"] + ) + + #Create NAT rule + self.debug("Creating NAT rule on the portable public ip") + NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + portableip.ipaddress.id + ) + + try: + self.debug("Disassociating portable ip: %s with id: %s" % + (portableip.ipaddress.ipaddress, portableip.ipaddress.id) + ) + + portableip.delete(self.apiclient) + + except Exception as e: + raise Exception("Exception while disassociating portable ip: %s" % e) + + return + + @attr(tags=["advanced"]) + def test_disassociate_ip_address_other_account(self): + """ Test disassociating portable IP with non-owner account + """ + + # 1. Create new portable ip range + # 2. Associate a portable ip + # 3. Try to Disassociate the portable ip with an account which is not owner of portable ip + # 4. Disassociating should fail + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + self.user_account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.api_client_user = self.testClient.createUserApiClient( + UserName=self.user_account.name, + DomainName=self.user_account.domain + ) + try: + self.debug("Disassociating portable ip: %s with id: %s with other account :%s" % + (portableip.ipaddress.ipaddress, portableip.ipaddress.id, self.user_account.name) + ) + + with self.assertRaises(Exception): + portableip.delete(self.api_client_user) + + except Exception as e: + raise Exception("Exception while disassociating portable ip: %s" % e) + + portableip.delete(self.apiclient) + return + +class TestDeleteAccount(cloudstackTestCase): + """ Test Delete Account having portable ip + """ + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDeleteAccount, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.region = get_region(cls.api_client, cls.services) + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["regionid"] = cls.region.id + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = template.id + + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id, + admin=True + ) + self.cleanup = [] + + portable_ip_range_services = get_portable_ip_range_services(self.config) + + if portable_ip_range_services is None: + self.skipTest('Failed to read config values related to portable ip range') + + portable_ip_range_services["regionid"] = self.region.id + + self.debug("Creating new portable IP range with startip:%s and endip:%s" % + (str(portable_ip_range_services["startip"]), + str(portable_ip_range_services["endip"]))) + + #create new portable ip range + new_portable_ip_range = PortablePublicIpRange.create(self.apiclient, + portable_ip_range_services) + + self.debug("Created new portable IP range with startip:%s and endip:%s and id:%s" % + (new_portable_ip_range.startip, + new_portable_ip_range.endip, + new_portable_ip_range.id)) + + self.cleanup.append(new_portable_ip_range) + + self.debug( + "Creating n/w offering" + ) + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + conservemode=False + ) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + self.debug("Creating network") + + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + + self.cleanup.append(self.network_offering) + + self.debug("Created network with id: %s" % self.network.id) + return + + def tearDown(self): + try: + # Disable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + #Clean up, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"]) + def test_delete_account_services_disabled(self): + """ test delete account with portable ip with no services enabled + """ + # 1. Associate a portable ip to an account + # 2. Delete account + # 3. Account should get deleted successfully + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + self.debug("Deleting account: %s :" % self.account.name) + + self.account.delete(self.apiclient) + + self.debug("Account deleted successfully") + + with self.assertRaises(Exception): + PublicIPAddress.list(self.apiclient, + id=portableip.ipaddress.id) + + return + + @attr(tags=["advanced"]) + def test_delete_account_services_enabled(self): + """ test delete account with portable ip with PF and firewall services enabled + """ + # 1. Associate a portable ip to an account + # 2. Enabled PF and Firewall rules on this IP + # 3. Delete account + # 4. Account should get deleted successfully + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + + self.cleanup.append(self.service_offering) + + self.debug("Deploying Virtual Machine") + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services['mode'] + ) + self.debug("Created virtual machine instance: %s" % self.virtual_machine.id) + + portableip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=self.network.id, + isportable=True + ) + self.debug("created public ip address (portable): %s" % portableip.ipaddress.ipaddress) + + # Open up firewall port for SSH + self.debug("Opening firewall on the portable public ip") + FireWallRule.create( + self.apiclient, + ipaddressid=portableip.ipaddress.id, + protocol=self.services["natrule"]["protocol"], + cidrlist=[self.services["natrule"]["cidr"]], + startport=self.services["natrule"]["publicport"], + endport=self.services["natrule"]["publicport"] + ) + + #Create NAT rule + self.debug("Creating NAT rule on the portable public ip") + NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + portableip.ipaddress.id + ) + + self.debug("Deleting account: %s :" % self.account.name) + + self.account.delete(self.apiclient) + + self.debug("Trying to list the ip address associated with deleted account, \ + should throw exception") + + with self.assertRaises(Exception): + PublicIPAddress.list(self.apiclient, + id=portableip.ipaddress.id) + + return diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index fa4cc8202d2..9e460658217 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -2360,7 +2360,9 @@ class PortablePublicIpRange: cmd.startip = services["startip"] cmd.endip = services["endip"] cmd.regionid = services["regionid"] - cmd.vlan = services["vlan"] + + if "vlan" in services: + cmd.vlan = services["vlan"] return PortablePublicIpRange(apiclient.createPortableIpRange(cmd).__dict__) diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index 6ffe951e977..e8958850ce5 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -686,3 +686,38 @@ def get_resource_type(resource_id): } return lookup[resource_id] + +def get_portable_ip_range_services(config): + """ Reads config values related to portable ip and fills up + services accordingly""" + + services = {} + attributeError = False + + if config.portableIpRange.startip: + services["startip"] = config.portableIpRange.startip + else: + attributeError = True + + if config.portableIpRange.endip: + services["endip"] = config.portableIpRange.endip + else: + attributeError = True + + if config.portableIpRange.netmask: + services["netmask"] = config.portableIpRange.netmask + else: + attributeError = True + + if config.portableIpRange.gateway: + services["gateway"] = config.portableIpRange.gateway + else: + attributeError = True + + if config.portableIpRange.vlan: + services["vlan"] = config.portableIpRange.vlan + + if attributeError: + services = None + + return services From 3a3caf62b8fcd7e917eba4ce32c672fd889b07c9 Mon Sep 17 00:00:00 2001 From: Frank Zhang Date: Fri, 13 Sep 2013 09:24:52 -0700 Subject: [PATCH 177/251] CloudStack CLOUDSTACK-4224 UCS:UI: Delete UCS returns unknown API --- client/tomcatconf/commands.properties.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index d4e55f7372c..69a31207220 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -617,7 +617,7 @@ listUcsManagers=1 listUcsProfiles=1 listUcsBlades=1 associateUcsProfileToBlade=1 -removedeleteUcsManager=1 +deleteUcsManager=1 #### New Load Balancer commands createLoadBalancer=15 From 758b0bff8e4baaed7055d999cacda8075c2ca376 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 13 Sep 2013 15:38:21 -0700 Subject: [PATCH 178/251] CLOUDSTACK-4669: UI > Infrastructure > zone > UCS manager > UCS blade > implement new action disassociateProfileFromBlade. --- ui/css/cloudstack3.css | 3 +- ui/scripts/system.js | 86 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 04f71ae60e9..b260f87c7c8 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11894,7 +11894,8 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .detach .icon, .detachISO .icon, -.detachDisk .icon { +.detachDisk .icon +.disassociateProfileFromBlade .icon{ background-position: -101px -65px; } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 3f5a5d4f8e6..a3643f3f6fb 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -6129,14 +6129,7 @@ } } }); - // for testing only (begin) - /* - selectedZoneObj.vmwaredcName = "datacenter"; - selectedZoneObj.vmwaredcVcenter = "10.10.20.20"; - selectedZoneObj.vmwaredcId = "c3c2562d-65e9-4fc7-92e2-773c2efe8f37"; - */ - // for testing only (end) - + args.response.success({ actionFilter: zoneActionfilter, data: selectedZoneObj @@ -13999,7 +13992,78 @@ notification: { poll: pollAsyncJobResult } - } + }, + + disassociateProfileFromBlade: { + label: 'Disassociate Profile from Blade', + addRow: 'false', + messages: { + confirm: function(args) { + return 'Please confirm that you want to disassociate Profile from Blade.'; + }, + notification: function(args) { + return 'Disassociate Profile from Blade'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('disassociateUcsProfileFromBlade'), + data: { + //ucsmanagerid: args.context.ucsManagers[0].id, + bladeid: args.context.blades[0].id + }, + success: function(json) { + //for testing only (begin) + /* + json = { + "disassociateucsprofilefrombladeresponse": { + "jobid": "e371592e-31be-4e53-9346-a5c565d420df" + } + } + */ + //for testing only (end) + + var jid = json.disassociateucsprofilefrombladeresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + //for testing only (begin) + /* + json = { + "queryasyncjobresultresponse": { + "accountid": "b24f6e36-f0ca-11e2-8c16-d637902e3581", + "userid": "b24f7d8d-f0ca-11e2-8c16-d637902e3581", + "cmd": "org.apache.cloudstack.api.AssociateUcsProfileToBladeCmd", + "jobstatus": 1, + "jobprocstatus": 0, + "jobresultcode": 0, + "jobresulttype": "object", + "jobresult": { + "ucsblade": { + "id": "80ab25c8-3dcf-400e-8849-84dc5e1e6594", + "ucsmanagerid": "07b5b813-83ed-4859-952c-c95cafb63ac4", + "bladedn": "sys/chassis-1/blade-4" + } + }, + "created": "2013-07-26T13:53:01-0700", + "jobid": "770bec68-7739-4127-8609-4b87bd7867d2" + } + }; + */ + //for testing only (end) + + return json.queryasyncjobresultresponse.jobresult.ucsblade; + } + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } } } } @@ -16091,7 +16155,9 @@ var allowedActions = []; if(jsonObj.profiledn == null) { allowedActions.push("associateProfileToBlade"); - } + } else { + allowedActions.push("disassociateProfileFromBlade"); + } return allowedActions; } From 93d8b965f9c60b09921282c74441aaba42520b34 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 13 Sep 2013 16:08:23 -0700 Subject: [PATCH 179/251] CLOUDSTACK-4669: Fix disassociateProfileFromBlade action icon --- ui/css/cloudstack3.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index b260f87c7c8..394771c2de1 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11894,14 +11894,15 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .detach .icon, .detachISO .icon, -.detachDisk .icon -.disassociateProfileFromBlade .icon{ +.detachDisk .icon, +.disassociateProfileFromBlade .icon { background-position: -101px -65px; } .detach:hover .icon, .detachISO:hover .icon, -.detachDisk:hover .icon { +.detachDisk:hover .icon, +.disassociateProfileFromBlade:hover .icon { background-position: -101px -647px; } From 670fc7e6ec58e246777f6fb348e69b16225a040e Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 13 Sep 2013 16:26:40 -0700 Subject: [PATCH 180/251] CLOUDSTACK-4659: Add the missing feature back for GC VMware worker VMs --- .../consoleproxy/ConsoleProxyResource.java | 25 +-- .../com/cloud/hypervisor/guru/VMwareGuru.java | 1 + .../vmware/manager/VmwareManager.java | 1 + .../vmware/manager/VmwareManagerImpl.java | 49 +++++- .../manager/VmwareStorageManagerImpl.java | 34 +--- .../vmware/resource/VmwareContextFactory.java | 6 + .../vmware/resource/VmwareResource.java | 153 ++++++------------ ...VmwareSecondaryStorageResourceHandler.java | 1 + .../resource/VmwareStorageProcessor.java | 4 +- .../src/com/cloud/configuration/Config.java | 1 + .../vmware/mo/CustomFieldConstants.java | 2 + .../vmware/mo/HypervisorHostHelper.java | 20 ++- .../vmware/mo/VirtualMachineMO.java | 8 +- 13 files changed, 153 insertions(+), 152 deletions(-) diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index 991764c53f8..ee5c36176c8 100644 --- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java +++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java @@ -112,6 +112,7 @@ public class ConsoleProxyResource extends ServerResourceBase implements } private Answer execute(StartConsoleProxyAgentHttpHandlerCommand cmd) { + s_logger.info("Invoke launchConsoleProxy() in responding to StartConsoleProxyAgentHttpHandlerCommand"); launchConsoleProxy(cmd.getKeystoreBits(), cmd.getKeystorePassword(), cmd.getEncryptorPassword()); return new Answer(cmd); } @@ -361,29 +362,31 @@ public class ConsoleProxyResource extends ServerResourceBase implements try { Class consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy"); try { + s_logger.info("Invoke setEncryptorPassword(), ecnryptorPassword: " + encryptorPassword); Method methodSetup = consoleProxyClazz.getMethod( "setEncryptorPassword", String.class); methodSetup.invoke(null, encryptorPassword); + s_logger.info("Invoke startWithContext()"); Method method = consoleProxyClazz.getMethod( "startWithContext", Properties.class, Object.class, byte[].class, String.class); method.invoke(null, _properties, resource, ksBits, ksPassword); } catch (SecurityException e) { - s_logger.error("Unable to launch console proxy due to SecurityException"); + s_logger.error("Unable to launch console proxy due to SecurityException", e); System.exit(ExitStatus.Error.value()); } catch (NoSuchMethodException e) { - s_logger.error("Unable to launch console proxy due to NoSuchMethodException"); + s_logger.error("Unable to launch console proxy due to NoSuchMethodException", e); System.exit(ExitStatus.Error.value()); } catch (IllegalArgumentException e) { - s_logger.error("Unable to launch console proxy due to IllegalArgumentException"); + s_logger.error("Unable to launch console proxy due to IllegalArgumentException", e); System.exit(ExitStatus.Error.value()); } catch (IllegalAccessException e) { - s_logger.error("Unable to launch console proxy due to IllegalAccessException"); + s_logger.error("Unable to launch console proxy due to IllegalAccessException", e); System.exit(ExitStatus.Error.value()); } catch (InvocationTargetException e) { - s_logger.error("Unable to launch console proxy due to InvocationTargetException"); + s_logger.error("Unable to launch console proxy due to InvocationTargetException " + e.getTargetException().toString(), e); System.exit(ExitStatus.Error.value()); } } catch (final ClassNotFoundException e) { @@ -402,22 +405,22 @@ public class ConsoleProxyResource extends ServerResourceBase implements Method methodSetup = consoleProxyClazz.getMethod("setEncryptorPassword", String.class); methodSetup.invoke(null, encryptorPassword); } catch (SecurityException e) { - s_logger.error("Unable to launch console proxy due to SecurityException"); + s_logger.error("Unable to launch console proxy due to SecurityException", e); System.exit(ExitStatus.Error.value()); } catch (NoSuchMethodException e) { - s_logger.error("Unable to launch console proxy due to NoSuchMethodException"); + s_logger.error("Unable to launch console proxy due to NoSuchMethodException", e); System.exit(ExitStatus.Error.value()); } catch (IllegalArgumentException e) { - s_logger.error("Unable to launch console proxy due to IllegalArgumentException"); + s_logger.error("Unable to launch console proxy due to IllegalArgumentException", e); System.exit(ExitStatus.Error.value()); } catch (IllegalAccessException e) { - s_logger.error("Unable to launch console proxy due to IllegalAccessException"); + s_logger.error("Unable to launch console proxy due to IllegalAccessException", e); System.exit(ExitStatus.Error.value()); } catch (InvocationTargetException e) { - s_logger.error("Unable to launch console proxy due to InvocationTargetException"); + s_logger.error("Unable to launch console proxy due to InvocationTargetException " + e.getTargetException().toString(), e); System.exit(ExitStatus.Error.value()); } catch (final ClassNotFoundException e) { - s_logger.error("Unable to launch console proxy due to ClassNotFoundException"); + s_logger.error("Unable to launch console proxy due to ClassNotFoundException", e); System.exit(ExitStatus.Error.value()); } } 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 73cc8e3e5a2..a94d49ebfab 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -374,6 +374,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { CommandExecLogVO execLog = new CommandExecLogVO(cmdTarget.first().getId(), cmdTarget.second().getId(), cmd.getClass().getSimpleName(), 1); _cmdExecLogDao.persist(execLog); cmd.setContextParam("execid", String.valueOf(execLog.getId())); + cmd.setContextParam("noderuninfo", String.format("%d-%d", _clusterMgr.getManagementNodeId(), _clusterMgr.getCurrentRunId())); if(cmd instanceof BackupSnapshotCommand || cmd instanceof CreatePrivateTemplateFromVolumeCommand || diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java index f9f5f7e7e39..6c675990bb3 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java @@ -52,6 +52,7 @@ public interface VmwareManager { VmwareStorageManager getStorageManager(); void gcLeftOverVMs(VmwareContext context); + boolean needRecycle(String workerTag); Pair getAddiionalVncPortRange(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index a1671c9fa90..a04a6eb92cd 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -52,6 +52,8 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.cluster.ClusterManager; +import com.cloud.cluster.ManagementServerHost; +import com.cloud.cluster.dao.ManagementServerHostPeerDao; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterDetailsDao; @@ -154,6 +156,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Inject VmwareDatacenterDao _vmwareDcDao; @Inject VmwareDatacenterZoneMapDao _vmwareDcZoneMapDao; @Inject LegacyZoneDao _legacyZoneDao; + @Inject ManagementServerHostPeerDao _mshostPeerDao; String _mountParent; StorageLayer _storage; @@ -167,6 +170,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw String _managemetPortGroupName; String _defaultSystemVmNicAdapterType = VirtualEthernetCardType.E1000.toString(); String _recycleHungWorker = "false"; + long _hungWorkerTimeout = 7200000; // 2 hour int _additionalPortRangeStart; int _additionalPortRangeSize; int _routerExtraPublicNics = 2; @@ -285,6 +289,10 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw if(_recycleHungWorker == null || _recycleHungWorker.isEmpty()) { _recycleHungWorker = "false"; } + + value = _configDao.getValue(Config.VmwareHungWorkerTimeout.key()); + if(value != null) + _hungWorkerTimeout = Long.parseLong(value) * 1000; _rootDiskController = _configDao.getValue(Config.VmwareRootDiskControllerType.key()); if(_rootDiskController == null || _rootDiskController.isEmpty()) { @@ -528,11 +536,50 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw return _storageMgr; } - @Override public void gcLeftOverVMs(VmwareContext context) { VmwareCleanupMaid.gcLeftOverVMs(context); } + + @Override + public boolean needRecycle(String workerTag) { + if(s_logger.isInfoEnabled()) + s_logger.info("Check to see if a worker VM with tag " + workerTag + " needs to be recycled"); + + if(workerTag == null || workerTag.isEmpty()) { + s_logger.error("Invalid worker VM tag " + workerTag); + return false; + } + + String tokens[] = workerTag.split("-"); + if(tokens.length != 3) { + s_logger.error("Invalid worker VM tag " + workerTag); + return false; + } + + long startTick = Long.parseLong(tokens[0]); + long msid = Long.parseLong(tokens[1]); + long runid = Long.parseLong(tokens[2]); + + if(_mshostPeerDao.countStateSeenInPeers(msid, runid, ManagementServerHost.State.Down) > 0) { + if(s_logger.isInfoEnabled()) + s_logger.info("Worker VM's owner management server node has been detected down from peer nodes, recycle it"); + return true; + } + + if(msid == _clusterMgr.getManagementNodeId() && runid != _clusterMgr.getCurrentRunId()) { + if(s_logger.isInfoEnabled()) + s_logger.info("Worker VM's owner management server has changed runid, recycle it"); + return true; + } + + if(System.currentTimeMillis() - startTick > _hungWorkerTimeout) { + if(s_logger.isInfoEnabled()) + s_logger.info("Worker VM expired, seconds elapsed: " + (System.currentTimeMillis() - startTick) / 1000); + return true; + } + return false; + } @Override public void prepareSecondaryStorageStore(String storageUrl) { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index 955b111339a..2d626c80f74 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -329,14 +329,8 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { dsMo = new DatastoreMO(hyperHost.getContext(), morDs); workerVMName = hostService.getWorkerName(context, cmd, 0); - - // attach a volume to dummay wrapper VM for taking snapshot and exporting the VM for backup - if (!hyperHost.createBlankVm(workerVMName, null, 1, 512, 0, false, 4, 0, VirtualMachineGuestOsIdentifier.OTHER_GUEST.value(), morDs, false)) { - String msg = "Unable to create worker VM to execute BackupSnapshotCommand"; - s_logger.error(msg); - throw new Exception(msg); - } - vmMo = hyperHost.findVmOnHyperHost(workerVMName); + vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVMName); + if (vmMo == null) { throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName); } @@ -1059,28 +1053,8 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { if (vmMo == null) { // create a dummy worker vm for attaching the volume DatastoreMO dsMo = new DatastoreMO(hyperHost.getContext(), morDs); - //restrict VM name to 32 chars, (else snapshot descriptor file name will be truncated to 32 chars of vm name) - VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); - vmConfig.setName(workerVmName); - vmConfig.setMemoryMB((long) 4); - vmConfig.setNumCPUs(1); - vmConfig.setGuestId(VirtualMachineGuestOsIdentifier.OTHER_GUEST.value()); - VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); - fileInfo.setVmPathName(String.format("[%s]", dsMo.getName())); - vmConfig.setFiles(fileInfo); - - // Scsi controller - VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); - scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); - scsiController.setBusNumber(0); - scsiController.setKey(1); - VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); - scsiControllerSpec.setDevice(scsiController); - scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); - vmConfig.getDeviceChange().add(scsiControllerSpec); - - hyperHost.createVm(vmConfig); - workerVm = hyperHost.findVmOnHyperHost(workerVmName); + workerVm = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVmName); + if (workerVm == null) { String msg = "Unable to create worker VM to execute CopyVolumeCommand"; s_logger.error(msg); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java index c0b716cc394..ed607e118d2 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java @@ -22,6 +22,7 @@ import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.cluster.ClusterManager; import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareContext; @@ -34,9 +35,11 @@ public class VmwareContextFactory { private static volatile int s_seq = 1; private static VmwareManager s_vmwareMgr; + private static ClusterManager s_clusterMgr; private static VmwareContextPool s_pool; @Inject VmwareManager _vmwareMgr; + @Inject ClusterManager _clusterMgr; static { // skip certificate check @@ -47,6 +50,7 @@ public class VmwareContextFactory { @PostConstruct void init() { s_vmwareMgr = _vmwareMgr; + s_clusterMgr = _clusterMgr; } public static VmwareContext create(String vCenterAddress, String vCenterUserName, String vCenterPassword) throws Exception { @@ -66,6 +70,7 @@ public class VmwareContextFactory { context.registerStockObject("serviceconsole", s_vmwareMgr.getServiceConsolePortGroupName()); context.registerStockObject("manageportgroup", s_vmwareMgr.getManagementPortGroupName()); + context.registerStockObject("noderuninfo", String.format("%d-%d", s_clusterMgr.getManagementNodeId(), s_clusterMgr.getCurrentRunId())); context.setPoolInfo(s_pool, VmwareContextPool.composePoolKey(vCenterAddress, vCenterUserName)); s_pool.registerOutstandingContext(context); @@ -83,6 +88,7 @@ public class VmwareContextFactory { context.registerStockObject("serviceconsole", s_vmwareMgr.getServiceConsolePortGroupName()); context.registerStockObject("manageportgroup", s_vmwareMgr.getManagementPortGroupName()); + context.registerStockObject("noderuninfo", String.format("%d-%d", s_clusterMgr.getManagementNodeId(), s_clusterMgr.getCurrentRunId())); } return context; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 0990b3afb9f..0e9ce93a8ab 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -29,7 +29,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -321,18 +320,14 @@ import com.vmware.vim25.VirtualDisk; import com.vmware.vim25.VirtualEthernetCard; import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo; import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; -import com.vmware.vim25.VirtualLsiLogicController; import com.vmware.vim25.VirtualMachineConfigSpec; -import com.vmware.vim25.VirtualMachineFileInfo; import com.vmware.vim25.VirtualMachineGuestOsIdentifier; import com.vmware.vim25.VirtualMachinePowerState; import com.vmware.vim25.VirtualMachineRelocateSpec; import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator; import com.vmware.vim25.VirtualMachineRuntimeInfo; -import com.vmware.vim25.VirtualSCSISharing; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; - public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService { private static final Logger s_logger = Logger.getLogger(VmwareResource.class); @@ -368,7 +363,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected boolean _fullCloneFlag = false; protected boolean _instanceNameFlag = false; - protected boolean _recycleHungWorker = false; protected DiskControllerType _rootDiskController = DiskControllerType.ide; @@ -4547,7 +4541,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (!dsMo.fileExists(volumeDatastorePath)) { String dummyVmName = getWorkerName(context, cmd, 0); - VirtualMachineMO vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName); + VirtualMachineMO vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, dummyVmName); if (vmMo == null) { throw new Exception("Unable to create a dummy VM for volume creation"); @@ -5702,7 +5696,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VirtualMachineMO vmMo = null; try { - vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName); + vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, dummyVmName); if (vmMo == null) { throw new Exception("Unable to create a dummy VM for volume creation"); } @@ -5759,7 +5753,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumeUuid); String dummyVmName = getWorkerName(context, cmd, 0); try { - vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName); + vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, dummyVmName); if (vmMo == null) { throw new Exception("Unable to create a dummy VM for volume creation"); } @@ -5794,34 +5788,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return (int)(bytes / (1024L * 1024L)); } - protected VirtualMachineMO prepareVolumeHostDummyVm(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception { - assert (hyperHost != null); - - VirtualMachineMO vmMo = null; - VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); - vmConfig.setName(vmName); - vmConfig.setMemoryMB((long) 4); // vmware request minimum of 4 MB - vmConfig.setNumCPUs(1); - vmConfig.setGuestId(VirtualMachineGuestOsIdentifier.OTHER_GUEST.value()); - VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); - fileInfo.setVmPathName(String.format("[%s]", dsMo.getName())); - vmConfig.setFiles(fileInfo); - - // Scsi controller - VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); - scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); - scsiController.setBusNumber(0); - scsiController.setKey(1); - VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); - scsiControllerSpec.setDevice(scsiController); - scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); - - vmConfig.getDeviceChange().add(scsiControllerSpec ); - hyperHost.createVm(vmConfig); - vmMo = hyperHost.findVmOnHyperHost(vmName); - return vmMo; - } - @Override public void disconnected() { } @@ -5848,70 +5814,53 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if(hyperHost.isHyperHostConnected()) { mgr.gcLeftOverVMs(context); - if(_recycleHungWorker) { - s_logger.info("Scan hung worker VM to recycle"); - - int key = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); - if(key == 0) { - s_logger.warn("Custom field " + CustomFieldConstants.CLOUD_VM_INTERNAL_NAME + " is not registered ?!"); - } - String instanceNameCustomField = "value[" + key + "]"; - - // GC worker that has been running for too long - ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost( - new String[] {"name", "config.template", "runtime.powerState", "runtime.bootTime", instanceNameCustomField }); - if(ocs != null) { - for(ObjectContent oc : ocs) { - List props = oc.getPropSet(); - if(props != null) { - String vmName = null; - String internalName = null; - boolean template = false; - VirtualMachinePowerState powerState = VirtualMachinePowerState.POWERED_OFF; - GregorianCalendar bootTime = null; - - for(DynamicProperty prop : props) { - if (prop.getName().equals("name")) - vmName = prop.getVal().toString(); - else if(prop.getName().startsWith("value[")) { - if(prop.getVal() != null) - internalName = ((CustomFieldStringValue)prop.getVal()).getValue(); - } - else if(prop.getName().equals("config.template")) - template = (Boolean)prop.getVal(); - else if(prop.getName().equals("runtime.powerState")) - powerState = (VirtualMachinePowerState)prop.getVal(); - else if(prop.getName().equals("runtime.bootTime")) - bootTime = (GregorianCalendar)prop.getVal(); - } - - VirtualMachineMO vmMo = new VirtualMachineMO(hyperHost.getContext(), oc.getObj()); - String name = null; - if (internalName != null) { - name = internalName; - } else { - name = vmName; - } - - if(!template && name.matches("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")) { - boolean recycle = false; - - // recycle stopped worker VM and VM that has been running for too long (hard-coded 10 hours for now) - if(powerState == VirtualMachinePowerState.POWERED_OFF) - recycle = true; - else if(bootTime != null && (new Date().getTime() - bootTime.getTimeInMillis() > 10*3600*1000)) - recycle = true; - - if(recycle) { - s_logger.info("Recycle pending worker VM: " + name); - - vmMo.powerOff(); - vmMo.destroy(); - } - } - } - } - } + s_logger.info("Scan hung worker VM to recycle"); + + int workerKey = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_WORKER); + int workerTagKey = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_WORKER_TAG); + String workerPropName = String.format("value[%d]", workerKey); + String workerTagPropName = String.format("value[%d]", workerTagKey); + + // GC worker that has been running for too long + ObjectContent[] ocs = hyperHost.getVmPropertiesOnHyperHost( + new String[] {"name", "config.template", workerPropName, workerTagPropName, + }); + if(ocs != null) { + for(ObjectContent oc : ocs) { + List props = oc.getPropSet(); + if(props != null) { + boolean template = false; + boolean isWorker = false; + String workerTag = null; + + for(DynamicProperty prop : props) { + if(prop.getName().equals("config.template")) { + template = (Boolean)prop.getVal(); + } else if(prop.getName().equals(workerPropName)) { + CustomFieldStringValue val = (CustomFieldStringValue)prop.getVal(); + if(val != null && val.getValue() != null && val.getValue().equalsIgnoreCase("true")) + isWorker = true; + } + else if(prop.getName().equals(workerTagPropName)) { + CustomFieldStringValue val = (CustomFieldStringValue)prop.getVal(); + workerTag = val.getValue(); + } + } + + VirtualMachineMO vmMo = new VirtualMachineMO(hyperHost.getContext(), oc.getObj()); + if(!template && isWorker) { + boolean recycle = false; + recycle = mgr.needRecycle(workerTag); + + if(recycle) { + s_logger.info("Recycle pending worker VM: " + vmMo.getName()); + + vmMo.powerOff(); + vmMo.destroy(); + } + } + } + } } } else { s_logger.error("Host is no longer connected."); @@ -6692,6 +6641,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_UUID); cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_NIC_MASK); cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME); + cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_WORKER); + cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_WORKER_TAG); VmwareHypervisorHost hostMo = this.getHyperHost(context); _hostName = hostMo.getHyperHostName(); 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 dcf71cb0e03..2c302ab29fc 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -218,6 +218,7 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe if (context != null) { context.registerStockObject("serviceconsole", cmd.getContextParam("serviceconsole")); context.registerStockObject("manageportgroup", cmd.getContextParam("manageportgroup")); + context.registerStockObject("noderuninfo", cmd.getContextParam("noderuninfo")); } currentContext.set(context); return context; diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 14fca3afd8d..5dad90aee46 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -173,7 +173,9 @@ public class VmwareStorageProcessor implements StorageProcessor { } if(vmMo.createSnapshot("cloud.template.base", "Base snapshot", false, false)) { - vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_UUID, templateUuid); + // the same template may be deployed with multiple copies at per-datastore per-host basis, + // save the original template name from CloudStack DB as the UUID to associate them. + vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_UUID, templateName); vmMo.markAsTemplate(); } else { vmMo.destroy(); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 8dd5c8b07eb..d4f9607c0cd 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -298,6 +298,7 @@ public enum Config { VmwareRootDiskControllerType("Advanced", ManagementServer.class, String.class, "vmware.root.disk.controller", "ide", "Specify the default disk controller for root volumes, valid values are scsi, ide", null), VmwareSystemVmNicDeviceType("Advanced", ManagementServer.class, String.class, "vmware.systemvm.nic.device.type", "E1000", "Specify the default network device type for system VMs, valid values are E1000, PCNet32, Vmxnet2, Vmxnet3", null), VmwareRecycleHungWorker("Advanced", ManagementServer.class, Boolean.class, "vmware.recycle.hung.wokervm", "false", "Specify whether or not to recycle hung worker VMs", null), + VmwareHungWorkerTimeout("Advanced", ManagementServer.class, Long.class, "vmware.hung.wokervm.timeout", "7200", "Worker VM timeout in seconds", null), VmwareEnableNestedVirtualization("Advanced", ManagementServer.class, Boolean.class, "vmware.nested.virtualization", "false", "When set to true this will enable nested virtualization when this is supported by the hypervisor", null), // Midonet diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java index 139d377591c..47c2d3873ee 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java @@ -23,4 +23,6 @@ public interface CustomFieldConstants { public final static String CLOUD_NIC_MASK = "cloud.nic.mask"; public final static String CLOUD_ZONE = "cloud.zone"; public final static String CLOUD_VM_INTERNAL_NAME = "cloud.vm.internal.name"; + public final static String CLOUD_WORKER = "cloud.vm.worker"; + public final static String CLOUD_WORKER_TAG = "cloud.vm.worker.tag"; } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index 014a9f8ba47..c7f16611e52 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -1237,8 +1237,24 @@ public class HypervisorHostHelper { scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); vmConfig.getDeviceChange().add(scsiControllerSpec); - hyperHost.createVm(vmConfig); - workingVM = hyperHost.findVmOnHyperHost(vmName); + if(hyperHost.createVm(vmConfig)) { + // Ugly work-around, it takes time for newly created VM to appear + for(int i = 0; i < 10 && workingVM == null; i++) { + workingVM = hyperHost.findVmOnHyperHost(vmName); + + try { + Thread.sleep(1000); + } catch(InterruptedException e) { + } + } + } + + if(workingVM != null) { + workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER, "true"); + String workerTag = String.format("%d-%s", System.currentTimeMillis(), + hyperHost.getContext().getStockObject("noderuninfo")); + workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER_TAG, workerTag); + } return workingVM; } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index f7118597cb2..a6df40e2300 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1559,15 +1559,11 @@ public class VirtualMachineMO extends BaseMO { assert(disks.length >= 1); HostMO hostMo = getRunningHost(); - VirtualMachineConfigInfo vmConfigInfo = getConfigInfo(); - if(!hostMo.createBlankVm(clonedVmName, null, 1, cpuSpeedMHz, 0, false, memoryMb, 0, vmConfigInfo.getGuestId(), morDs, false)) - throw new Exception("Unable to create a blank VM"); - - VirtualMachineMO clonedVmMo = hostMo.findVmOnHyperHost(clonedVmName); + VirtualMachineMO clonedVmMo = HypervisorHostHelper.createWorkerVM(hostMo, new DatastoreMO(hostMo.getContext(), morDs), clonedVmName); if(clonedVmMo == null) throw new Exception("Unable to find just-created blank VM"); - + boolean bSuccess = false; try { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); From d28cca7bf0907e17bded24a736018809c12fa479 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Fri, 13 Sep 2013 16:53:11 -0700 Subject: [PATCH 181/251] CLOUDSTACK-4671:ListZone API failed with Assertion error if assertion is turned on for MS. --- server/src/com/cloud/api/query/QueryManagerImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 879e74da271..615e207d1a4 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -2508,7 +2508,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sdc.addOr("accountId", SearchCriteria.Op.EQ, account.getId()); sdc.addOr("accountId", SearchCriteria.Op.NULL); - sc.addAnd("account", SearchCriteria.Op.SC, sdc); + sc.addAnd("accountId", SearchCriteria.Op.SC, sdc); } } else if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) { @@ -2537,7 +2537,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { SearchCriteria sdc = _dcJoinDao.createSearchCriteria(); sdc.addOr("domainId", SearchCriteria.Op.IN, domainIds.toArray()); sdc.addOr("domainId", SearchCriteria.Op.NULL); - sc.addAnd("domain", SearchCriteria.Op.SC, sdc); + sc.addAnd("domainId", SearchCriteria.Op.SC, sdc); // remove disabled zones sc.addAnd("allocationState", SearchCriteria.Op.NEQ, Grouping.AllocationState.Disabled); @@ -2548,7 +2548,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sdc2.addOr("accountId", SearchCriteria.Op.EQ, account.getId()); sdc2.addOr("accountId", SearchCriteria.Op.NULL); - sc.addAnd("account", SearchCriteria.Op.SC, sdc2); + sc.addAnd("accountId", SearchCriteria.Op.SC, sdc2); // remove Dedicated zones not dedicated to this domainId or // subdomainId @@ -2588,7 +2588,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { SearchCriteria sdc = _dcJoinDao.createSearchCriteria(); sdc.addOr("domainId", SearchCriteria.Op.IN, domainIds.toArray()); sdc.addOr("domainId", SearchCriteria.Op.NULL); - sc.addAnd("domain", SearchCriteria.Op.SC, sdc); + sc.addAnd("domainId", SearchCriteria.Op.SC, sdc); // remove disabled zones sc.addAnd("allocationState", SearchCriteria.Op.NEQ, Grouping.AllocationState.Disabled); @@ -2617,7 +2617,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { if (dcIds.size() == 0) { return new Pair, Integer>(new ArrayList(), 0); } else { - sc.addAnd("idIn", SearchCriteria.Op.IN, dcIds.toArray()); + sc.addAnd("id", SearchCriteria.Op.IN, dcIds.toArray()); } } From f4e4be01ff1f7cd2d1aafb2bb3dc2b4cd94cec58 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Fri, 13 Sep 2013 13:46:29 -0700 Subject: [PATCH 182/251] CLOUDSTACK-4668: Upgrade to 4.2 fails with NullPointerException when hypervisor_type is null for a cluster entry Changes: - Care for null value during comparing the hypervisor_type - Do not consider removed clusters - Method rename according to coding conventions --- .../schema/src/com/cloud/upgrade/dao/Upgrade410to420.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 3c753e90156..43f03c057f9 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -83,7 +83,7 @@ public class Upgrade410to420 implements DbUpgrade { createPlaceHolderNics(conn); updateRemoteAccessVpn(conn); updateSystemVmTemplates(conn); - updateCluster_details(conn); + updateOverCommitRatioClusterDetails(conn); updatePrimaryStore(conn); addEgressFwRulesForSRXGuestNw(conn); upgradeEIPNetworkOfferings(conn); @@ -898,7 +898,7 @@ public class Upgrade410to420 implements DbUpgrade { } //update the cluster_details table with default overcommit ratios. - private void updateCluster_details(Connection conn) { + private void updateOverCommitRatioClusterDetails(Connection conn) { PreparedStatement pstmt = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 =null; @@ -907,7 +907,7 @@ public class Upgrade410to420 implements DbUpgrade { ResultSet rscpu_global = null; ResultSet rsmem_global = null; try { - pstmt = conn.prepareStatement("select id, hypervisor_type from `cloud`.`cluster`"); + pstmt = conn.prepareStatement("select id, hypervisor_type from `cloud`.`cluster` WHERE removed IS NULL"); pstmt1=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'cpuOvercommitRatio', ?)"); pstmt2=conn.prepareStatement("INSERT INTO `cloud`.`cluster_details` (cluster_id, name, value) VALUES(?, 'memoryOvercommitRatio', ?)"); pstmt3=conn.prepareStatement("select value from `cloud`.`configuration` where name=?"); @@ -926,7 +926,7 @@ public class Upgrade410to420 implements DbUpgrade { while (rs1.next()) { long id = rs1.getLong(1); String hypervisor_type = rs1.getString(2); - if (hypervisor_type.equalsIgnoreCase(HypervisorType.VMware.toString())) { + if (HypervisorType.VMware.toString().equalsIgnoreCase(hypervisor_type)) { pstmt1.setLong(1,id); pstmt1.setString(2,global_cpu_overprovisioning_factor); pstmt1.execute(); From f3be68a83301837d14243fec8eba9927565b928e Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Fri, 13 Sep 2013 14:37:00 -0700 Subject: [PATCH 183/251] CLOUDSTACK-4664: [ZWPS] High delay to start a stopped VM which has ROOT/DATA volumes migrated to Second Zone wide primary Storage(More than 10 mins) Changes: - DeploymentPlanningManager needs to reuse the zone wide primary storage pool when VM's volume is READY --- .../deploy/DeploymentPlanningManagerImpl.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index b73aad21893..2ca58c6b50d 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -37,6 +37,7 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.engine.cloud.entity.api.db.VMReservationVO; import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.messagebus.MessageBus; @@ -78,6 +79,7 @@ import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.resource.ResourceState; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.ScopeType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; @@ -1076,11 +1078,24 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy if (!pool.isInMaintenance()) { if (!avoid.shouldAvoid(pool)) { long exstPoolDcId = pool.getDataCenterId(); - long exstPoolPodId = pool.getPodId() != null ? pool.getPodId() : -1; long exstPoolClusterId = pool.getClusterId() != null ? pool.getClusterId() : -1; + boolean canReusePool = false; if (plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId) { + canReusePool = true; + } else if (plan.getDataCenterId() == exstPoolDcId) { + DataStore dataStore = this.dataStoreMgr.getPrimaryDataStore(pool.getId()); + if (dataStore != null && dataStore.getScope() != null + && dataStore.getScope().getScopeType() == ScopeType.ZONE) { + canReusePool = true; + } + } else { + s_logger.debug("Pool of the volume does not fit the specified plan, need to reallocate a pool for this volume"); + canReusePool = false; + } + + if (canReusePool) { s_logger.debug("Planner need not allocate a pool for this volume since its READY"); suitablePools.add(pool); suitableVolumeStoragePools.put(toBeCreated, suitablePools); @@ -1088,8 +1103,6 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy readyAndReusedVolumes.add(toBeCreated); } continue; - } else { - s_logger.debug("Pool of the volume does not fit the specified plan, need to reallocate a pool for this volume"); } } else { s_logger.debug("Pool of the volume is in avoid set, need to reallocate a pool for this volume"); From 8dfd34cd7e0b39bb002ba192026a2dfbb231de61 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Fri, 13 Sep 2013 14:56:43 -0700 Subject: [PATCH 184/251] CLOUDSTACK-4651: Restarting management server when volume Snapshot is still in progress for root volume of a VM , then there is no way to restart VM since the startVM job is stuck forever since the volume is in "Snapshoting" state. Change: -If no volume of the VM is usable, VM cannot be deployed or started. Atleast ROOT volume should always be in usable state to start up the VM --- .../src/com/cloud/deploy/DeploymentPlanningManagerImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 2ca58c6b50d..6d36a070291 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -1054,6 +1054,11 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy Map> suitableVolumeStoragePools = new HashMap>(); List readyAndReusedVolumes = new ArrayList(); + // There should be atleast the ROOT volume of the VM in usable state + if (volumesTobeCreated.isEmpty()) { + throw new CloudRuntimeException("Unable to create deployment, no usable volumes found for the VM"); + } + // for each volume find list of suitable storage pools by calling the // allocators for (VolumeVO toBeCreated : volumesTobeCreated) { From deacb5d7c8a078b507912fab950461fea0bf61a3 Mon Sep 17 00:00:00 2001 From: radhikap Date: Sat, 14 Sep 2013 09:09:28 +0530 Subject: [PATCH 185/251] minor edits on release notes --- docs/en-US/Release_Notes.xml | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 497acacbbb6..ce2a7371628 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -81,14 +81,6 @@ under the License. from a single central Management Server. Usage records can also be consolidated and tracked at the region level, creating reports or invoices for each geographic region. - - - - - - region-overview.png: Nested structure of a region. - -
    Object Storage Plugin Architecture @@ -106,7 +98,7 @@ under the License. that uses the object storage plugin capability introduced in &PRODUCT; 4.2. Several new pluggable service interfaces are available so that different storage providers can develop vendor-specific plugins based on the well-defined contracts that can be - seemlessly managed by &PRODUCT;. + seamlessly managed by &PRODUCT;.
    Zone-Wide Primary Storage @@ -153,14 +145,6 @@ under the License. a configuration file (plugins.js). The next time the user refreshes the UI in the browser, the plugin will appear under the Plugins button in the left navigation bar. - - - - - - plugin4.jpg: The plugin appears in the UI - -
    Networking Enhancements @@ -193,9 +177,6 @@ under the License. - - - @@ -239,16 +220,8 @@ under the License. -
    - Support for KVM - VPC is now supported on KVM hypervisors. -
    -
    - Support for Simultaneously Deploying a VM on VPC and Multiple Shared - Networks - Support for the ability to simultaneously deploy a VM on a VPC tier and one or - more Shared networks is supported. -
    +
    Support for KVMVPC is now supported on KVM + hypervisors.
    Load Balancing Support for VPC In a VPC, you can configure two types of load balancing—external LB and From 391be476dcc6e1519caa01cbcb4cbeba79f1ba43 Mon Sep 17 00:00:00 2001 From: radhikap Date: Sat, 14 Sep 2013 09:16:45 +0530 Subject: [PATCH 186/251] known issues added, 4.1 reference removed From e34c80398e84dd496f3abdebf837bb455a4a457d Mon Sep 17 00:00:00 2001 From: radhikap Date: Sat, 14 Sep 2013 10:51:07 +0530 Subject: [PATCH 187/251] release notes structure issue fixed, links to known issues and fixed issues have been added --- docs/en-US/Release_Notes.xml | 2593 +++++++++++++--------------------- 1 file changed, 954 insertions(+), 1639 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index ce2a7371628..d1def441685 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -57,1007 +57,965 @@ under the License. What's New in 4.2.0 -
    - &PRODUCT; 4.2 includes the following new features. -
    - Features to Support Heterogeneous Workloads - The following new features help &PRODUCT; 4.2 better support both legacy and cloud-era - style zones. -
    - Regions - To increase reliability of the cloud, you can optionally group resources into - geographic regions. A region is the largest available organizational unit within a cloud - deployment. A region is made up of several availability zones, where each zone is - equivalent to a datacenter. Each region is controlled by its own cluster of Management - Servers, running in one of the zones. The zones in a region are typically located in - close geographical proximity. Regions are a useful technique for providing fault - tolerance and disaster recovery. - By grouping zones into regions, the cloud can achieve higher availability and - scalability. User accounts can span regions, so that users can deploy VMs in multiple, - widely-dispersed regions. Even if one of the regions becomes unavailable, the services - are still available to the end-user through VMs deployed in another region. And by - grouping communities of zones under their own nearby Management Servers, the latency of - communications within the cloud is reduced compared to managing widely-dispersed zones - from a single central Management Server. - Usage records can also be consolidated and tracked at the region level, creating - reports or invoices for each geographic region. -
    -
    - Object Storage Plugin Architecture - Artifacts such as templates, ISOs and snapshots are kept in storage which &PRODUCT; - refers to as secondary storage. To improve scalability and performance, as when a number - of hosts access secondary storage concurrently, object storage can be used for secondary - storage. Object storage can also provide built-in high availability capability. When - using object storage, access to secondary storage data can be made available across - multiple zones in a region. This is a huge benefit, as it is no longer necessary to copy - templates, snapshots etc. across zones as would be needed in an NFS-only - environment. - Object storage is provided through third-party software such as Amazon Simple - Storage Service (S3) or any other object storage that supports the S3 interface. These - third party object storages can be integrated with &PRODUCT; by writing plugin software - that uses the object storage plugin capability introduced in &PRODUCT; 4.2. Several new - pluggable service interfaces are available so that different storage providers can - develop vendor-specific plugins based on the well-defined contracts that can be - seamlessly managed by &PRODUCT;. -
    -
    - Zone-Wide Primary Storage - (Supported on KVM and VMware) - In &PRODUCT; 4.2, you can provision primary storage on a per-zone basis. Data - volumes in the primary storage can be attached to any VM on any host in the zone. - In previous &PRODUCT; versions, each cluster had its own primary storage. Data in - the primary storage was directly available only to VMs within that cluster. If a VM in a - different cluster needed some of the data, it must be copied from one cluster to - another, using the zone's secondary storage as an intermediate step. This operation was - unnecessarily time-consuming. -
    -
    - VMware Datacenter Now Visible As a &PRODUCT; Zone - In order to support zone-wide functions for VMware, changes have been made so that - &PRODUCT; is now aware of VMware Datacenters and can map each Datacenter to a &PRODUCT; - zone. Previously, &PRODUCT; was only aware of VMware Clusters, a smaller organizational - unit than Datacenters. This implies that a single &PRODUCT; zone could possibly contain - clusters from different VMware Datacenters. In order for zone-wide functions, such as - zone-wide primary storage, to work for VMware hosts, &PRODUCT; has to make sure that a - zone contains only a single VMware Datacenter. Therefore, when you are creating a new - &PRODUCT; zone, you will now be able to select a VMware Datacenter for the zone. If you - are provisioning multiple VMware Datacenters, each one will be set up as a single zone - in &PRODUCT;. - - If you are upgrading from a previous &PRODUCT; version, and your existing - deployment contains a zone with clusters from multiple VMware Datacenters, that zone - will not be forcibly migrated to the new model. It will continue to function as - before. However, any new zone-wide operations, such as zone-wide primary storage, will - not be available in that zone. - - -
    + &PRODUCT; 4.2 includes the following new features. +
    + Features to Support Heterogeneous Workloads + The following new features help &PRODUCT; 4.2 better support both legacy and cloud-era + style zones. +
    + Regions + To increase reliability of the cloud, you can optionally group resources into + geographic regions. A region is the largest available organizational unit within a cloud + deployment. A region is made up of several availability zones, where each zone is + equivalent to a datacenter. Each region is controlled by its own cluster of Management + Servers, running in one of the zones. The zones in a region are typically located in close + geographical proximity. Regions are a useful technique for providing fault tolerance and + disaster recovery. + By grouping zones into regions, the cloud can achieve higher availability and + scalability. User accounts can span regions, so that users can deploy VMs in multiple, + widely-dispersed regions. Even if one of the regions becomes unavailable, the services are + still available to the end-user through VMs deployed in another region. And by grouping + communities of zones under their own nearby Management Servers, the latency of + communications within the cloud is reduced compared to managing widely-dispersed zones + from a single central Management Server. + Usage records can also be consolidated and tracked at the region level, creating + reports or invoices for each geographic region.
    -
    - Third-Party UI Plugin Framework - Using the new third-party plugin framework, you can write and install extensions to - &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the - Citrix-provided features. - The basic procedure for adding a UI plugin is explained in the Developer Guide. In - summary, the plugin developer creates the plugin code itself (in Javascript), a thumbnail - image, the plugin listing, and a CSS file. The &PRODUCT; administrator adds the folder - containing the plugin code under the &PRODUCT; PLUGINS folder and adds the plugin name to - a configuration file (plugins.js). - The next time the user refreshes the UI in the browser, the plugin will appear under - the Plugins button in the left navigation bar. +
    + Object Storage Plugin Architecture + Artifacts such as templates, ISOs and snapshots are kept in storage which &PRODUCT; + refers to as secondary storage. To improve scalability and performance, as when a number + of hosts access secondary storage concurrently, object storage can be used for secondary + storage. Object storage can also provide built-in high availability capability. When using + object storage, access to secondary storage data can be made available across multiple + zones in a region. This is a huge benefit, as it is no longer necessary to copy templates, + snapshots etc. across zones as would be needed in an NFS-only environment. + Object storage is provided through third-party software such as Amazon Simple Storage + Service (S3) or any other object storage that supports the S3 interface. These third party + object storages can be integrated with &PRODUCT; by writing plugin software that uses the + object storage plugin capability introduced in &PRODUCT; 4.2. Several new pluggable + service interfaces are available so that different storage providers can develop + vendor-specific plugins based on the well-defined contracts that can be seamlessly managed + by &PRODUCT;.
    -
    - Networking Enhancements - The following new features provide additional networking functionality in &PRODUCT; - 4.2. -
    - IPv6 (Technical Preview) - &PRODUCT; 4.2 introduces initial support for IPv6. This feature is provided as a - technical preview only. Full support is planned for a future release. -
    -
    - Portable IPs - Portable IPs in &PRODUCT; are elastic IPs that can be transferred across - geographically separated zones. As an administrator, you can provision a pool of - portable IPs at region level and are available for user consumption. The users can - acquire portable IPs if admin has provisioned portable public IPs at the region level - they are part of. These IPs can be used for any service within an advanced zone. You can - also use portable IPs for EIP service in Basic zones. Additionally, a portable IP can be - transferred from one network to another network. -
    -
    - N-Tier Applications - In &PRODUCT; 3.0.6, a functionality was added to allow users to create a multi-tier - application connected to a single instance of a Virtual Router that supports inter-VLAN - routing. Such a multi-tier application is called a virtual private cloud (VPC). Users - were also able to connect their multi-tier applications to a private Gateway or a - Site-to-Site VPN tunnel and route certain traffic to those gateways. For &PRODUCT; 4.2, - additional features are implemented to enhance VPC applications. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Support for KVMVPC is now supported on KVM - hypervisors.
    -
    - Load Balancing Support for VPC - In a VPC, you can configure two types of load balancing—external LB and - internal LB. External LB is nothing but a LB rule created to redirect the traffic - received at a public IP of the VPC virtual router. The traffic is load balanced within - a tier based on your configuration. Citrix NetScaler and VPC virtual router are - supported for external LB. When you use internal LB service, traffic received at a - tier is load balanced across different VMs within that tier. For example, traffic - reached at Web tier is redirected to another VM in that tier. External load balancing - devices are not supported for internal LB. The service is provided by a internal LB VM - configured on the target tier. -
    - Load Balancing Within a Tier (External LB) - A &PRODUCT; user or administrator may create load balancing rules that balance - traffic received at a public IP to one or more VMs that belong to a network tier - that provides load balancing service in a VPC. A user creates a rule, specifies an - algorithm, and assigns the rule to a set of VMs within a tier. -
    -
    - Load Balancing Across Tiers - &PRODUCT; supports sharing workload across different tiers within your VPC. - Assume that multiple tiers are set up in your environment, such as Web tier and - Application tier. Traffic to each tier is balanced on the VPC virtual router on the - public side. If you want the traffic coming from the Web tier to the Application - tier to be balanced, use the internal load balancing feature offered by - &PRODUCT;. -
    -
    - Netscaler Support for VPC - Citrix NetScaler is supported for external LB. Certified version for this - feature is NetScaler 10.0 Build 74.4006.e. -
    +
    + Zone-Wide Primary Storage + (Supported on KVM and VMware) + In &PRODUCT; 4.2, you can provision primary storage on a per-zone basis. Data volumes + in the primary storage can be attached to any VM on any host in the zone. + In previous &PRODUCT; versions, each cluster had its own primary storage. Data in the + primary storage was directly available only to VMs within that cluster. If a VM in a + different cluster needed some of the data, it must be copied from one cluster to another, + using the zone's secondary storage as an intermediate step. This operation was + unnecessarily time-consuming. +
    +
    + VMware Datacenter Now Visible As a &PRODUCT; Zone + In order to support zone-wide functions for VMware, changes have been made so that + &PRODUCT; is now aware of VMware Datacenters and can map each Datacenter to a &PRODUCT; + zone. Previously, &PRODUCT; was only aware of VMware Clusters, a smaller organizational + unit than Datacenters. This implies that a single &PRODUCT; zone could possibly contain + clusters from different VMware Datacenters. In order for zone-wide functions, such as + zone-wide primary storage, to work for VMware hosts, &PRODUCT; has to make sure that a + zone contains only a single VMware Datacenter. Therefore, when you are creating a new + &PRODUCT; zone, you will now be able to select a VMware Datacenter for the zone. If you + are provisioning multiple VMware Datacenters, each one will be set up as a single zone in + &PRODUCT;. + + If you are upgrading from a previous &PRODUCT; version, and your existing deployment + contains a zone with clusters from multiple VMware Datacenters, that zone will not be + forcibly migrated to the new model. It will continue to function as before. However, any + new zone-wide operations, such as zone-wide primary storage, will not be available in + that zone. + + +
    +
    +
    + Third-Party UI Plugin Framework + Using the new third-party plugin framework, you can write and install extensions to + &PRODUCT;. The installed and enabled plugins will appear in the UI. + The basic procedure for adding a UI plugin is explained in the Developer Guide. In + summary, the plugin developer creates the plugin code itself (in Javascript), a thumbnail + image, the plugin listing, and a CSS file. The &PRODUCT; administrator adds the folder + containing the plugin code under the &PRODUCT; PLUGINS folder and adds the plugin name to a + configuration file (plugins.js). + The next time the user refreshes the UI in the browser, the plugin will appear under the + Plugins button in the left navigation bar. +
    +
    + Networking Enhancements + The following new features provide additional networking functionality in &PRODUCT; + 4.2. +
    + IPv6 + &PRODUCT; 4.2 introduces initial support for IPv6. This feature is provided as a + technical preview only. Full support is planned for a future release. +
    +
    + Portable IPs + Portable IPs in &PRODUCT; are elastic IPs that can be transferred across + geographically separated zones. As an administrator, you can provision a pool of portable + IPs at region level and are available for user consumption. The users can acquire portable + IPs if admin has provisioned portable public IPs at the region level they are part of. + These IPs can be used for any service within an advanced zone. You can also use portable + IPs for EIP service in Basic zones. Additionally, a portable IP can be transferred from + one network to another network. +
    +
    + N-Tier Applications + In &PRODUCT; 3.0.6, a functionality was added to allow users to create a multi-tier + application connected to a single instance of a Virtual Router that supports inter-VLAN + routing. Such a multi-tier application is called a virtual private cloud (VPC). Users were + also able to connect their multi-tier applications to a private Gateway or a Site-to-Site + VPN tunnel and route certain traffic to those gateways. For &PRODUCT; 4.2, additional + features are implemented to enhance VPC applications. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Support for KVMVPC is now supported on KVM + hypervisors.
    +
    + Load Balancing Support for VPC + In a VPC, you can configure two types of load balancing—external LB and + internal LB. External LB is nothing but a LB rule created to redirect the traffic + received at a public IP of the VPC virtual router. The traffic is load balanced within a + tier based on your configuration. Citrix NetScaler and VPC virtual router are supported + for external LB. When you use internal LB service, traffic received at a tier is load + balanced across different VMs within that tier. For example, traffic reached at Web tier + is redirected to another VM in that tier. External load balancing devices are not + supported for internal LB. The service is provided by a internal LB VM configured on the + target tier. +
    + Load Balancing Within a Tier (External LB) + A &PRODUCT; user or administrator may create load balancing rules that balance + traffic received at a public IP to one or more VMs that belong to a network tier that + provides load balancing service in a VPC. A user creates a rule, specifies an + algorithm, and assigns the rule to a set of VMs within a tier.
    -
    - Enhanced Access Control List - Network Access Control List (ACL) on the VPC virtual router is enhanced. The - network ACLs can be created for the tiers only if the NetworkACL service is supported. - In &PRODUCT; terminology, Network ACL is a group of Network ACL items. Network ACL - items are nothing but numbered rules that are evaluated in order, starting with the - lowest numbered rule. These rules determine whether traffic is allowed in or out of - any tier associated with the network ACL. You need to add the Network ACL items to the - Network ACL, then associate the Network ACL with a tier. Network ACL is associated - with a VPC and can be assigned to multiple VPC tiers within a VPC. A Tier is - associated with a Network ACL at all the times. Each tier can be associated with only - one ACL. - The default Network ACL is used when no ACL is associated. Default behavior is all - incoming traffic to guest networks is blocked and all outgoing traffic from guest - networks is allowed. Default network ACL cannot be removed or modified. -
    - ACL on Private Gateway - The traffic on the VPC private gateway is controlled by creating both ingress - and egress network ACL rules. The ACLs contains both allow and deny rules. As per - the rule, all the ingress traffic to the private gateway interface and all the - egress traffic out from the private gateway interface are blocked. You can change - this default behaviour while creating a private gateway. -
    -
    - Allow ACL on All Level 4 Protocols - In addition to the existing protocol support for ICMP, TCP, UDP, support for All - Level 4 protocols is added. The protocol numbers from 0 to 255 are supported. -
    -
    - Support for ACL Deny Rules - In addition to the existing support for ACL Allow rules, support for ACL Deny - rules has been added in &PRODUCT; 4.2. As part of this, two operations are - supported: Number and Action. You can configure a rule, allow or deny, by using - action. Use Number to add a rule number. -
    +
    + Load Balancing Across Tiers + &PRODUCT; supports sharing workload across different tiers within your VPC. Assume + that multiple tiers are set up in your environment, such as Web tier and Application + tier. Traffic to each tier is balanced on the VPC virtual router on the public side. + If you want the traffic coming from the Web tier to the Application tier to be + balanced, use the internal load balancing feature offered by &PRODUCT;.
    -
    - Deploying VMs to a VPC Tier and Shared Networks - &PRODUCT; allows you to deploy VMs on a VPC tier and one or more shared networks. - With this feature, the VMs deployed in a multi-tier application can receive services - offered by a service provider over the shared network. One example of such a service - is monitoring service. -
    -
    - Adding a Private Gateway to a VPC - A private gateway can be added by the root admin only. The VPC private network has - 1:1 relationship with the NIC of the physical network. You can configure multiple - private gateways to a single VPC. No gateways with duplicated VLAN and IP are allowed - in the same data center. -
    - Source NAT on Private Gateway - You might want to deploy multiple VPCs with the same super CIDR and guest tier - CIDR. Therefore, multiple guest VMs from different VPCs can have the same IPs to - reach a enterprise data center through the private gateway. In such cases, a NAT - service need to be configured on the private gateway. If Source NAT is enabled, the - guest VMs in VPC reaches the enterprise network via private gateway IP address by - using the NAT service. - The Source NAT service on a private gateway can be enabled while adding the - private gateway. On deletion of a private gateway, source NAT rules specific to the - private gateway are deleted. -
    -
    - VPN Gateways - Support up to 8 VPN Gateways is added. -
    -
    - Creating a Static Route - &PRODUCT; enables you to specify routing for the VPN connection you create. You - can enter one or CIDR addresses to indicate which traffic is to be routed back to - the gateway. -
    -
    - Blacklisting Routes - &PRODUCT; enables you to block a list of routes so that they are not assigned to - any of the VPC private gateways. Specify the list of routes that you want to - blacklist in the blacklisted.routes global parameter. Note that the - parameter update affects only new static route creations. If you block an existing - static route, it remains intact and continue functioning. You cannot add a static - route if the route is blacklisted for the zone. -
    +
    + Netscaler Support for VPC + Citrix NetScaler is supported for external LB. Certified version for this feature + is NetScaler 10.0 Build 74.4006.e.
    -
    - Assigning VLANs to Isolated Networks - &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. - You can assign a VLAN ID when a network is created, just the way it's done for Shared - networks. - The former behaviour also is supported — VLAN is randomly allocated to a - network from the VNET range of the physical network when the network turns to - Implemented state. The VLAN is released back to the VNET pool when the network shuts - down as a part of the Network Garbage Collection. The VLAN can be re-used either by the - same network when it is implemented again, or by any other network. On each subsequent - implementation of a network, a new VLAN can be assigned. - - You cannot change a VLAN once it's assigned to the network. The VLAN remains with - the network for its entire life cycle. - +
    + Enhanced Access Control List + Network Access Control List (ACL) on the VPC virtual router is enhanced. The network + ACLs can be created for the tiers only if the NetworkACL service is supported. In + &PRODUCT; terminology, Network ACL is a group of Network ACL items. Network ACL items + are nothing but numbered rules that are evaluated in order, starting with the lowest + numbered rule. These rules determine whether traffic is allowed in or out of any tier + associated with the network ACL. You need to add the Network ACL items to the Network + ACL, then associate the Network ACL with a tier. Network ACL is associated with a VPC + and can be assigned to multiple VPC tiers within a VPC. A Tier is associated with a + Network ACL at all the times. Each tier can be associated with only one ACL. + The default Network ACL is used when no ACL is associated. Default behavior is all + incoming traffic to guest networks is blocked and all outgoing traffic from guest + networks is allowed. Default network ACL cannot be removed or modified. +
    + ACL on Private Gateway + The traffic on the VPC private gateway is controlled by creating both ingress and + egress network ACL rules. The ACLs contains both allow and deny rules. As per the + rule, all the ingress traffic to the private gateway interface and all the egress + traffic out from the private gateway interface are blocked. You can change this + default behaviour while creating a private gateway. +
    +
    + Allow ACL on All Level 4 Protocols + In addition to the existing protocol support for ICMP, TCP, UDP, support for All + Level 4 protocols is added. The protocol numbers from 0 to 255 are supported. +
    +
    + Support for ACL Deny Rules + In addition to the existing support for ACL Allow rules, support for ACL Deny + rules has been added in &PRODUCT; 4.2. As part of this, two operations are supported: + Number and Action. You can configure a rule, allow or deny, by using action. Use + Number to add a rule number. +
    -
    - Persistent Networks - &PRODUCT; 4.2 supports Persistent Networks. The network that you can provision - without having to deploy any VMs on it is called a Persistent Network. A Persistent - Network can be part of a VPC or a non-VPC environment. With the addition of this - feature, you will have the ability to create a network in &PRODUCT; in which physical - devices can be deployed without having to run any VMs. Additionally, you can deploy - physical devices on that network. Another advantages is that you can create a VPC with a - tier that consists only physical devices. For example, you might create a VPC for a - three-tier application, deploy VMs for Web and Application tier, and use physical - machines for the Database tier. Another use case is that if you are providing services - by using physical hardware, you can define the network as persistent and therefore even - if all its VMs are destroyed the services will not be discontinued. +
    + Deploying VMs to a VPC Tier and Shared Networks + &PRODUCT; allows you to deploy VMs on a VPC tier and one or more shared networks. + With this feature, the VMs deployed in a multi-tier application can receive services + offered by a service provider over the shared network. One example of such a service is + monitoring service.
    -
    - Cisco VNMC Support - Cisco Virtual Network Management Center (VNMC) provides centralized multi-device and - policy management for Cisco Network Virtual Services. When Cisco VNMC is integrated with - ASA 1000v Cloud Firewall and Cisco Nexus 1000v dvSwitch in &PRODUCT; you will be able - to: - - - Configure Cisco ASA 1000v Firewalls - - - Create and apply security profiles that contain ACL policy sets for both ingress - and egress traffic, and NAT policy sets - - - &PRODUCT; supports Cisco VNMC on Cisco Nexus 1000v dvSwich-enabled VMware - hypervisors. -
    -
    - VMware vNetwork Distributed vSwitch - &PRODUCT; supports VMware vSphere Distributed Switch (VDS) for virtual network - configuration in a VMware vSphere environment. Each vCenter server instance can support - up to 128 VDSs and each VDS can manage up to 500 VMware hosts. &PRODUCT; supports - configuring virtual networks in a deployment with a mix of Virtual Distributed Switch, - Standard Virtual Switch and Nexus 1000v Virtual Switch. -
    -
    - IP Reservation in Isolated Guest Networks - In Isolated guest networks in &PRODUCT; 4.2, a part of the guest IP address space - can be reserved for non-&PRODUCT; VMs or physical servers. To do so, you configure a - range of Reserved IP addresses by specifying the CIDR when a guest network is in - Implemented state. The advantage of having this feature is that if your customers wish - to have non-&PRODUCT; controlled VMs or physical servers on the same network, they can - use a part of the IP address space that is primarily provided to the guest network. When - IP reservation is configured, the administrator can add additional VMs or physical - servers that are not part of &PRODUCT; to the same network and assign them the Reserved - IP addresses. &PRODUCT; guest VMs cannot acquire IPs from the Reserved IP Range. -
    -
    - Dedicated Resources: Public IP Addresses and VLANs Per Account - &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs - exclusively for an account. During zone creation, you can continue to define a set of - VLANs and multiple public IP ranges. This feature extends the functionality to enable - you to dedicate a fixed set of VLANs and guest IP addresses for a tenant. - This feature provides you the following capabilities: - - - Reserve a VLAN range and public IP address range from an Advanced zone and - assign it to an account - - - Disassociate a VLAN and public IP address range from an account - - - - Ensure that you check whether the required range is available and conforms to - account limits. The maximum IPs per account limit cannot be superseded. - -
    -
    - Enhanced Juniper SRX Support for Egress Firewall Rules - Egress firewall rules were previously supported on virtual routers, and now they are - also supported on Juniper SRX external networking devices. - Egress traffic originates from a private network to a public network, such as the - Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed - from a guest network to the Internet. However, you can control the egress traffic in an - Advanced zone by creating egress firewall rules. When an egress firewall rule is - applied, the traffic specific to the rule is allowed and the remaining traffic is - blocked. When all the firewall rules are removed the default policy, Block, is - applied. - - Egress firewall rules are not supported on Shared networks. They are supported - only on Isolated guest networks. - -
    -
    - Configuring the Default Egress Policy - The default egress policy for Isolated guest network can be configured by using - Network offering. Use the create network offering option to determine whether the - default policy should be block or allow all the traffic to the public network from a - guest network. Use this network offering to create the network. If no policy is - specified, by default all the traffic is allowed from the guest network that you create - by using this network offering. - You have two options: Allow and Deny. - If you select Allow for a network offering, by default egress traffic is allowed. - However, when an egress rule is configured for a guest network, rules are applied to - block the specified traffic and rest are allowed. If no egress rules are configured for - the network, egress traffic is accepted. If you select Deny for a network offering, by - default egress traffic for the guest network is blocked. However, when an egress rules - is configured for a guest network, rules are applied to allow the specified traffic. - While implementing a guest network, &PRODUCT; adds the firewall egress rule specific to - the default egress policy for the guest network. - This feature is supported only on virtual router and Juniper SRX. -
    -
    - Non-Contiguous VLAN Ranges - &PRODUCT; provides you with the flexibility to add non contiguous VLAN ranges to - your network. The administrator can either update an existing VLAN range or add multiple - non contiguous VLAN ranges while creating a zone. You can also use the - UpdatephysicalNetwork API to extend the VLAN range. -
    -
    - Isolation in Advanced Zone Using Private VLAN - Isolation of guest traffic in shared networks can be achieved by using Private VLANs - (PVLAN). PVLANs provide Layer 2 isolation between ports within the same VLAN. In a - PVLAN-enabled shared network, a user VM cannot reach other user VM though they can reach - the DHCP server and gateway, this would in turn allow users to control traffic within a - network and help them deploy multiple applications without communication between - application as well as prevent communication with other users’ VMs. - - - Isolate VMs in a shared networks by using Private VLANs. - - - Supported on KVM, XenServer, and VMware hypervisors. - - - PVLAN-enabled shared network can be a part of multiple networks of a guest VM. - - - - For further reading: - - - Understanding Private VLANs - - - Cisco Systems' Private VLANs: - Scalable Security in a Multi-Client Environment - - - Private VLAN (PVLAN) on vNetwork Distributed - Switch - Concept Overview (1010691) - - -
    -
    - Configuring Multiple IP Addresses on a Single NIC - (Supported on XenServer, KVM, and VMware hypervisors) - &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 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. -
    -
    - Adding Multiple IP Ranges - (Supported on KVM, xenServer, and VMware hypervisors) - &PRODUCT; 4.2 provides you with the flexibility to add guest IP ranges from - different subnets in Basic zones and security groups-enabled Advanced zones. For - security groups-enabled Advanced zones, it implies multiple subnets can be added to the - same VLAN. With the addition of this feature, you will be able to add IP address ranges - from the same subnet or from a different one when IP address are exhausted. This would - in turn allows you to employ higher number of subnets and thus reduce the address - management overhead. - Ensure that you manually configure the gateway of the new subnet before adding the - IP range. Note that &PRODUCT; supports only one gateway for a subnet; overlapping - subnets are not currently supported. - You can also delete IP ranges. This operation fails if an IP from the remove range - is in use. If the remove range contains the IP address on which the DHCP server is - running, &PRODUCT; acquires a new IP from the same subnet. If no IP is available in the - subnet, the remove operation fails. - - The feature can only be implemented on IPv4 addresses. - -
    -
    - Support for Multiple Networks in VMs - (Supported on XenServer, VMware and KVM hypervisors) - &PRODUCT; 4.2 provides you the ability to add and remove multiple networks to a VM. - You can remove a network from a VM and add a new network. You can also change the - default network of a VM. With this functionality, hybrid or traditional server loads can - be accommodated with ease. - For adding or removing a NIC to work on VMware, ensure that vm-tools are running on - guest VMs. -
    -
    - Global Server Load Balancing - &PRODUCT; 4.2 supports Global Server Load Balancing (GSLB) functionalities to - provide business continuity by load balancing traffic to an instance on active zones - only in case of zone failures . &PRODUCT; achieve this by extending its functionality of - integrating with NetScaler Application Delivery Controller (ADC), which also provides - various GSLB capabilities, such as disaster recovery and load balancing. The DNS - redirection technique is used to achieve GSLB in &PRODUCT;. In order to support this - functionality, region level services and service provider are introduced. A new service - 'GSLB' is introduced as a region level service. The GSLB service provider is introduced - that will provider the GSLB service. Currently, NetScaler is the supported GSLB provider - in &PRODUCT;. GSLB functionality works in an Active-Active data center environment. - -
    -
    - Enhanced Load Balancing Services Using External Provider on Shared VLANs - Network services like Firewall, Load Balancing, and NAT are now supported in shared - networks created in an advanced zone. In effect, the following network services shall be - made available to a VM in a shared network: Source NAT, Static NAT, Port Forwarding, - Firewall and Load balancing. Subset of these service can be chosen while creating a - network offering for shared networks. Services available in a shared network is defined - by the network offering and the service chosen in the network offering. For example, if - network offering for a shared network has source NAT service enabled, a public IP shall - be provisioned and source NAT is configured on the firewall device to provide public - access to the VMs on the shared network. Static NAT, Port Forwarding, Load Balancing, - and Firewall services shall be available only on the acquired public IPs associated with - a shared network. - Additionally, Netscaler and Juniper SRX firewall device can be configured inline or - side-by-side mode. -
    -
    - Health Checks for Load Balanced Instances - - This feature is supported only on NetScaler version 10.0 and beyond. - - (NetScaler load balancer only) A load balancer rule distributes requests among a - pool of services (a service in this context means an application running on a virtual - machine). When creating a load balancer rule, you can specify a health check which will - ensure that the rule forwards requests only to services that are healthy (running and - available). When a health check is in effect, the load balancer will stop forwarding - requests to any resources that it has found to be unhealthy. If the resource later - becomes available again, the periodic health check (periodicity is configurable) will - discover it and the resource will once again be made available to the load - balancer. - To configure how often the health check is performed by default, use the global - configuration setting healthcheck.update.interval. This default applies to all the - health check policies in the cloud. You can override this value for an individual health - check policy. +
    + Adding a Private Gateway to a VPC + A private gateway can be added by the root admin only. The VPC private network has + 1:1 relationship with the NIC of the physical network. You can configure multiple + private gateways to a single VPC. No gateways with duplicated VLAN and IP are allowed in + the same data center. +
    + Source NAT on Private Gateway + You might want to deploy multiple VPCs with the same super CIDR and guest tier + CIDR. Therefore, multiple guest VMs from different VPCs can have the same IPs to reach + a enterprise data center through the private gateway. In such cases, a NAT service + need to be configured on the private gateway. If Source NAT is enabled, the guest VMs + in VPC reaches the enterprise network via private gateway IP address by using the NAT + service. + The Source NAT service on a private gateway can be enabled while adding the + private gateway. On deletion of a private gateway, source NAT rules specific to the + private gateway are deleted. +
    +
    + VPN Gateways + Support up to 8 VPN Gateways is added. +
    +
    + Creating a Static Route + &PRODUCT; enables you to specify routing for the VPN connection you create. You + can enter one or CIDR addresses to indicate which traffic is to be routed back to the + gateway. +
    +
    + Blacklisting Routes + &PRODUCT; enables you to block a list of routes so that they are not assigned to + any of the VPC private gateways. Specify the list of routes that you want to blacklist + in the blacklisted.routes global parameter. Note that the parameter + update affects only new static route creations. If you block an existing static route, + it remains intact and continue functioning. You cannot add a static route if the route + is blacklisted for the zone. +
    -
    - Host and Virtual Machine Enhancements - The following new features expand the ways you can use hosts and virtual - machines. -
    - VMware DRS Support - The VMware vSphere Distributed Resources Scheduler (DRS) is supported. -
    -
    - Windows 8 and Windows Server 2012 as VM Guest OS - (Supported on XenServer, VMware, and KVM) - Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual - machines. The OS would be made available the same as any other, by uploading an ISO or a - template. The instructions for uploading ISOs and templates are given in the - Administrator's Guide. - - Limitation: When used with VMware hosts, this - feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch - 4. - - -
    -
    - Change Account Ownership of Virtual Machines - A root administrator can now change the ownership of any virtual machine from one - account to any other account. A domain or sub-domain administrator can do the same for - VMs within the domain from one account to any other account in the domain. -
    -
    - Private Pod, Cluster, or Host - Dedicating pod, cluster or host to a specific domain/account means that the - domain/account will have sole access to the dedicated pod, cluster or hosts such that - scalability, security and manageability within a domain/account can be improved. The - resources which belong to that tenant will be placed into that dedicated pod, cluster or - host. -
    -
    - Resizing Volumes - &PRODUCT; provides the ability to resize data disks; &PRODUCT; controls volume size - by using disk offerings. This provides &PRODUCT; administrators with the flexibility to - choose how much space they want to make available to the end users. Volumes within the - disk offerings with the same storage tag can be resized. For example, if you only want - to offer 10, 50, and 100 GB offerings, the allowed resize should stay within those - limits. That implies if you define a 10 GB, a 50 GB and a 100 GB disk offerings, a user - can upgrade from 10 GB to 50 GB, or 50 GB to 100 GB. If you create a custom-sized disk - offering, then you have the option to resize the volume by specifying a new, larger - size. Additionally, using the resizeVolume API, a data volume can be moved from a static - disk offering to a custom disk offering with the size specified. This functionality - allows those who might be billing by certain volume sizes or disk offerings to stick to - that model, while providing the flexibility to migrate to whatever custom size - necessary. This feature is supported on KVM, XenServer, and VMware hosts. However, - shrinking volumes is not supported on VMware hosts -
    -
    - VMware Volume Snapshot Improved Performance - When you take a snapshot of a data volume on VMware, &PRODUCT; will now use a more - efficient storage technique to improve performance. - Previously, every snapshot was immediately exported from vCenter to a mounted NFS - share and packaged into an OVA file format. This operation consumed time and resources. - Starting from 4.2, the original file formats (e.g., VMDK) provided by vCenter will be - retained. An OVA file will only be created as needed, on demand. - The new process applies only to newly created snapshots after upgrade to &PRODUCT; - 4.2. Snapshots that have already been taken and stored in OVA format will continue to - exist in that format, and will continue to work as expected. -
    -
    - Storage Migration: XenMotion and vMotion - (Supported on XenServer and VMware) - Storage migration allows VMs to be moved from one host to another, where the VMs are - not located on storage shared between the two hosts. It provides the option to live - migrate a VM’s disks along with the VM itself. It is now possible to migrate a VM from - one XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks - are on local storage, or even to migrate a VM’s disks from one storage repository to - another, all while the VM is running. -
    -
    - Configuring Usage of Linked Clones on VMware - (For ESX hypervisor in conjunction with vCenter) - In &PRODUCT; 4.2, the creation of VMs as full clones is allowed. In previous - versions, only linked clones were possible. - For a full description of clone types, refer to VMware documentation. In summary: A - full clone is a copy of an existing virtual machine which, once created, does not depend - in any way on the original virtual machine. A linked clone is also a copy of an existing - virtual machine, but it has ongoing dependency on the original. A linked clone shares - the virtual disk of the original VM, and retains access to all files that were present - at the time the clone was created. - A new global configuration setting has been added, vmware.create.full.clone. When - the administrator sets this to true, end users can create guest VMs only as full clones. - The default value is true for new installations. For customers upgrading from a previous - version of &PRODUCT;, the default value of vmware.create.full.clone is false. -
    -
    - VM Deployment Rules - Rules can be set up to ensure that particular VMs are not placed on the same - physical host. These "anti-affinity rules" can increase the reliability of applications - by ensuring that the failure of a single host can not take down the entire group of VMs - supporting a given application. See Affinity Groups in the &PRODUCT; 4.2 Administration - Guide. -
    -
    - CPU and Memory Scaling for Running VMs - (Supported on VMware and XenServer) - You can now change the CPU and RAM values for a running virtual machine. In previous - versions of &PRODUCT;, this could only be done on a stopped VM. - It is not always possible to accurately predict the CPU and RAM requirements when - you first deploy a VM. You might need to increase or decrease these resources at any - time during the life of a VM. With the new ability to dynamically modify CPU and RAM - levels, you can change these resources for a running VM without incurring any - downtime. - Dynamic CPU and RAM scaling can be used in the following cases: - - - New VMs that are created after the installation of &PRODUCT; 4.2. If you are - upgrading from a previous version of &PRODUCT;, your existing VMs created with - previous versions will not have the dynamic scaling capability. - - - User VMs on hosts running VMware and XenServer. - - - System VMs on VMware. - - - VM Tools or XenServer Tools must be installed on the virtual machine. - - - The new requested CPU and RAM values must be within the constraints allowed by - the hypervisor and the VM operating system. - - - To configure this feature, use the following new global configuration - variables: - - - enable.dynamic.scale.vm: Set to True to enable the feature. By default, the - feature is turned off. - - - scale.retry: How many times to attempt the scaling operation. Default = - 2. - - -
    -
    - CPU and Memory Over-Provisioning - (Supported for XenServer, KVM, and VMware) - In &PRODUCT; 4.2, CPU and memory (RAM) over-provisioning factors can be set for each - cluster to change the number of VMs that can run on each host in the cluster. This helps - optimize the use of resources. By increasing the over-provisioning ratio, more resource - capacity will be used. If the ratio is set to 1, no over-provisioning is done. - In previous releases, &PRODUCT; did not perform memory over-provisioning. It - performed CPU over-provisioning based on a ratio configured by the administrator in the - global configuration setting cpu.overprovisioning.factor. Starting in 4.2, the - administrator can specify a memory over-provisioning ratio, and can specify both CPU and - memory over-provisioning ratios on a per-cluster basis, rather than only on a global - basis. - In any given cloud, the optimum number of VMs for each host is affected by such - things as the hypervisor, storage, and hardware configuration. These may be different - for each cluster in the same cloud. A single global over-provisioning setting could not - provide the best utilization for all the different clusters in the cloud. It had to be - set for the lowest common denominator. The new per-cluster setting provides a finer - granularity for better utilization of resources, no matter where the &PRODUCT; placement - algorithm decides to place a VM. -
    -
    - Kickstart Installation for Bare Metal Provisioning - &PRODUCT; 4.2 supports the kick start installation method for RPM-based Linux - operating systems on baremetal hosts in basic zones. Users can provision a baremetal - host managed by &PRODUCT; as long as they have the kick start file and corresponding OS - installation ISO ready. - Tested on CentOS 5.5, CentOS 6.2, CentOS 6.3, Ubuntu 12.04. - For more information, see the Baremetal Installation Guide. -
    -
    - Enhanced Bare Metal Support on Cisco UCS - You can now more easily provision new Cisco UCS server blades into &PRODUCT; for use - as bare metal hosts. The goal is to enable easy expansion of the cloud by leveraging the - programmability of the UCS converged infrastructure and &PRODUCT;’s knowledge of the - cloud architecture and ability to orchestrate. With this new feature, &PRODUCT; can - automatically understand the UCS environment, server profiles, etc. to make it easy to - deploy a bare metal OS on a Cisco UCS. -
    -
    - Changing a VM's Base Image - Every VM is created from a base image, which is a template or ISO which has been - created and stored in &PRODUCT;. Both cloud administrators and end users can create and - modify templates, ISOs, and VMs. - In &PRODUCT; 4.2, there is a new way to modify an existing VM. You can change an - existing VM from one base image to another. For example, suppose there is a template - based on a particular operating system, and the OS vendor releases a software patch. The - administrator or user naturally wants to apply the patch and then make sure existing VMs - start using it. Whether a software update is involved or not, it's also possible to - simply switch a VM from its current template to any other desired template. -
    -
    - Reset VM on Reboot - In &PRODUCT; 4.2, you can specify that you want to discard the root disk and create - a new one whenever a given VM is rebooted. This is useful for secure environments that - need a fresh start on every boot and for desktops that should not retain state. The IP - address of the VM will not change due to this operation. -
    -
    - Virtual Machine Snapshots for VMware - (VMware hosts only) In addition to the existing &PRODUCT; ability to snapshot - individual VM volumes, you can now take a VM snapshot to preserve all the VM's data - volumes as well as (optionally) its CPU/memory state. This is useful for quick restore - of a VM. For example, you can snapshot a VM, then make changes such as software - upgrades. If anything goes wrong, simply restore the VM to its previous state using the - previously saved VM snapshot. - The snapshot is created using the VMware native snapshot facility. The VM snapshot - includes not only the data volumes, but optionally also whether the VM is running or - turned off (CPU state) and the memory contents. The snapshot is stored in &PRODUCT;'s - primary storage. - VM snapshots can have a parent/child relationship. Each successive snapshot of the - same VM is the child of the snapshot that came before it. Each time you take an - additional snapshot of the same VM, it saves only the differences between the current - state of the VM and the state stored in the most recent previous snapshot. The previous - snapshot becomes a parent, and the new snapshot is its child. It is possible to create a - long chain of these parent/child snapshots, which amount to a "redo" record leading from - the current state of the VM back to the original. -
    -
    - Increased Userdata Size When Deploying a VM - You can now specify up to 32KB of userdata when deploying a virtual machine through - the &PRODUCT; UI or the deployVirtualMachine API call. -
    -
    - Set VMware Cluster Size Limit Depending on VMware Version - The maximum number of hosts in a vSphere cluster is determined by the VMware - hypervisor software. For VMware versions 4.2, 4.1, 5.0, and 5.1, the limit is 32 - hosts. - For &PRODUCT; 4.2, the global configuration setting vmware.percluster.host.max has - been removed. The maximum number of hosts in a VMware cluster is now determined by the - underlying hypervisor software. - - Best Practice: It is advisable for VMware clusters in &PRODUCT; to be smaller than - the VMware hypervisor's maximum size. A cluster size of up to 8 hosts has been found - optimal for most real-world situations. - -
    -
    - Limiting Resource Usage - Previously in &PRODUCT;, resource usage limit was imposed based on the resource - count, that is, restrict a user or domain on the basis of the number of VMs, volumes, or - snapshots used. In &PRODUCT; 4.2, a new set of resource types has been added to the - existing pool of resources (VMs, Volumes, and Snapshots) to support the customization - model—need-basis usage, such as large VM or small VM. The new resource types are - now broadly classified as CPU, RAM, Primary storage, and Secondary storage. &PRODUCT; - 4.2 allows the root administrator to impose resource usage limit by the following - resource types for Domain, Project and Accounts. - - - CPUs - - - Memory (RAM) - - - Primary Storage (Volumes) - - - Secondary Storage (Snapshots, Templates, ISOs) - - -
    +
    + Assigning VLANs to Isolated Networks + &PRODUCT; provides you the ability to control VLAN assignment to Isolated networks. + You can assign a VLAN ID when a network is created, just the way it's done for Shared + networks. + The former behaviour also is supported — VLAN is randomly allocated to a network + from the VNET range of the physical network when the network turns to Implemented state. + The VLAN is released back to the VNET pool when the network shuts down as a part of the + Network Garbage Collection. The VLAN can be re-used either by the same network when it is + implemented again, or by any other network. On each subsequent implementation of a + network, a new VLAN can be assigned. + + You cannot change a VLAN once it's assigned to the network. The VLAN remains with + the network for its entire life cycle. +
    -
    - Monitoring, Maintenance, and Operations Enhancements - -
    - Publish and Subscribe for Event Notification - An event is essentially a significant or meaningful change in the state of both - virtual and physical resources associated with a cloud environment. In &PRODUCT; an - event could be a state change of virtual or psychical resources, an action performed by - an user (action events), or policy based events (alerts). In &PRODUCT; 4.2, a new event - notification framework has been added. This framework provides a means for the - Management Server components to publish and subscribe to &PRODUCT; events. Event - notification is achieved by implementing the concept of event bus abstraction in the - Management Server. - A new event for state change, resource state change, is introduced as part of Event - notification framework. Every resource, such as user VM, volume, NIC, network, public - IP, snapshot, and template, is associated with a state machine and generates events as - part of the state change. That implies that a change in the state of a resource results - in a state change event, and the event is published in the corresponding state machine - on the event bus. All the &PRODUCT; events (alerts, action events, usage events) and the - additional category of resource state change events, are published on to the events - bus. -
    -
    - Deleting and Archiving Events and Alerts - In addition to viewing a list of events and alerts in the UI, the administrator can - now delete and archive them. In order to support deleting and archiving alerts, the - following global parameters have been added: - - - alert.purge.delay: The alerts older than - specified number of days are purged. Set the value to 0 to never purge alerts - automatically. - - - alert.purge.interval: The interval in seconds - to wait before running the alert purge thread. The default is 86400 seconds (one - day). - - - - Archived alerts or events cannot be viewed in the UI, or by using the API. They - are maintained in the database for auditing or compliance purposes. - -
    -
    - Increased Granularity for Configuration Parameters - Some configuration parameters which were previously available only at the global - level of the cloud can now be set for smaller components of the cloud, such as at the - zone level. To set these parameters, look for the new Settings tab in the UI. You will - find it on the detail page for an account, cluster, zone, or primary storage. - The account level parameters are: remote.access.vpn.client.iprange, - allow.public.user.templates, use.system.public.ips, and - use.system.guest.vlans - The cluster level parameters are - cluster.storage.allocated.capacity.notificationthreshold, - cluster.storage.capacity.notificationthreshold, - cluster.cpu.allocated.capacity.notificationthreshold, - cluster.memory.allocated.capacity.notificationthreshold, - cluster.cpu.allocated.capacity.disablethreshold, - cluster.memory.allocated.capacity.disablethreshold, - cpu.overprovisioning.factor, mem.overprovisioning.factor, - vmware.reserve.cpu, and vmware.reserve.mem. - The zone level parameters are - pool.storage.allocated.capacity.disablethreshold, - pool.storage.capacity.disablethreshold, - storage.overprovisioning.factor, network.throttling.rate, - guest.domain.suffix, router.template.xen, - router.template.kvm, router.template.vmware, - router.template.hyperv, router.template.lxc, - enable.dynamic.scale.vm, use.external.dns, and - blacklisted.routes. -
    -
    - API Request Throttling - In &PRODUCT; 4.2, you can limit the rate at which API requests can be placed for - each account. This is useful to avoid malicious attacks on the Management Server, - prevent performance degradation, and provide fairness to all accounts. - If the number of API calls exceeds the threshold, an error message is returned for - any additional API calls. The caller will have to retry these API calls at another - time. - To control the API request throttling, use the following new global configuration - settings: - - - api.throttling.enabled - Enable/Disable API throttling. By default, this setting - is false, so API throttling is not enabled. - - - api.throttling.interval (in seconds) - Time interval during which the number of - API requests is to be counted. When the interval has passed, the API count is reset - to 0. - - - api.throttling.max - Maximum number of APIs that can be placed within the - api.throttling.interval period. - - - api.throttling.cachesize - Cache size for storing API counters. Use a value - higher than the total number of accounts managed by the cloud. One cache entry is - needed for each account, to store the running API total for that account within the - current time window. - - -
    -
    - Sending Alerts to External SNMP and Syslog Managers - In addition to showing administrator alerts on the Dashboard in the &PRODUCT; UI and - sending them in email, &PRODUCT; now can also send the same alerts to external SNMP or - Syslog management software. This is useful if you prefer to use an SNMP or Syslog - manager to monitor your cloud. - The supported protocol is SNMP version 2. -
    -
    - Changing the Default Password Encryption - Passwords are encoded when creating or updating users. The new default preferred - encoder, replacing MD5, is SHA256. It is more secure than MD5 hashing. If you take no - action to customize password encryption and authentication, SHA256 Salt will be - used. - If you prefer a different authentication mechanism, &PRODUCT; 4.2 provides a way for - you to determine the default encoding and authentication mechanism for admin and user - logins. Two new configurable lists have been introduced: userPasswordEncoders and - userAuthenticators. userPasswordEncoders allow you to configure the order of preference - for encoding passwords, and userAuthenticator allows you to configure the order in which - authentication schemes are invoked to validate user passwords. - The plain text user authenticator has been modified not to convert supplied - passwords to their md5 sums before checking them with the database entries. It performs - a simple string comparison between retrieved and supplied login passwords instead of - comparing the retrieved md5 hash of the stored password against the supplied md5 hash of - the password, because clients no longer hash the password. -
    -
    - Log Collection Utility cloud-bugtool - &PRODUCT; provides a command-line utility called cloud-bugtool to make it easier to - collect the logs and other diagnostic data required for troubleshooting. This is - especially useful when interacting with Citrix Technical Support. - You can use cloud-bugtool to collect the following: - - - Basic system and environment information and network configuration including IP - addresses, routing, and name resolver settings - - - Information about running processes - - - Management Server logs - - - System logs in /var/log/ - - - Dump of the cloud database - - - - cloud-bugtool collects information which might be considered sensitive and - confidential. Using the --nodb option to avoid the cloud database can - reduce this concern, though it is not guaranteed to exclude all sensitive data. - - -
    -
    - Snaphotting, Backups, Cloning and System VMs for RBD Primary Storage - - These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt - 0.9.14 on the KVM hypervisors. - - This release of &PRODUCT; will leverage the features of RBD format 2. This allows - snapshotting and backing up those snapshots. - Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they - are not RBD diffs. This because when restoring a backup of a snapshot it is not - mandatory that this backup is deployed on RBD again, it could also be a NFS Primary - Storage. - Another key feature of RBD format 2 is cloning. With this release templates will be - copied to Primary Storage once and by using the cloning mechanism new disks will be - cloned from this parent template. This saves space and decreases deployment time for - instances dramatically. - Before this release, a NFS Primary Storage was still required for running the System - VMs from. The reason was a so called 'patch disk' that was generated by the hypervisor - which contained metadata for the System VM. The scripts generating this disk didn't - support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of the - patch disk a VirtIO serial console is used to pass meta information to System VMs. This - enabled the deployment of System VMs on RBD Primary Storage. -
    +
    + Persistent Networks + &PRODUCT; 4.2 supports Persistent Networks. The network that you can provision without + having to deploy any VMs on it is called a Persistent Network. A Persistent Network can be + part of a VPC or a non-VPC environment. With the addition of this feature, you will have + the ability to create a network in &PRODUCT; in which physical devices can be deployed + without having to run any VMs. Additionally, you can deploy physical devices on that + network. Another advantages is that you can create a VPC with a tier that consists only + physical devices. For example, you might create a VPC for a three-tier application, deploy + VMs for Web and Application tier, and use physical machines for the Database tier. Another + use case is that if you are providing services by using physical hardware, you can define + the network as persistent and therefore even if all its VMs are destroyed the services + will not be discontinued. +
    +
    + Cisco VNMC Support + Cisco Virtual Network Management Center (VNMC) provides centralized multi-device and + policy management for Cisco Network Virtual Services. When Cisco VNMC is integrated with + ASA 1000v Cloud Firewall and Cisco Nexus 1000v dvSwitch in &PRODUCT; you will be able to: + + + Configure Cisco ASA 1000v Firewalls + + + Create and apply security profiles that contain ACL policy sets for both ingress + and egress traffic, and NAT policy sets + + + &PRODUCT; supports Cisco VNMC on Cisco Nexus 1000v dvSwich-enabled VMware + hypervisors. +
    +
    + VMware vNetwork Distributed vSwitch + &PRODUCT; supports VMware vSphere Distributed Switch (VDS) for virtual network + configuration in a VMware vSphere environment. Each vCenter server instance can support up + to 128 VDSs and each VDS can manage up to 500 VMware hosts. &PRODUCT; supports configuring + virtual networks in a deployment with a mix of Virtual Distributed Switch, Standard + Virtual Switch and Nexus 1000v Virtual Switch. +
    +
    + IP Reservation in Isolated Guest Networks + In Isolated guest networks in &PRODUCT; 4.2, a part of the guest IP address space can + be reserved for non-&PRODUCT; VMs or physical servers. To do so, you configure a range of + Reserved IP addresses by specifying the CIDR when a guest network is in Implemented state. + The advantage of having this feature is that if your customers wish to have non-&PRODUCT; + controlled VMs or physical servers on the same network, they can use a part of the IP + address space that is primarily provided to the guest network. When IP reservation is + configured, the administrator can add additional VMs or physical servers that are not part + of &PRODUCT; to the same network and assign them the Reserved IP addresses. &PRODUCT; + guest VMs cannot acquire IPs from the Reserved IP Range. +
    +
    + Dedicated Resources: Public IP Addresses and VLANs Per Account + &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs + exclusively for an account. During zone creation, you can continue to define a set of + VLANs and multiple public IP ranges. This feature extends the functionality to enable you + to dedicate a fixed set of VLANs and guest IP addresses for a tenant. + This feature provides you the following capabilities: + + + Reserve a VLAN range and public IP address range from an Advanced zone and assign + it to an account + + + Disassociate a VLAN and public IP address range from an account + + + + Ensure that you check whether the required range is available and conforms to + account limits. The maximum IPs per account limit cannot be superseded. + +
    +
    + Enhanced Juniper SRX Support for Egress Firewall Rules + Egress firewall rules were previously supported on virtual routers, and now they are + also supported on Juniper SRX external networking devices. + Egress traffic originates from a private network to a public network, such as the + Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed + from a guest network to the Internet. However, you can control the egress traffic in an + Advanced zone by creating egress firewall rules. When an egress firewall rule is applied, + the traffic specific to the rule is allowed and the remaining traffic is blocked. When all + the firewall rules are removed the default policy, Block, is applied. + + Egress firewall rules are not supported on Shared networks. They are supported only + on Isolated guest networks. + +
    +
    + Configuring the Default Egress Policy + The default egress policy for Isolated guest network can be configured by using + Network offering. Use the create network offering option to determine whether the default + policy should be block or allow all the traffic to the public network from a guest + network. Use this network offering to create the network. If no policy is specified, by + default all the traffic is allowed from the guest network that you create by using this + network offering. + You have two options: Allow and Deny. + If you select Allow for a network offering, by default egress traffic is allowed. + However, when an egress rule is configured for a guest network, rules are applied to block + the specified traffic and rest are allowed. If no egress rules are configured for the + network, egress traffic is accepted. If you select Deny for a network offering, by default + egress traffic for the guest network is blocked. However, when an egress rules is + configured for a guest network, rules are applied to allow the specified traffic. While + implementing a guest network, &PRODUCT; adds the firewall egress rule specific to the + default egress policy for the guest network. + This feature is supported only on virtual router and Juniper SRX. +
    +
    + Non-Contiguous VLAN Ranges + &PRODUCT; provides you with the flexibility to add non contiguous VLAN ranges to your + network. The administrator can either update an existing VLAN range or add multiple non + contiguous VLAN ranges while creating a zone. You can also use the UpdatephysicalNetwork + API to extend the VLAN range. +
    +
    + Isolation in Advanced Zone Using Private VLAN + Isolation of guest traffic in shared networks can be achieved by using Private VLANs + (PVLAN). PVLANs provide Layer 2 isolation between ports within the same VLAN. In a + PVLAN-enabled shared network, a user VM cannot reach other user VM though they can reach + the DHCP server and gateway, this would in turn allow users to control traffic within a + network and help them deploy multiple applications without communication between + application as well as prevent communication with other users’ VMs. + + + Isolate VMs in a shared networks by using Private VLANs. + + + Supported on KVM, XenServer, and VMware hypervisors. + + + PVLAN-enabled shared network can be a part of multiple networks of a guest VM. + + + + For further reading: + + + Understanding Private VLANs + + + Cisco Systems' Private VLANs: + Scalable Security in a Multi-Client Environment + + + Private VLAN (PVLAN) on vNetwork Distributed + Switch - Concept Overview (1010691) + + +
    +
    + Configuring Multiple IP Addresses on a Single NIC + (Supported on XenServer, KVM, and VMware hypervisors) + &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 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. +
    +
    + Adding Multiple IP Ranges + (Supported on KVM, xenServer, and VMware hypervisors) + &PRODUCT; 4.2 provides you with the flexibility to add guest IP ranges from different + subnets in Basic zones and security groups-enabled Advanced zones. For security + groups-enabled Advanced zones, it implies multiple subnets can be added to the same VLAN. + With the addition of this feature, you will be able to add IP address ranges from the same + subnet or from a different one when IP address are exhausted. This would in turn allows + you to employ higher number of subnets and thus reduce the address management + overhead. + Ensure that you manually configure the gateway of the new subnet before adding the IP + range. Note that &PRODUCT; supports only one gateway for a subnet; overlapping subnets are + not currently supported. + You can also delete IP ranges. This operation fails if an IP from the remove range is + in use. If the remove range contains the IP address on which the DHCP server is running, + &PRODUCT; acquires a new IP from the same subnet. If no IP is available in the subnet, the + remove operation fails. + + The feature can only be implemented on IPv4 addresses. + +
    +
    + Support for Multiple Networks in VMs + (Supported on XenServer, VMware and KVM hypervisors) + &PRODUCT; 4.2 provides you the ability to add and remove multiple networks to a VM. + You can remove a network from a VM and add a new network. You can also change the default + network of a VM. With this functionality, hybrid or traditional server loads can be + accommodated with ease. + For adding or removing a NIC to work on VMware, ensure that vm-tools are running on + guest VMs. +
    +
    + Global Server Load Balancing + &PRODUCT; 4.2 supports Global Server Load Balancing (GSLB) functionalities to provide + business continuity by load balancing traffic to an instance on active zones only in case + of zone failures . &PRODUCT; achieve this by extending its functionality of integrating + with NetScaler Application Delivery Controller (ADC), which also provides various GSLB + capabilities, such as disaster recovery and load balancing. The DNS redirection technique + is used to achieve GSLB in &PRODUCT;. In order to support this functionality, region level + services and service provider are introduced. A new service 'GSLB' is introduced as a + region level service. The GSLB service provider is introduced that will provider the GSLB + service. Currently, NetScaler is the supported GSLB provider in &PRODUCT;. GSLB + functionality works in an Active-Active data center environment. +
    +
    + Enhanced Load Balancing Services Using External Provider on Shared VLANs + Network services like Firewall, Load Balancing, and NAT are now supported in shared + networks created in an advanced zone. In effect, the following network services shall be + made available to a VM in a shared network: Source NAT, Static NAT, Port Forwarding, + Firewall and Load balancing. Subset of these service can be chosen while creating a + network offering for shared networks. Services available in a shared network is defined by + the network offering and the service chosen in the network offering. For example, if + network offering for a shared network has source NAT service enabled, a public IP shall be + provisioned and source NAT is configured on the firewall device to provide public access + to the VMs on the shared network. Static NAT, Port Forwarding, Load Balancing, and + Firewall services shall be available only on the acquired public IPs associated with a + shared network. + Additionally, Netscaler and Juniper SRX firewall device can be configured inline or + side-by-side mode. +
    +
    + Health Checks for Load Balanced Instances + + This feature is supported only on NetScaler version 10.0 and beyond. + + (NetScaler load balancer only) A load balancer rule distributes requests among a pool + of services (a service in this context means an application running on a virtual machine). + When creating a load balancer rule, you can specify a health check which will ensure that + the rule forwards requests only to services that are healthy (running and available). When + a health check is in effect, the load balancer will stop forwarding requests to any + resources that it has found to be unhealthy. If the resource later becomes available + again, the periodic health check (periodicity is configurable) will discover it and the + resource will once again be made available to the load balancer. + To configure how often the health check is performed by default, use the global + configuration setting healthcheck.update.interval. This default applies to all the health + check policies in the cloud. You can override this value for an individual health check + policy. +
    +
    +
    + Host and Virtual Machine Enhancements + The following new features expand the ways you can use hosts and virtual + machines. +
    + VMware DRS Support + The VMware vSphere Distributed Resources Scheduler (DRS) is supported. +
    +
    + Windows 8 and Windows Server 2012 as VM Guest OS + (Supported on XenServer, VMware, and KVM) + Windows 8 and Windows Server 2012 can now be used as OS types on guest virtual + machines. The OS would be made available the same as any other, by uploading an ISO or a + template. The instructions for uploading ISOs and templates are given in the + Administrator's Guide. + + Limitation: When used with VMware hosts, this + feature works only for the following versions: vSphere ESXi 5.1 and ESXi 5.0 Patch + 4. + + +
    +
    + Change Account Ownership of Virtual Machines + A root administrator can now change the ownership of any virtual machine from one + account to any other account. A domain or sub-domain administrator can do the same for VMs + within the domain from one account to any other account in the domain. +
    +
    + Private Pod, Cluster, or Host + Dedicating pod, cluster or host to a specific domain/account means that the + domain/account will have sole access to the dedicated pod, cluster or hosts such that + scalability, security and manageability within a domain/account can be improved. The + resources which belong to that tenant will be placed into that dedicated pod, cluster or + host. +
    +
    + Resizing Volumes + &PRODUCT; provides the ability to resize data disks; &PRODUCT; controls volume size by + using disk offerings. This provides &PRODUCT; administrators with the flexibility to + choose how much space they want to make available to the end users. Volumes within the + disk offerings with the same storage tag can be resized. For example, if you only want to + offer 10, 50, and 100 GB offerings, the allowed resize should stay within those limits. + That implies if you define a 10 GB, a 50 GB and a 100 GB disk offerings, a user can + upgrade from 10 GB to 50 GB, or 50 GB to 100 GB. If you create a custom-sized disk + offering, then you have the option to resize the volume by specifying a new, larger size. + Additionally, using the resizeVolume API, a data volume can be moved from a static disk + offering to a custom disk offering with the size specified. This functionality allows + those who might be billing by certain volume sizes or disk offerings to stick to that + model, while providing the flexibility to migrate to whatever custom size necessary. This + feature is supported on KVM, XenServer, and VMware hosts. However, shrinking volumes is + not supported on VMware hosts +
    +
    + VMware Volume Snapshot Improved Performance + When you take a snapshot of a data volume on VMware, &PRODUCT; will now use a more + efficient storage technique to improve performance. + Previously, every snapshot was immediately exported from vCenter to a mounted NFS + share and packaged into an OVA file format. This operation consumed time and resources. + Starting from 4.2, the original file formats (e.g., VMDK) provided by vCenter will be + retained. An OVA file will only be created as needed, on demand. + The new process applies only to newly created snapshots after upgrade to &PRODUCT; + 4.2. Snapshots that have already been taken and stored in OVA format will continue to + exist in that format, and will continue to work as expected. +
    +
    + Storage Migration: XenMotion and vMotion + (Supported on XenServer and VMware) + Storage migration allows VMs to be moved from one host to another, where the VMs are + not located on storage shared between the two hosts. It provides the option to live + migrate a VM’s disks along with the VM itself. It is now possible to migrate a VM from one + XenServer resource pool / VMware cluster to another, or to migrate a VM whose disks are on + local storage, or even to migrate a VM’s disks from one storage repository to another, all + while the VM is running. +
    +
    + Configuring Usage of Linked Clones on VMware + (For ESX hypervisor in conjunction with vCenter) + In &PRODUCT; 4.2, the creation of VMs as full clones is allowed. In previous versions, + only linked clones were possible. + For a full description of clone types, refer to VMware documentation. In summary: A + full clone is a copy of an existing virtual machine which, once created, does not depend + in any way on the original virtual machine. A linked clone is also a copy of an existing + virtual machine, but it has ongoing dependency on the original. A linked clone shares the + virtual disk of the original VM, and retains access to all files that were present at the + time the clone was created. + A new global configuration setting has been added, vmware.create.full.clone. When the + administrator sets this to true, end users can create guest VMs only as full clones. The + default value is true for new installations. For customers upgrading from a previous + version of &PRODUCT;, the default value of vmware.create.full.clone is false. +
    +
    + VM Deployment Rules + Rules can be set up to ensure that particular VMs are not placed on the same physical + host. These "anti-affinity rules" can increase the reliability of applications by ensuring + that the failure of a single host can not take down the entire group of VMs supporting a + given application. See Affinity Groups in the &PRODUCT; 4.2 Administration Guide. +
    +
    + CPU and Memory Scaling for Running VMs + (Supported on VMware and XenServer) + You can now change the CPU and RAM values for a running virtual machine. In previous + versions of &PRODUCT;, this could only be done on a stopped VM. + It is not always possible to accurately predict the CPU and RAM requirements when you + first deploy a VM. You might need to increase or decrease these resources at any time + during the life of a VM. With the new ability to dynamically modify CPU and RAM levels, + you can change these resources for a running VM without incurring any downtime. + Dynamic CPU and RAM scaling can be used in the following cases: + + + New VMs that are created after the installation of &PRODUCT; 4.2. If you are + upgrading from a previous version of &PRODUCT;, your existing VMs created with + previous versions will not have the dynamic scaling capability. + + + User VMs on hosts running VMware and XenServer. + + + System VMs on VMware. + + + VM Tools or XenServer Tools must be installed on the virtual machine. + + + The new requested CPU and RAM values must be within the constraints allowed by the + hypervisor and the VM operating system. + + + To configure this feature, use the following new global configuration + variables: + + + enable.dynamic.scale.vm: Set to True to enable the feature. By default, the + feature is turned off. + + + scale.retry: How many times to attempt the scaling operation. Default = 2. + + +
    +
    + CPU and Memory Over-Provisioning + (Supported for XenServer, KVM, and VMware) + In &PRODUCT; 4.2, CPU and memory (RAM) over-provisioning factors can be set for each + cluster to change the number of VMs that can run on each host in the cluster. This helps + optimize the use of resources. By increasing the over-provisioning ratio, more resource + capacity will be used. If the ratio is set to 1, no over-provisioning is done. + In previous releases, &PRODUCT; did not perform memory over-provisioning. It performed + CPU over-provisioning based on a ratio configured by the administrator in the global + configuration setting cpu.overprovisioning.factor. Starting in 4.2, the administrator can + specify a memory over-provisioning ratio, and can specify both CPU and memory + over-provisioning ratios on a per-cluster basis, rather than only on a global + basis. + In any given cloud, the optimum number of VMs for each host is affected by such things + as the hypervisor, storage, and hardware configuration. These may be different for each + cluster in the same cloud. A single global over-provisioning setting could not provide the + best utilization for all the different clusters in the cloud. It had to be set for the + lowest common denominator. The new per-cluster setting provides a finer granularity for + better utilization of resources, no matter where the &PRODUCT; placement algorithm decides + to place a VM. +
    +
    + Kickstart Installation for Bare Metal Provisioning + &PRODUCT; 4.2 supports the kick start installation method for RPM-based Linux + operating systems on baremetal hosts in basic zones. Users can provision a baremetal host + managed by &PRODUCT; as long as they have the kick start file and corresponding OS + installation ISO ready. + Tested on CentOS 5.5, CentOS 6.2, CentOS 6.3, Ubuntu 12.04. + For more information, see the Baremetal Installation Guide. +
    +
    + Enhanced Bare Metal Support on Cisco UCS + You can now more easily provision new Cisco UCS server blades into &PRODUCT; for use + as bare metal hosts. The goal is to enable easy expansion of the cloud by leveraging the + programmability of the UCS converged infrastructure and &PRODUCT;’s knowledge of the cloud + architecture and ability to orchestrate. With this new feature, &PRODUCT; can + automatically understand the UCS environment, server profiles, etc. to make it easy to + deploy a bare metal OS on a Cisco UCS. +
    +
    + Changing a VM's Base Image + Every VM is created from a base image, which is a template or ISO which has been + created and stored in &PRODUCT;. Both cloud administrators and end users can create and + modify templates, ISOs, and VMs. + In &PRODUCT; 4.2, there is a new way to modify an existing VM. You can change an + existing VM from one base image to another. For example, suppose there is a template based + on a particular operating system, and the OS vendor releases a software patch. The + administrator or user naturally wants to apply the patch and then make sure existing VMs + start using it. Whether a software update is involved or not, it's also possible to simply + switch a VM from its current template to any other desired template. +
    +
    + Reset VM on Reboot + In &PRODUCT; 4.2, you can specify that you want to discard the root disk and create a + new one whenever a given VM is rebooted. This is useful for secure environments that need + a fresh start on every boot and for desktops that should not retain state. The IP address + of the VM will not change due to this operation. +
    +
    + Virtual Machine Snapshots for VMware + (VMware hosts only) In addition to the existing &PRODUCT; ability to snapshot + individual VM volumes, you can now take a VM snapshot to preserve all the VM's data + volumes as well as (optionally) its CPU/memory state. This is useful for quick restore of + a VM. For example, you can snapshot a VM, then make changes such as software upgrades. If + anything goes wrong, simply restore the VM to its previous state using the previously + saved VM snapshot. + The snapshot is created using the VMware native snapshot facility. The VM snapshot + includes not only the data volumes, but optionally also whether the VM is running or + turned off (CPU state) and the memory contents. The snapshot is stored in &PRODUCT;'s + primary storage. + VM snapshots can have a parent/child relationship. Each successive snapshot of the + same VM is the child of the snapshot that came before it. Each time you take an additional + snapshot of the same VM, it saves only the differences between the current state of the VM + and the state stored in the most recent previous snapshot. The previous snapshot becomes a + parent, and the new snapshot is its child. It is possible to create a long chain of these + parent/child snapshots, which amount to a "redo" record leading from the current state of + the VM back to the original. +
    +
    + Increased Userdata Size When Deploying a VM + You can now specify up to 32KB of userdata when deploying a virtual machine through + the &PRODUCT; UI or the deployVirtualMachine API call. +
    +
    + Set VMware Cluster Size Limit Depending on VMware Version + The maximum number of hosts in a vSphere cluster is determined by the VMware + hypervisor software. For VMware versions 4.2, 4.1, 5.0, and 5.1, the limit is 32 + hosts. + For &PRODUCT; 4.2, the global configuration setting vmware.percluster.host.max has + been removed. The maximum number of hosts in a VMware cluster is now determined by the + underlying hypervisor software. + + Best Practice: It is advisable for VMware clusters in &PRODUCT; to be smaller than + the VMware hypervisor's maximum size. A cluster size of up to 8 hosts has been found + optimal for most real-world situations. + +
    +
    + Limiting Resource Usage + Previously in &PRODUCT;, resource usage limit was imposed based on the resource count, + that is, restrict a user or domain on the basis of the number of VMs, volumes, or + snapshots used. In &PRODUCT; 4.2, a new set of resource types has been added to the + existing pool of resources (VMs, Volumes, and Snapshots) to support the customization + model—need-basis usage, such as large VM or small VM. The new resource types are now + broadly classified as CPU, RAM, Primary storage, and Secondary storage. &PRODUCT; 4.2 + allows the root administrator to impose resource usage limit by the following resource + types for Domain, Project and Accounts. + + + CPUs + + + Memory (RAM) + + + Primary Storage (Volumes) + + + Secondary Storage (Snapshots, Templates, ISOs) + + +
    +
    +
    + Monitoring, Maintenance, and Operations Enhancements +
    + Deleting and Archiving Events and Alerts + In addition to viewing a list of events and alerts in the UI, the administrator can + now delete and archive them. In order to support deleting and archiving alerts, the + following global parameters have been added: + + + alert.purge.delay: The alerts older than + specified number of days are purged. Set the value to 0 to never purge alerts + automatically. + + + alert.purge.interval: The interval in seconds to + wait before running the alert purge thread. The default is 86400 seconds (one + day). + + + + Archived alerts or events cannot be viewed in the UI, or by using the API. They are + maintained in the database for auditing or compliance purposes. + +
    +
    + Increased Granularity for Configuration Parameters + Some configuration parameters which were previously available only at the global level + of the cloud can now be set for smaller components of the cloud, such as at the zone + level. To set these parameters, look for the new Settings tab in the UI. You will find it + on the detail page for an account, cluster, zone, or primary storage. + The account level parameters are: remote.access.vpn.client.iprange, + allow.public.user.templates, use.system.public.ips, and + use.system.guest.vlans + The cluster level parameters are + cluster.storage.allocated.capacity.notificationthreshold, + cluster.storage.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.notificationthreshold, + cluster.memory.allocated.capacity.notificationthreshold, + cluster.cpu.allocated.capacity.disablethreshold, + cluster.memory.allocated.capacity.disablethreshold, + cpu.overprovisioning.factor, mem.overprovisioning.factor, + vmware.reserve.cpu, and vmware.reserve.mem. + The zone level parameters are + pool.storage.allocated.capacity.disablethreshold, + pool.storage.capacity.disablethreshold, + storage.overprovisioning.factor, network.throttling.rate, + guest.domain.suffix, router.template.xen, + router.template.kvm, router.template.vmware, + router.template.hyperv, router.template.lxc, + enable.dynamic.scale.vm, use.external.dns, and + blacklisted.routes. +
    +
    + API Request Throttling + In &PRODUCT; 4.2, you can limit the rate at which API requests can be placed for each + account. This is useful to avoid malicious attacks on the Management Server, prevent + performance degradation, and provide fairness to all accounts. + If the number of API calls exceeds the threshold, an error message is returned for any + additional API calls. The caller will have to retry these API calls at another + time. + To control the API request throttling, use the following new global configuration + settings: + + + api.throttling.enabled - Enable/Disable API throttling. By default, this setting + is false, so API throttling is not enabled. + + + api.throttling.interval (in seconds) - Time interval during which the number of + API requests is to be counted. When the interval has passed, the API count is reset to + 0. + + + api.throttling.max - Maximum number of APIs that can be placed within the + api.throttling.interval period. + + + api.throttling.cachesize - Cache size for storing API counters. Use a value higher + than the total number of accounts managed by the cloud. One cache entry is needed for + each account, to store the running API total for that account within the current time + window. + + +
    +
    + Sending Alerts to External SNMP and Syslog Managers + In addition to showing administrator alerts on the Dashboard in the &PRODUCT; UI and + sending them in email, &PRODUCT; now can also send the same alerts to external SNMP or + Syslog management software. This is useful if you prefer to use an SNMP or Syslog manager + to monitor your cloud. + The supported protocol is SNMP version 2. +
    +
    + Changing the Default Password Encryption + Passwords are encoded when creating or updating users. The new default preferred + encoder, replacing MD5, is SHA256. It is more secure than MD5 hashing. If you take no + action to customize password encryption and authentication, SHA256 Salt will be + used. + If you prefer a different authentication mechanism, &PRODUCT; 4.2 provides a way for + you to determine the default encoding and authentication mechanism for admin and user + logins. Two new configurable lists have been introduced: userPasswordEncoders and + userAuthenticators. userPasswordEncoders allow you to configure the order of preference + for encoding passwords, and userAuthenticator allows you to configure the order in which + authentication schemes are invoked to validate user passwords. + The plain text user authenticator has been modified not to convert supplied passwords + to their md5 sums before checking them with the database entries. It performs a simple + string comparison between retrieved and supplied login passwords instead of comparing the + retrieved md5 hash of the stored password against the supplied md5 hash of the password, + because clients no longer hash the password. +
    +
    + Log Collection Utility cloud-bugtool + &PRODUCT; provides a command-line utility called cloud-bugtool to make it easier to + collect the logs and other diagnostic data required for troubleshooting. This is + especially useful when interacting with Citrix Technical Support. + You can use cloud-bugtool to collect the following: + + + Basic system and environment information and network configuration including IP + addresses, routing, and name resolver settings + + + Information about running processes + + + Management Server logs + + + System logs in /var/log/ + + + Dump of the cloud database + + + + cloud-bugtool collects information which might be considered sensitive and + confidential. Using the --nodb option to avoid the cloud database can + reduce this concern, though it is not guaranteed to exclude all sensitive data. + + +
    +
    + Snaphotting, Backups, Cloning and System VMs for RBD Primary Storage + + These new RBD features require at least librbd 0.61.7 (Cuttlefish) and libvirt + 0.9.14 on the KVM hypervisors. + + This release of &PRODUCT; will leverage the features of RBD format 2. This allows + snapshotting and backing up those snapshots. + Backups of snapshots to Secondary Storage are full copies of the RBD snapshot, they + are not RBD diffs. This because when restoring a backup of a snapshot it is not mandatory + that this backup is deployed on RBD again, it could also be a NFS Primary Storage. + Another key feature of RBD format 2 is cloning. With this release templates will be + copied to Primary Storage once and by using the cloning mechanism new disks will be cloned + from this parent template. This saves space and decreases deployment time for instances + dramatically. + Before this release, a NFS Primary Storage was still required for running the System + VMs from. The reason was a so called 'patch disk' that was generated by the hypervisor + which contained metadata for the System VM. The scripts generating this disk didn't + support RBD and thus System VMs had to be deployed from NFS. With 4.2 instead of the patch + disk a VirtIO serial console is used to pass meta information to System VMs. This enabled + the deployment of System VMs on RBD Primary Storage.
    @@ -1066,658 +1024,15 @@ under the License. >Jira to track its issues. All new features and bugs for 4.2.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.2.0. - Approximately 470 bugs were resolved or closed in the 4.2.0 cycle. - - - - - - - - Defect - - - Description - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + For list of issues fixed, see Issues Fixed in + 4.2.
    Known Issues in 4.2.0 - - - - - - - - Issue ID - - - Description - - - - - - CLOUDSTACK-3466 - - VM Migration across VMware clusters which are added with different switches - (Standard Switch,Vmware DVS, Cisco Nexus 1000v) is not supported. - - - - CLOUDSTACK-4207 - - The following exception is observed when the Management Server is started - after the upgrade from any older versions to &PRODUCT; 4.2. - jsonParseException: The JsonDeserializer - com.cloud.agent.transport.ArrayTypeAdaptor@2426e26f failed to deserialize json - object - Ignore this exception, this would stop after you upgrade the System VM. - However, if you want to prevent this, stop system VM from the hypervisor before - upgrade. - - - - CLOUDSTACK-2709 - - Egress rules are not supported on Shared networks. - - - - CLOUDSTACK-1747 - The mvn deploydb command creates only 4.0 database, not 4.2 - database. - Due to tooling changes between 4.0 and 4.2, &PRODUCT; database is created by - using the 4.0 schema and updated to the 4.2 schema when the management server - starts for the first time. Neglect the same schema if the management server has - not started yet. - - - - - CLOUDSTACK-1306 - - - Enhance the error message that is displayed when trying to deploy a VM by - passing the static IPv4 addresses that are assigned to another VM or an IPv4 - address that is outside the IP range. - - - - - CLOUDSTACK-1236 - - - Warning while adding a XenSever 6.1 host. The warning displayed is 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-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 - - - - CLOUDSTACK-4645 - There is no upgrade path from 4.1.1 to 4.2.0. - - - CLOUDSTACK-4641 - Create volume form snapshot command times out exactly after 1 hour in - case of KVM hosts. - - - CLOUDSTACK-4621 - Changing the management server's Ethernet interface or MAC address leaves - the system in unstable state. - - - CLOUDSTACK-4615 - (Baremetal) Baremetal agent is missing in the installer. - - - CLOUDSTACK-4598 - Long delays during deploying a VM; both network and deployment planner - are delayed. - - - CLOUDSTACK-4596 - The same IP range is allowed to be defined in different VLANs across - public and portable ranges. - - - CLOUDSTACK-4588 - (VMware) VM deployment failed while creating a volume with null pointer - exception. - - - CLOUDSTACK-4578 - (VMware) SSVM is not getting created if one host is down in a - cluster. - - - CLOUDSTACK-4551 - Migrating the data volume from NFS to local storage does not change the - underlying disk offering. - - - CLOUDSTACK-4550 - Migration does not work in the case of bridge naming while upgrading KVM - agents to version 4.2. - - - CLOUDSTACK-4549 - Deploying VMs from template fails if the template is created from a - snapshot. - - - CLOUDSTACK-4540 - (VMware) When deploying 30 parallel VMs , 16 VMs fails to get deployed - due to the following error: "VmDataCommand failed due to Exception: - java.lang.Exception Message: Timed out in waiting SSH execution - result". - - - CLOUDSTACK-4506 - In a mixed hypervisor setup, destroying a VM whose host has been removed, - throws a null pointer exception. The Root volume of that VM also is not deleted - from the primary memory. - - - CLOUDSTACK-4442 - Source NAT not applied when network starts up. - - - CLOUDSTACK-4405 - (KVM) Migration between existing hosts and new hosts - fails. - - - CLOUDSTACK-4402 - No options to delete the primary storage if the last host with which it - was associated is already removed. - - - CLOUDSTACK-4366 - (Ubuntu) Key translation fails for the Japanese keyboard for the Menu key - and Caps Lock buttons. - - - CLOUDSTACK-4351 - Host/Hypervisor System Requirements has misleading or premature note in - the documentation. - - - CLOUDSTACK-4348 - Regression truncation issues occurs when moving the cursor to the "plus" - buttons. - - - CLOUDSTACK-4300 - (KVM) System VMs are not coming up after 2.2.14 to 4.2 - upgrade. - - - CLOUDSTACK-4292 - The destroyedvm API failed with ArrayIndexexception while - expunging. - - - CLOUDSTACK-4247 - (VMWARE) Network read and write statistics always returns - zero. - - - CLOUDSTACK-4224 - Deleting UCS returns unknown API. - - - CLOUDSTACK-4220 - From 3.0.6 to 4.2 upgrade, Add VMWare DataCenter button is provided for - legacy zones. - - - CLOUDSTACK-4201 - The listServiceOfferings API does not take virtualmachineid parameter of - SystemVM to return the Service Offerings available for the VM to change a Service - Offering. - - - CLOUDSTACK-4200 - The listSystemVMs API and listRouters API fail to return hypervisor - property. - - - CLOUDSTACK-4148 - The usage statistics are not triggered for Shared network. - - - CLOUDSTACK-4139 - (VMWARE) Resizing the volumes fails if they are created from - snapshot. - - - CLOUDSTACK-4137 - (KVM): After removing a cluster, manage cluster will not bring KVM hosts - to UP state. Manually restart the cloud-agent on KVM hosts. - - - CLOUDSTACK-4128 - The System VMs start up does not check for existence of staging secondary - storage in a zone. - - - CLOUDSTACK-4099 - Update the systemvm templates in DevCloud2. - - - CLOUDSTACK-4095 - Region ID is displayed within the Database Transaction - code. - - - CLOUDSTACK-4072 - The mysql-connector-java rpm is required while upgrading from 2.2.14 to - 4.2. - - - CLOUDSTACK-4036 - The UI remains in processing state and the queryAsyncJobResult is being - called repeatedly for the scaleSystemVm API. - - - CLOUDSTACK-4016 - The listPublicIpAddresses lists the portable IP that was already - transferred to a different Isolated network. - - - CLOUDSTACK-3968 - Distributed Port groups are not deleted when guest networks are removed. - The user account of this network is removed from &PRODUCT; - - - CLOUDSTACK-3967 - No support for usage statistics collection at the portable IP - level - - - CLOUDSTACK-3953 - The usage statistics are not collected for GSLB rules. - - - CLOUDSTACK-3911 - No check available while adding public range in a zone to see whether the - same VLAN exists in a portable IP range. - - - CLOUDSTACK-3888 - The UI does not return the mode (Strict/Preferred) when listing the - ServiceOffering that uses ImplicitDedicationPlanner. - - - CLOUDSTACK-3808 - Attaching volumes does not work when root is at the zone-level primary - store and data at the cluster level or host level store. - - - CLOUDSTACK-3791 - Download template fails with a null pointer exception. - - - CLOUDSTACK-3788 - The weekly Snapshot is stuck in Allocated State. - - - CLOUDSTACK-3765 - Upgrading CloudPlatform 4.2 build on centos5.5 does not - work. - - - CLOUDSTACK-3737 - Uploaded volume is not getting deleted from secondary storage after - attaching it to a guest VM. - - - CLOUDSTACK-3658 - Several old object storage tables and columns are deprecated as a part of - 4.1 to 4.2 database upgrade. - - - CLOUDSTACK-3627 - Public IP interface (eth2) is not getting configured with Redundant - virtual router. The State is FAULT. - - - CLOUDSTACK-3608 - The guest_os_hypervisor table in the database has repeated mappings of - hypervisor and guest OS. - - - CLOUDSTACK-3583 - Stopping the Management server does not remove the PID. - - - CLOUDSTACK-3565 - Restarting libvirtd service leads to destroying the storage - pool. - - - CLOUDSTACK-3243 - Wrong NFS mount point is given in the documentation. - - - CLOUDSTACK-3138 - Flaws in the documentation for the upgrade from 3.0.2 to - 4.1.0. - - - CLOUDSTACK-2791 - Installation instruction is wrong. - - - CLOUDSTACK-1986 - Key translation fails for the following Japanese keyboard keys: ¥_,\ |, - Muhenkan, Henkan, Hiragana/Katakana, Kanji Key, and Caps Lock. - - - CLOUDSTACK-1775 - Events related to User/Domain/Account are not being generated expect for - the USER-DISABLE,DOMAIN-DELETE and ACCOUNT.DISABLE events. - - - CLOUDSTACK-732 - KVM snapshot is not supported. - - - - + This section includes a summary of known issues that were fixed in 4.2.0. For list of + known issues, see Known + Issues.
    From fe5468881a37a63f2218ea301acc684c53d0d722 Mon Sep 17 00:00:00 2001 From: frank Date: Fri, 13 Sep 2013 22:27:46 -0700 Subject: [PATCH 188/251] Add disassocating profile to UCS --- api/src/com/cloud/event/EventTypes.java | 1 + client/tomcatconf/commands.properties.in | 1 + plugins/hypervisors/ucs/pom.xml | 5 ++ .../com/cloud/ucs/manager/UcsCommands.java | 31 ++++++++-- .../com/cloud/ucs/manager/UcsHttpClient.java | 7 ++- .../src/com/cloud/ucs/manager/UcsManager.java | 2 + .../com/cloud/ucs/manager/UcsManagerImpl.java | 50 ++++++++++++--- .../com/cloud/ucs/structure/UcsProfile.java | 0 .../api/DisassociateUcsProfileCmd.java | 61 +++++++++++++++++++ 9 files changed, 144 insertions(+), 14 deletions(-) mode change 100644 => 100755 plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsCommands.java mode change 100644 => 100755 plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsHttpClient.java mode change 100644 => 100755 plugins/hypervisors/ucs/src/com/cloud/ucs/structure/UcsProfile.java create mode 100755 plugins/hypervisors/ucs/src/org/apache/cloudstack/api/DisassociateUcsProfileCmd.java diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index ff596749a63..dc2040031f8 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -443,6 +443,7 @@ public class EventTypes { public static final String EVENT_CLEANUP_VM_RESERVATION = "VM.RESERVATION.CLEANUP"; public static final String EVENT_UCS_ASSOCIATED_PROFILE = "UCS.ASSOCIATEPROFILE"; + public static final String EVENT_UCS_DISASSOCIATED_PROFILE = "UCS.DISASSOCIATEPROFILE"; static { diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 69a31207220..492661ef409 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -618,6 +618,7 @@ listUcsProfiles=1 listUcsBlades=1 associateUcsProfileToBlade=1 deleteUcsManager=1 +disassociateUcsProfileFromBlade=1 #### New Load Balancer commands createLoadBalancer=15 diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml index 24bdc948e73..fe89d26b4a7 100755 --- a/plugins/hypervisors/ucs/pom.xml +++ b/plugins/hypervisors/ucs/pom.xml @@ -52,5 +52,10 @@ cloud-api ${project.version} + + javax.inject + javax.inject + 1 + diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsCommands.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsCommands.java old mode 100644 new mode 100755 index c0753f463cd..52e5edfdb9c --- a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsCommands.java +++ b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsCommands.java @@ -67,6 +67,29 @@ public class UcsCommands { return cmd.toString(); } + public static String disassociateProfileFromBlade(String cookie, String profileDn) { + XmlObject cmd = new XmlObject("configConfMo"); + cmd.putElement("cookie", cookie); + cmd.putElement("inHierarchical", "false") + .putElement("inConfig", new XmlObject("inConfig") + .putElement("lsServer", new XmlObject("lsServer") + .putElement("dn", profileDn).putElement("lsBinding", new XmlObject("lsBinding").putElement("rn", "pn").putElement("status", "deleted")) + ) + ); + + return cmd.dump(); + } + + public static String deleteProfile(String cookie, String profileDn) { + XmlObject cmd = new XmlObject("configConfMos"); + cmd.putElement("cookie", cookie); + cmd.putElement("inHierarchical", "true") + .putElement("inConfigs", new XmlObject("inConfigs").putElement("pair", new XmlObject("pair").putElement("key", profileDn) + .putElement("lsServer", new XmlObject("lsServer").putElement("dn", profileDn).putElement("status", "deleted")) + )); + return cmd.dump(); + } + public static String associateProfileToBlade(String cookie, String profileDn, String bladeDn) { XmlObject cmd = new XmlObject("configConfMos").putElement("cookie", cookie).putElement("inHierarchical", "true").putElement( "inConfigs", new XmlObject("inConfigs").putElement( @@ -95,10 +118,10 @@ public class UcsCommands { .putElement("uuid", "") .putElement("vconProfileName", "") .putElement("lsBinding", new XmlObject("lsBinding") - .putElement("pnDn", bladeDn) - .putElement("restrictMigration", "no") - .putElement("rn", "pn") - ) + .putElement("pnDn", bladeDn) + .putElement("restrictMigration", "no") + .putElement("rn", "pn") + ) ) ) ); diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsHttpClient.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsHttpClient.java old mode 100644 new mode 100755 index 945d921a8d3..7758c4cd6e2 --- a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsHttpClient.java +++ b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsHttpClient.java @@ -19,6 +19,7 @@ package com.cloud.ucs.manager; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory; import org.apache.commons.httpclient.methods.PostMethod; @@ -28,10 +29,14 @@ import org.apache.commons.httpclient.protocol.Protocol; import com.cloud.utils.exception.CloudRuntimeException; public class UcsHttpClient { - private static HttpClient client = new HttpClient(); + private static HttpClient client; private static Protocol ucsHttpsProtocol = new org.apache.commons.httpclient.protocol.Protocol("https", new EasySSLProtocolSocketFactory(), 443); private final String url; + static { + client = new HttpClient(); + } + public UcsHttpClient(String ip) { url = String.format("http://%s/nuova", ip); Protocol.registerProtocol("https", ucsHttpsProtocol); diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManager.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManager.java index 0833e31f0f3..babec3aca8e 100755 --- a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManager.java +++ b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManager.java @@ -42,4 +42,6 @@ public interface UcsManager extends Manager, PluggableService { ListResponse listUcsBlades(ListUcsBladeCmd cmd); void deleteUcsManager(Long id); + + UcsBladeResponse disassociateProfile(Long bladeId); } diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java index 4aec48c1c85..05b1214d9c9 100755 --- a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java +++ b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java @@ -30,17 +30,12 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.api.AddUcsManagerCmd; -import org.apache.cloudstack.api.AssociateUcsProfileToBladeCmd; -import org.apache.cloudstack.api.ListUcsBladeCmd; -import org.apache.cloudstack.api.ListUcsManagerCmd; -import org.apache.cloudstack.api.ListUcsProfileCmd; +import org.apache.cloudstack.api.*; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UcsBladeResponse; import org.apache.cloudstack.api.response.UcsManagerResponse; import org.apache.cloudstack.api.response.UcsProfileResponse; -import org.apache.log4j.Logger; -import org.apache.cloudstack.api.DeleteUcsManagerCmd; +import org.apache.log4j.Logger; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; @@ -296,7 +291,14 @@ public class UcsManagerImpl implements UcsManager { UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); String res = client.call(cmd); List profiles = UcsProfile.fromXmlString(res); - return profiles; + List unassociated = new ArrayList(); + for (UcsProfile p : profiles) { + if (isProfileAssociated(mgrvo.getId(), p.getDn())) { + continue; + } + unassociated.add(p); + } + return unassociated; } @Override @@ -324,6 +326,7 @@ public class UcsManagerImpl implements UcsManager { return xo.get("outConfig.lsServer.dn"); } + private boolean isProfileAssociated(Long ucsMgrId, String dn) { UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); @@ -331,6 +334,17 @@ public class UcsManagerImpl implements UcsManager { String cmd = UcsCommands.configResolveDn(cookie, dn); String res = client.call(cmd); XmlObject xo = XmlObjectParser.parseFromString(res); + + return xo.get("outConfig.lsServer.assocState").equals("associated"); + } + + private boolean isBladeAssociated(Long ucsMgrId, String dn) { + UcsManagerVO mgrvo = ucsDao.findById(ucsMgrId); + UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); + String cookie = getCookie(ucsMgrId); + String cmd = UcsCommands.configResolveDn(cookie, dn); + String res = client.call(cmd); + XmlObject xo = XmlObjectParser.parseFromString(res); s_logger.debug(String.format("association response is %s", res)); if (xo.get("outConfig.computeBlade.association").equals("none")) { @@ -363,7 +377,7 @@ public class UcsManagerImpl implements UcsManager { int count = 0; int timeout = 600; while (count < timeout) { - if (isProfileAssociated(mgrvo.getId(), bvo.getDn())) { + if (isBladeAssociated(mgrvo.getId(), bvo.getDn())) { break; } @@ -504,6 +518,7 @@ public class UcsManagerImpl implements UcsManager { cmds.add(AddUcsManagerCmd.class); cmds.add(AssociateUcsProfileToBladeCmd.class); cmds.add(DeleteUcsManagerCmd.class); + cmds.add(DisassociateUcsProfileCmd.class); return cmds; } @@ -517,4 +532,21 @@ public class UcsManagerImpl implements UcsManager { } ucsDao.remove(id); } + + @Override + public UcsBladeResponse disassociateProfile(Long bladeId) { + UcsBladeVO blade = bladeDao.findById(bladeId); + UcsManagerVO mgrvo = ucsDao.findById(blade.getUcsManagerId()); + UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); + String cookie = getCookie(mgrvo.getId()); + String cmd = UcsCommands.disassociateProfileFromBlade(cookie, blade.getProfileDn()); + client.call(cmd); + cmd = UcsCommands.deleteProfile(cookie, blade.getProfileDn()); + client = new UcsHttpClient(mgrvo.getUrl()); + client.call(cmd); + blade.setProfileDn(null); + bladeDao.update(blade.getId(), blade); + UcsBladeResponse rsp = bladeVOToResponse(blade); + return rsp; + } } diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/structure/UcsProfile.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/structure/UcsProfile.java old mode 100644 new mode 100755 diff --git a/plugins/hypervisors/ucs/src/org/apache/cloudstack/api/DisassociateUcsProfileCmd.java b/plugins/hypervisors/ucs/src/org/apache/cloudstack/api/DisassociateUcsProfileCmd.java new file mode 100755 index 00000000000..ed2098954d5 --- /dev/null +++ b/plugins/hypervisors/ucs/src/org/apache/cloudstack/api/DisassociateUcsProfileCmd.java @@ -0,0 +1,61 @@ +package org.apache.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.exception.*; +import com.cloud.ucs.manager.UcsManager; +import com.cloud.user.Account; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UcsBladeResponse; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +/** + * Created with IntelliJ IDEA. + * User: frank + * Date: 9/13/13 + * Time: 6:23 PM + * To change this template use File | Settings | File Templates. + */ +@APICommand(name="disassociateUcsProfileFromBlade", description="disassociate a profile from a blade", responseObject=UcsBladeResponse.class) +public class DisassociateUcsProfileCmd extends BaseAsyncCmd { + private static Logger logger = Logger.getLogger(DisassociateUcsProfileCmd.class); + + @Inject + private UcsManager mgr; + + @Parameter(name=ApiConstants.UCS_BLADE_ID, type=CommandType.UUID, entityType=UcsBladeResponse.class, description="blade id", required=true) + private Long bladeId; + + @Override + public String getEventType() { + return EventTypes.EVENT_UCS_DISASSOCIATED_PROFILE; + } + + @Override + public String getEventDescription() { + return "disassociate a profile from blade"; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + UcsBladeResponse rsp = mgr.disassociateProfile(bladeId); + rsp.setResponseName(getCommandName()); + this.setResponseObject(rsp); + } catch(Exception e) { + logger.warn(e.getMessage(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return "disassociateucsprofilefrombladeresponse"; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} From 4ab2a741f6d9496e18c78dc7f8f376f051041707 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Sat, 14 Sep 2013 18:26:28 -0700 Subject: [PATCH 189/251] Fix unit test failure of VmwareDatacenterApiUnitTest. --- .../hypervisor/vmware/VmwareDatacenterApiUnitTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java b/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java index d79d41e024c..cd81e9cd19c 100644 --- a/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java +++ b/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java @@ -56,6 +56,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.cloud.agent.AgentManager; import com.cloud.cluster.ClusterManager; +import com.cloud.cluster.dao.ManagementServerHostPeerDao; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; @@ -377,6 +378,11 @@ public class VmwareDatacenterApiUnitTest { return Mockito.mock(LegacyZoneDao.class); } + @Bean + public ManagementServerHostPeerDao managementServerHostPeerDao() { + return Mockito.mock(ManagementServerHostPeerDao.class); + } + @Bean public ConfigurationDao configurationDao() { return Mockito.mock(ConfigurationDao.class); From 5e52a86a2ac8783141c99e8ab34a96fb21b8826a Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Sun, 15 Sep 2013 11:39:32 -0700 Subject: [PATCH 190/251] CLOUDSTACK-4659: Fix the regression caused by worker VM consolidation for GC purpose --- .../resource/VmwareStorageProcessor.java | 24 ++++++------------- .../vmware/mo/VirtualMachineMO.java | 9 +++---- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 5dad90aee46..05ffec96325 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -660,15 +660,10 @@ public class VmwareStorageProcessor implements StorageProcessor { } // 4 MB is the minimum requirement for VM memory in VMware - vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), + Pair cloneResult = vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); - clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName); - if(clonedVm == null) { - String msg = "Unable to create dummy VM to export volume. volume path: " + volumePath; - s_logger.error(msg); - throw new Exception(msg); - } - + clonedVm = cloneResult.first(); + clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, true, false); long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); @@ -984,17 +979,12 @@ public class VmwareStorageProcessor implements StorageProcessor { } // 4 MB is the minimum requirement for VM memory in VMware - String disks[] = vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), - VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); - clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName); - if(clonedVm == null) { - String msg = "Unable to create dummy VM to export volume. volume path: " + volumePath; - s_logger.error(msg); - throw new Exception(msg); - } + Pair cloneResult = vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), + VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); + clonedVm = cloneResult.first(); + String disks[] = cloneResult.second(); clonedVm.exportVm(exportPath, exportName, false, false); - return new Pair(volumeDeviceInfo.second(), disks); } finally { if(clonedVm != null) { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index a6df40e2300..f64c3c0d1e8 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1545,15 +1545,15 @@ public class VirtualMachineMO extends BaseMO { } // return the disk chain (VMDK datastore paths) for cloned snapshot - public String[] cloneFromCurrentSnapshot(String clonedVmName, int cpuSpeedMHz, int memoryMb, String diskDevice, + public Pair cloneFromCurrentSnapshot(String clonedVmName, int cpuSpeedMHz, int memoryMb, String diskDevice, ManagedObjectReference morDs) throws Exception { assert(morDs != null); String[] disks = getCurrentSnapshotDiskChainDatastorePaths(diskDevice); - cloneFromDiskChain(clonedVmName, cpuSpeedMHz, memoryMb, disks, morDs); - return disks; + VirtualMachineMO clonedVm = cloneFromDiskChain(clonedVmName, cpuSpeedMHz, memoryMb, disks, morDs); + return new Pair(clonedVm, disks); } - public void cloneFromDiskChain(String clonedVmName, int cpuSpeedMHz, int memoryMb, + public VirtualMachineMO cloneFromDiskChain(String clonedVmName, int cpuSpeedMHz, int memoryMb, String[] disks, ManagedObjectReference morDs) throws Exception { assert(disks != null); assert(disks.length >= 1); @@ -1576,6 +1576,7 @@ public class VirtualMachineMO extends BaseMO { vmConfigSpec.getDeviceChange().add(deviceConfigSpec); clonedVmMo.configureVm(vmConfigSpec); bSuccess = true; + return clonedVmMo; } finally { if(!bSuccess) { clonedVmMo.detachAllDisks(); From 3e14b6b795ba77f1d2ae6a241a918568150df899 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 16 Sep 2013 13:52:54 -0700 Subject: [PATCH 191/251] CLOUDSTACK-4687: UI > listView widget > actions in a grid row > when an action is completed, refresh the grid row with only data returned by getUpdatedItem() instead of combination of data returned by getUpdatedItem() and original embedded data in grid row. --- ui/scripts/system.js | 20 ++++++++++---------- ui/scripts/ui/widgets/listView.js | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index a3643f3f6fb..9a430873282 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -14032,25 +14032,25 @@ /* json = { "queryasyncjobresultresponse": { - "accountid": "b24f6e36-f0ca-11e2-8c16-d637902e3581", - "userid": "b24f7d8d-f0ca-11e2-8c16-d637902e3581", - "cmd": "org.apache.cloudstack.api.AssociateUcsProfileToBladeCmd", + "accountid": "835fb2d5-0b76-11e3-9350-f4f3e49b5dfe", + "userid": "835fc0e5-0b76-11e3-9350-f4f3e49b5dfe", + "cmd": "org.apache.cloudstack.api.DisassociateUcsProfileCmd", "jobstatus": 1, "jobprocstatus": 0, "jobresultcode": 0, "jobresulttype": "object", "jobresult": { "ucsblade": { - "id": "80ab25c8-3dcf-400e-8849-84dc5e1e6594", - "ucsmanagerid": "07b5b813-83ed-4859-952c-c95cafb63ac4", - "bladedn": "sys/chassis-1/blade-4" + "id": "f8d08575-7a1c-4f79-a588-d129c38bcc4f", + "ucsmanagerid": "0d87c1a6-5664-425c-9024-2ddd9605d260", + "bladedn": "sys/chassis-1/blade-1" } }, - "created": "2013-07-26T13:53:01-0700", - "jobid": "770bec68-7739-4127-8609-4b87bd7867d2" + "created": "2013-09-13T22:17:29-0700", + "jobid": "2c3698a8-39ac-43e6-8ade-86eb2d3726a0" } - }; - */ + }; + */ //for testing only (end) return json.queryasyncjobresultresponse.jobresult.ucsblade; diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 0745c411e35..076b3ab5c4d 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -212,7 +212,7 @@ if ($instanceRow.is(':visible')) { if (args.data) { $newRow = replaceItem($instanceRow, - $.extend($instanceRow.data('json-obj'), args.data), + args.data, //$.extend($instanceRow.data('json-obj'), args.data), /* $.extend($instanceRow.data('json-obj'), args.data) causes CLOUDSTACK-4687 */ actionFilter); } else { // Nothing new, so just put in existing data From 5d130332938dfeeb5db75f92de74ac97712e92e3 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 16 Sep 2013 14:52:01 -0700 Subject: [PATCH 192/251] CLOUDSTACK-4687: UI > infrastructure > zone > UCS > blades > add extra properties (Chassis, Blade ID) to ucsblade object returned by API which has only bladedn property. --- ui/scripts/sharedFunctions.js | 9 ++++++++- ui/scripts/system.js | 16 ++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 0ae8644970e..9287c1cb7da 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -1101,7 +1101,7 @@ function listViewDataProvider(args, data) { }); } -//used by infrastruct page and network page +//used by infrastructure page and network page var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { jsonObj.networkdomaintext = jsonObj.networkdomain; jsonObj.networkofferingidText = jsonObj.networkofferingid; @@ -1123,6 +1123,13 @@ var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { } } +//used by infrastructure page +var addExtraPropertiesToUcsBladeObject = function(jsonObj) { + var array1 = jsonObj.bladedn.split('/'); + jsonObj.chassis = array1[1]; + jsonObj.bladeid = array1[2]; +} + //find service object in network object function ipFindNetworkServiceByName(pName, networkObj) { diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 9a430873282..1ddbc21cb8a 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -13833,15 +13833,13 @@ */ //for testing only (end) - var data = json.listucsbladeresponse.ucsblade ? json.listucsbladeresponse.ucsblade : []; - for (var i = 0; i < data.length; i++) { - var array1 = data[i].bladedn.split('/'); - data[i].chassis = array1[1]; - data[i].bladeid = array1[2]; + var items = json.listucsbladeresponse.ucsblade ? json.listucsbladeresponse.ucsblade : []; + for (var i = 0; i < items.length; i++) { + addExtraPropertiesToUcsBladeObject(items[i]); } args.response.success({ actionFilter: bladeActionfilter, - data: data + data: items }); } }); @@ -13981,7 +13979,8 @@ }; */ //for testing only (end) - + + addExtraPropertiesToUcsBladeObject(json.queryasyncjobresultresponse.jobresult.ucsblade); return json.queryasyncjobresultresponse.jobresult.ucsblade; } } @@ -14052,7 +14051,8 @@ }; */ //for testing only (end) - + + addExtraPropertiesToUcsBladeObject(json.queryasyncjobresultresponse.jobresult.ucsblade); return json.queryasyncjobresultresponse.jobresult.ucsblade; } } From 584bb9bc0c5b93f0f36ab3c28f5b495624cbdd3c Mon Sep 17 00:00:00 2001 From: frank Date: Mon, 16 Sep 2013 16:11:03 -0700 Subject: [PATCH 193/251] CLOUDSTACK-4689 UI:API: associate Profile to Blade may take longer in some cases, which will result in timeout --- .../ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java index 05b1214d9c9..4000e987683 100755 --- a/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java +++ b/plugins/hypervisors/ucs/src/com/cloud/ucs/manager/UcsManagerImpl.java @@ -375,7 +375,7 @@ public class UcsManagerImpl implements UcsManager { UcsHttpClient client = new UcsHttpClient(mgrvo.getUrl()); String res = client.call(ucscmd); int count = 0; - int timeout = 600; + int timeout = 3600; while (count < timeout) { if (isBladeAssociated(mgrvo.getId(), bvo.getDn())) { break; From af2646f9a813b86ad8ce2987a11e64ac45833345 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Wed, 28 Aug 2013 23:40:22 -0400 Subject: [PATCH 194/251] Automation: Resource Limits - memory limits test cases --- .../memory_limits/test_domain_limits.py | 761 +++++++++++++++++ .../memory_limits/test_maximum_limits.py | 352 ++++++++ .../memory_limits/test_memory_limits.py | 764 ++++++++++++++++++ .../memory_limits/test_project_limits.py | 350 ++++++++ 4 files changed, 2227 insertions(+) create mode 100644 test/integration/component/memory_limits/test_domain_limits.py create mode 100644 test/integration/component/memory_limits/test_maximum_limits.py create mode 100644 test/integration/component/memory_limits/test_memory_limits.py create mode 100644 test/integration/component/memory_limits/test_project_limits.py diff --git a/test/integration/component/memory_limits/test_domain_limits.py b/test/integration/component/memory_limits/test_domain_limits.py new file mode 100644 index 00000000000..479ec0ba3e9 --- /dev/null +++ b/test/integration/component/memory_limits/test_domain_limits.py @@ -0,0 +1,761 @@ +# 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 memory resource limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Resources, + Domain + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + wait_for_cleanup, + find_suitable_host, + get_resource_type + ) + +class Services: + """Test memory resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 5120, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestDomainMemoryLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDomainMemoryLimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + self.account.name) + + if api_client is None: + api_client = self.apiclient + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a sub-domain under: %s" % self.domain.name) + self.child_domain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.child_do_admin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_1.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_1) + self.cleanup.append(self.child_domain_1) + + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=15360, + account=self.child_do_admin_1.name, + domainid=self.child_do_admin_1.domainid) + + self.child_domain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + self.child_do_admin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_2.id) + + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_2) + self.cleanup.append(self.child_domain_2) + + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=15360, + account=self.child_do_admin_2.name, + domainid=self.child_do_admin_2.domainid) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_change_service_offering(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 2. List Resource count for the root admin Memory usage + # 3. Upgrade and downgrade service offering + # 4. Resource count should list properly for the domain + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = { self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].memorytotal + + self.asserEqual(resource_count_after_stop, expected_resource_count, + "Resource count should be same after stopping the instance") + + self.debug("Creating service offering with 7 GB RAM") + self.services["service_offering"]["memory"] = 7168 + self.service_offering_7gb = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering_7gb) + + self.debug( + "Upgrade service offering of instance %s from %s to %s" % + (vm.name, + self.service_offering.name, + self.service_offering_7gb.name)) + + try: + vm.change_service_offering(self.apiclient, + serviceOfferingId=self.service_offering_7gb.id) + except Exception as e: + self.fail("Failed to change service offering of vm %s - %s" % + (vm.name, e)) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_upgrade = account_list[0].memorytotal + + self.debug(resource_count_after_upgrade) + + self.assertTrue(resource_count_after_upgrade > resource_count_after_stop, + "Resource count should be more than before, after upgrading service offering") + + self.debug( + "Down grade service offering of instance %s from %s to %s" % + (vm.name, + self.service_offering_7gb.name, + self.service_offering.name)) + + try: + vm.change_service_offering(self.apiclient, + serviceOfferingId=self.service_offering.id) + except Exception as e: + self.fail("Failed to change service offering of vm %s - %s" % + (vm.name, e)) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_downgrade = account_list[0].memorytotal + + self.assertTrue(resource_count_after_downgrade < resource_count_after_upgrade, + "Resource count should be less than before, after downgrading service offering") + + self.debug("Starting instance: %s" % vm.name) + try: + vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].memorytotal + + self.assertTrue(resource_count_after_start == resource_count_after_downgrade, + "Resource count should be same after starting the instance") + + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_migrate_vm(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 2. List Resource count for the root admin Memory usage + # 3. Migrate vm to another host, resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = { self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, vm) + self.debug("Migrating instance: %s to host: %s" % + (vm.name, host.name)) + try: + vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].memorytotal + + self.assertTrue(resource_count_after_migrate == resource_count, + "Resource count should be same after migrating the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_delete_vm(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 2. List Resource count for the root admin Memory usage + # 3. Delete vm, resource count should list as 0 after delete operation. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = { self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % vm.name) + try: + vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + # Wait for expunge interval to cleanup Memory + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].memorytotal + self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_04_deploy_multiple_vm(self): + """Test Deploy multiple VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM + # 2. Deploy multiple VMs with this service offering + # 3. List Resource count for the root admin Memory usage + # 4. Memory usage should list properly + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = { self.domain: self.admin, + self.child_domain: self.child_do_admin + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) + vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) + vm_3 = self.createInstance(service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance - Memory capacity is fully utilized") + with self.assertRaises(Exception): + self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) * 3 #Total 3 VMs + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + vm_2.delete(self.apiclient) + vm_3.delete(self.apiclient) + return + + +class TestMultipleChildDomainsMemory(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMultipleChildDomainsMemory, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls._cleanup = [] + 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, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, account, service_off, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + account.name) + + if api_client is None: + api_client = self.apiclient + + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.parent_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.parentd_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.domain.name) + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=10240, + account=self.parentd_admin.name, + domainid=self.parentd_admin.domainid) + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + + self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) + self.cdomain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + + self.cadmin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_1.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cdomain_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=5120, + domainid=self.cadmin_1.domainid) + + self.debug("Updating the Memory resource count for account: %s" % + self.cadmin_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=2148, + account=self.cadmin_1.name, + domainid=self.cadmin_1.domainid) + + self.cadmin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.cdomain_2.id + ) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cdomain_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=5120, + domainid=self.cadmin_2.domainid) + + self.debug("Updating the Memory resource count for domain: %s" % + self.cadmin_2.name) + Resources.updateLimit(self.apiclient, + resourcetype=9, + max=2148, + account=self.cadmin_2.name, + domainid=self.cadmin_2.domainid) + + # Cleanup the resources created at end of test + self.cleanup.append(self.cadmin_1) + self.cleanup.append(self.cadmin_2) + self.cleanup.append(self.cdomain_1) + self.cleanup.append(self.cdomain_2) + self.cleanup.append(self.parentd_admin) + self.cleanup.append(self.parent_domain) + + users = { + self.parent_domain: self.parentd_admin, + self.cdomain_1: self.cadmin_1, + self.cdomain_2: self.cadmin_2 + } + return users + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_multiple_child_domains(self): + """Test memory limits with multiple child domains""" + + # Validate the following + # 1. Create Domain1 with 10 GB RAM and 2 child domains with 5 GB + # each.Assign 2 GB for Domain1 admin1 & Domain1 User1 .Assign 2 + # GB for Domain2 admin1 & Domain2 User1 + # 2. Deploy VM's by Domain1 admin1/user1/ Domain2 user1/Admin1 account + # and verify the resource updates + # 3. Deploy VM by admin account after reaching max parent domain limit + # 4. Deploy VM with child account after reaching max child domain limit + # 5. Delete user account and verify the resource updates + # 6. Destroy user/admin account VM's and verify the child & Parent + # domain resource updates + + self.debug("Creating service offering with 2 GB RAM") + self.services["service_offering"]["memory"] = 2048 + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + + api_client_cadmin_1 = self.testClient.createUserApiClient( + UserName=self.cadmin_1.name, + DomainName=self.cadmin_1.domain) + + api_client_cadmin_2 = self.testClient.createUserApiClient( + UserName=self.cadmin_2.name, + DomainName=self.cadmin_2.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(account=self.cadmin_1, + service_off=self.service_offering, api_client=api_client_cadmin_1) + + vm_2 = self.createInstance(account=self.cadmin_2, + service_off=self.service_offering, api_client=api_client_cadmin_2) + + account_list = Account.list(self.apiclient, id=self.cadmin_1.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_1 = account_list[0].memorytotal + self.debug(resource_count_cadmin_1) + + account_list = Account.list(self.apiclient, id=self.cadmin_2.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_2 = account_list[0].memorytotal + self.debug(resource_count_cadmin_2) + + self.debug( + "Creating instance when Memory limit is fully used in parent domain") + with self.assertRaises(Exception): + self.createInstance(account=self.cadmin_1, + service_off=self.service_offering, api_client=api_client_cadmin_1) + + self.debug( + "Creating instance when Memory limit is fully used in child domain") + with self.assertRaises(Exception): + self.createInstance(account=self.cadmin_2, + service_off=self.service_offering, api_client=api_client_cadmin_2) + self.debug("Destroying instances: %s, %s" % (vm_1.name, vm_2.name)) + try: + vm_1.delete(self.apiclient) + vm_2.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + self.debug("Checking resource count for account: %s" % self.cadmin_1.name) + + account_list = Account.list(self.apiclient, id=self.cadmin_1.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_1 = account_list[0].memorytotal + + self.assertEqual(resource_count_cadmin_1, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + + self.debug("Checking resource count for account: %s" % self.cadmin_2.name) + + account_list = Account.list(self.apiclient, id=self.cadmin_1.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_cadmin_2 = account_list[0].memorytotal + + self.assertEqual(resource_count_cadmin_2, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + return diff --git a/test/integration/component/memory_limits/test_maximum_limits.py b/test/integration/component/memory_limits/test_maximum_limits.py new file mode 100644 index 00000000000..b1ebbb429b6 --- /dev/null +++ b/test/integration/component/memory_limits/test_maximum_limits.py @@ -0,0 +1,352 @@ +# 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 memory resource limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Resources, + Domain, + Project + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources + ) + +class Services: + """Test memory resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 5120, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestMaxMemoryLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMaxMemoryLimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + + self.debug("Creating service offering with 5 GB RAM") + + self.cleanup = [self.account, ] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, account=None, + project=None, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + self.account.name) + + if api_client is None: + api_client = self.apiclient + + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name if account else None, + domainid=account.domainid if account else None, + projectid=project.id if project else None, + networkids=networks, + serviceofferingid=service_off.id) + + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self, account_limit=2, domain_limit=2, project_limit=2): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.child_domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + + self.debug("domain crated with domain id %s" % self.child_domain.id) + + self.child_do_admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain.id + ) + + self.debug("domain admin created for domain id %s" % + self.child_do_admin.domainid) + + # Create project as a domain admin + self.project = Project.create(self.apiclient, + self.services["project"], + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + + # Cleanup created project at end of test + self.cleanup.append(self.project) + + + # Cleanup accounts created + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) + + self.debug("Updating the Memory resource count for domain: %s" % + self.child_domain.name) + # Update resource limits for account 1 + responses = Resources.updateLimit(self.apiclient, + resourcetype=9, + max=(account_limit * 1024), + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid + ) + + self.debug("Memory Resource count for child domain admin account is now: %s" % + responses.max) + + # Update resource limits for project + responses = Resources.updateLimit(self.apiclient, + resourcetype=9, + max=(project_limit * 1024), + projectid=self.project.id) + + self.debug("Memory Resource count for project is now") + self.debug(responses.max) + + # TODO: Update the Memory limit for domain only + responses = Resources.updateLimit(self.apiclient, + resourcetype=9, + max=(domain_limit * 1024), + domainid=self.child_domain.id) + + self.debug("Memory Resource count for domain %s with id %s is now %s" % + (responses.domain, responses.domainid, responses.max)) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_deploy_vm_domain_limit_reached(self): + """Test Try to deploy VM with admin account where account has not used + the resources but @ domain they are not available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ domain they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=8, domain_limit=4) + + api_client = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Creating instance with domain %s and admin account %s" % + (self.child_do_admin.domainid, + self.child_do_admin.name)) + + with self.assertRaises(Exception): + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_deploy_vm_account_limit_reached(self): + """Test Try to deploy VM with admin account where account has used + the resources but @ domain they are available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has used the + # resources but @ domain they are available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=7, domain_limit=14) + + api_client = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance with account: %s" % + self.child_do_admin.name) + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance in account 1 when Memory limit is reached") + + with self.assertRaises(Exception): + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client) + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_deploy_vm_project_limit_reached(self): + """Test TTry to deploy VM with admin account where account has not used + the resources but @ project they are not available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ project they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=8,domain_limit=8, project_limit=4) + + api_client = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance with project: %s" % self.project.name) + with self.assertRaises(Exception): + self.createInstance(project = self.project, + service_off=self.service_offering, api_client=api_client) + return + + @attr(tags=["advanced", "advancedns"]) + def test_04_deployVm__account_limit_reached(self): + """Test Try to deploy VM with admin account where account has used + the resources but @ project they are available""" + + # Validate the following + # 1. Try to deploy VM with admin account where account has used the + # resources but @ project they are available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds" + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=6, project_limit=12, domain_limit=12) + + api_client = self.testClient.createUserApiClient( + UserName=self.child_do_admin.name, + DomainName=self.child_do_admin.domain) + + self.debug("Deploying instance with account: %s" % + self.child_do_admin.name) + self.createInstance(account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance in account: %s when memory limit is reached" % + self.child_do_admin.name) + + with self.assertRaises(Exception): + self.createInstance(project=self.project, account=self.child_do_admin, + service_off=self.service_offering, api_client=api_client) + return diff --git a/test/integration/component/memory_limits/test_memory_limits.py b/test/integration/component/memory_limits/test_memory_limits.py new file mode 100644 index 00000000000..18eab4c2383 --- /dev/null +++ b/test/integration/component/memory_limits/test_memory_limits.py @@ -0,0 +1,764 @@ +# 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 memory resource limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Resources, + Domain + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + wait_for_cleanup, + find_suitable_host, + get_resource_type + ) + +class Services: + """Test memory resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 5120, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestMemoryLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMemoryLimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + self.vm = self.createInstance(service_off=self.service_offering) + + self.cleanup = [self.account, ] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + self.account.name) + + if api_client is None: + api_client = self.apiclient + + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_stop_start_instance(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 2 .List Resource count for the root admin Memory usage + # 3. Stop and start instance, resource count should list properly. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % self.vm.name) + try: + self.vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % self.vm.name) + try: + self.vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_start, + "Resource count should be same after stopping the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_migrate_instance(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 2. List Resource count for the root admin Memory usage + # 3. Migrate vm, resource count should list properly. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, self.vm) + self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) + try: + self.vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after stopping the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_delete_instance(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 2. List Resource count for the root admin Memory usage + # 3. Delete instance, resource count should be 0 after delete operation. + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % self.vm.name) + try: + self.vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + # Wait for expunge interval to cleanup Memory + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].memorytotal + self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_04_deploy_multiple_vm_with_5gb_ram(self): + """Test Deploy multiple VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM + # 2. Deploy multiple VMs with this service offering + # 3. List Resource count for the root admin Memory usage + # 4. Memory usage should list properly + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Creating two instances with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(service_off=self.service_offering) + self.createInstance(service_off=self.service_offering) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_new = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) * 3 #Total 3 VMs + + self.assertEqual(resource_count_new, expected_resource_count, + "Resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % vm_1.name) + try: + vm_1.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].memorytotal + + expected_resource_count -= int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count_after_delete, expected_resource_count, + "Resource count should match with the expected resource count") + return + +class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestDomainMemoryLimitsConfiguration, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, service_off, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + self.account.name) + + if api_client is None: + api_client = self.apiclient + + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupAccounts(self): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.child_domain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + self.child_do_admin_1 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_1.id + ) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_1) + self.cleanup.append(self.child_domain_1) + + self.debug("Creating a domain under: %s" % self.domain.name) + + self.child_domain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + self.child_do_admin_2 = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.child_domain_2.id) + # Cleanup the resources created at end of test + self.cleanup.append(self.child_do_admin_2) + self.cleanup.append(self.child_domain_2) + + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_stop_start_instance(self): + """Test Deploy VM with 5 GB memory & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB memory in child domains of root domain & Deploy VM + # 2. List Resource count memory usage + # 3. Stop and Start instance, check resource count. + # 4. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + self.debug("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_stop = account_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % vm.name) + try: + vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_start = account_list[0].memorytotal + + self.assertEqual(resource_count_after_stop, resource_count_after_start, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_migrate_instance(self): + """Test Deploy VM with 5 GB memory & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB memory in child domains of root domain & Deploy VM + # 2. List Resource count + # 3. Migrate instance to another host + # 4. Resource count should list properly. + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") + + host = find_suitable_host(self.apiclient, vm) + self.debug("Migrating instance: %s to host: %s" % + (vm.name, host.name)) + try: + vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_delete_instance(self): + """Test Deploy VM with 5 GB RAM & verify the usage""" + + # Validate the following + # 1. Create compute offering with 5 GB RAM in child domains of root domain & Deploy VM + # 2. List Resource count for the Memory usage + # 3. Delete instance + # 4. Resource count should list as 0 + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm = self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") + + self.debug("Destroying instance: %s" % vm.name) + try: + vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + return + + @attr(tags=["advanced", "advancedns","simulator"]) + @attr(configuration='max.account.memory') + def test_04_deploy_multiple_vm(self): + """Test Deploy multiple VM with 5 GB memory & verify the usage""" + #keep the configuration value - max.account.memory = 20480 + + # Validate the following + # 1. Create compute offering with 5 GB RAM + # 2. Deploy multiple VMs with this service offering in child domains of root domain + # 3. List Resource count for the root admin Memory usage + # 4. Memory usage should list properly + + self.debug("Creating service offering with 5 GB RAM") + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Adding to cleanup list after execution + self.cleanup.append(self.service_offering) + + self.debug("Setting up account and domain hierarchy") + self.setupAccounts() + users = {self.child_domain_1: self.child_do_admin_1, + self.child_domain_2: self.child_do_admin_2 + } + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + memory_account_gc = Resources.list(self.apiclient, + resourcetype = 9, #Memory + account = self.account.name, + domainid = self.domain.id + ) + + if memory_account_gc[0].max != 20480: + self.skipTest("This test case requires configuration value max.account.memory to be 20480") + + api_client = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + vm_1 = self.createInstance(service_off=self.service_offering, api_client=api_client) + vm_2 = self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + self.createInstance(service_off=self.service_offering, api_client=api_client) + + self.debug("Deploying instance - memory capacity is fully utilized") + with self.assertRaises(Exception): + self.createInstance(service_off=self.service_offering, api_client=api_client) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count = account_list[0].memorytotal + + expected_resource_count = int(self.services["service_offering"]["memory"]) * 4 #Total 4 vms + + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should with the expected resource count") + + self.debug("Destroying instance: %s" % vm_1.name) + try: + vm_1.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_delete = account_list[0].memorytotal + + expected_resource_count -= int(self.services["service_offering"]["memory"]) + + self.assertEqual(resource_count_after_delete, expected_resource_count, + "Resource count should match with the expected resource count") + + host = find_suitable_host(self.apiclient, vm_2) + self.debug("Migrating instance: %s to host: %s" % (vm_2.name, + host.name)) + try: + vm_2.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + account_list = Account.list(self.apiclient, id=self.account.id) + self.assertIsInstance(account_list, + list, + "List Accounts should return a valid response" + ) + resource_count_after_migrate = account_list[0].memorytotal + + self.debug(resource_count_after_migrate) + self.assertEqual(resource_count_after_delete, resource_count_after_migrate, + "Resource count should be same after migrating the instance") + return diff --git a/test/integration/component/memory_limits/test_project_limits.py b/test/integration/component/memory_limits/test_project_limits.py new file mode 100644 index 00000000000..1c0ed92d89e --- /dev/null +++ b/test/integration/component/memory_limits/test_project_limits.py @@ -0,0 +1,350 @@ +# 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 memory resource limits +""" +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.integration.lib.base import ( + Account, + ServiceOffering, + VirtualMachine, + Domain, + Project + ) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + wait_for_cleanup, + find_suitable_host, + get_resource_type + ) + +class Services: + """Test memory resource limit services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "resource", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 5120, # In MBs + }, + "virtual_machine": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'KVM', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "netmask": '255.255.255.0' + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "domain": { + "name": "Domain", + }, + "ostype": 'CentOS 5.3 (64-bit)', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode: Advanced, Basic + } + +class TestProjectsMemoryLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestProjectsMemoryLimits, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["mode"] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [cls.service_offering, ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + self.cleanup = [self.account, ] + + self.debug("Setting up account and domain hierarchy") + self.setupProjectAccounts() + + api_client = self.testClient.createUserApiClient( + UserName=self.admin.name, + DomainName=self.admin.domain) + + self.debug("Creating an instance with service offering: %s" % + self.service_offering.name) + self.vm = self.createInstance(project=self.project, + service_off=self.service_offering, api_client=api_client) + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createInstance(self, project, service_off, networks=None, api_client=None): + """Creates an instance in account""" + self.debug("Deploying an instance in account: %s" % + self.account.name) + + if api_client is None: + api_client = self.apiclient + + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) + + def setupProjectAccounts(self): + + self.debug("Creating a domain under: %s" % self.domain.name) + self.domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.admin = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + # Create project as a domain admin + self.project = Project.create(self.apiclient, + self.services["project"], + account=self.admin.name, + domainid=self.admin.domainid) + # Cleanup created project at end of test + self.cleanup.append(self.project) + self.cleanup.append(self.admin) + self.cleanup.append(self.domain) + self.debug("Created project with domain admin with name: %s" % + self.project.name) + + projects = Project.list(self.apiclient, id=self.project.id, + listall=True) + + self.assertEqual(isinstance(projects, list), True, + "Check for a valid list projects response") + project = projects[0] + self.assertEqual(project.name, self.project.name, + "Check project name from list response") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_01_project_vmlifecycle_start_stop_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Stop VM of an accounts added to the project to a new host + # 4. Resource count should list properly + # 5. Start VM of an accounts added to the project to a new host + # 6. Resource count should list properly + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.debug(project_list) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].memorytotal + + self.debug(resource_count) + + self.debug("Stopping instance: %s" % self.vm.name) + try: + self.vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_stop = project_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_stop, + "Resource count should be same after stopping the instance") + + self.debug("Starting instance: %s" % self.vm.name) + try: + self.vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_start = project_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_start, + "Resource count should be same after starting the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_02_project_vmlifecycle_migrate_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Migrate VM of an accounts added to the project to a new host + # 4. Resource count should list properly. + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.debug(project_list) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].memorytotal + self.debug(resource_count) + + host = find_suitable_host(self.apiclient, self.vm) + self.debug("Migrating instance: %s to host: %s" % + (self.vm.name, host.name)) + try: + self.vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Failed to migrate instance: %s" % e) + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_migrate = project_list[0].memorytotal + + self.assertEqual(resource_count, resource_count_after_migrate, + "Resource count should be same after migrating the instance") + return + + @attr(tags=["advanced", "advancedns","simulator"]) + def test_03_project_vmlifecycle_delete_instance(self): + + # Validate the following + # 1. Assign account to projects and verify the resource updates + # 2. Deploy VM with the accounts added to the project + # 3. Destroy VM of an accounts added to the project + # 4. Resource count should list as 0 after destroying the instance + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.debug(project_list) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count = project_list[0].memorytotal + self.debug(resource_count) + + self.debug("Destroying instance: %s" % self.vm.name) + try: + self.vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + # Wait for expunge interval to cleanup Memory + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + + self.debug("Checking memory resource count for project: %s" % self.project.name) + project_list = Project.list(self.apiclient, id=self.project.id, listall=True) + self.assertIsInstance(project_list, + list, + "List Projects should return a valid response" + ) + resource_count_after_delete = project_list[0].memorytotal + self.assertEqual(resource_count_after_delete, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + return From 39f7ddbb8f7eedb050da2991cdc1fb72a9e97f5f Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Tue, 17 Sep 2013 10:52:04 -0600 Subject: [PATCH 195/251] CLOUDSTACK-3565 - fix for new libvirt behavior when defining NFS pools that are already mounted in KVM hypervisor --- .../com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 42d9084727e..51e3363d2c8 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -154,7 +154,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { // if error is that pool is mounted, try to handle it if (e.toString().contains("already mounted")) { s_logger.error("Attempting to unmount old mount libvirt is unaware of at "+targetPath); - String result = Script.runSimpleBashScript("umount " + targetPath ); + String result = Script.runSimpleBashScript("umount -l " + targetPath ); if (result == null) { s_logger.error("Succeeded in unmounting " + targetPath); try { From af2951ad56481ae599f0f0e0c4e47eb5ffefca5b Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 17 Sep 2013 15:15:05 -0700 Subject: [PATCH 196/251] CLOUDSTACK-4534:[object_store_refactor] Deleting uploaded volume is not deleting the volume from backend. --- .../storage/volume/VolumeServiceImpl.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index c93dcee8def..5e633161d38 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -226,7 +226,7 @@ public class VolumeServiceImpl implements VolumeService { return false; } VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volumeId); - if (vol.getState() == State.Expunged && volumeStore == null) { + if ((vol.getState() == State.Expunged || (vol.getPodId() == null && vol.getState() == State.Destroy)) && volumeStore == null) { // volume is expunged from primary, as well as on secondary return true; } else { @@ -264,18 +264,23 @@ public class VolumeServiceImpl implements VolumeService { String volumePath = vol.getPath(); Long poolId = vol.getPoolId(); - if (poolId == null || volumePath == null || volumePath.trim().isEmpty()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Marking volume that was never created as destroyed: " + vol); + if (poolId == null || volumePath == null || volumePath.trim().isEmpty() ) { + // not created on primary store + if (volumeStore == null) { + // also not created on secondary store + if (s_logger.isDebugEnabled()) { + s_logger.debug("Marking volume that was never created as destroyed: " + vol); + } + volDao.remove(vol.getId()); + future.complete(result); + return future; } - volDao.remove(vol.getId()); - future.complete(result); - return future; } VolumeObject vo = (VolumeObject) volume; if (volume.getDataStore().getRole() == DataStoreRole.Image) { - volume.processEvent(Event.DestroyRequested); + // no need to change state in volumes table + volume.processEventOnly(Event.DestroyRequested); } else if (volume.getDataStore().getRole() == DataStoreRole.Primary) { volume.processEvent(Event.ExpungeRequested); } From b96aefee2fcf07f18c8259364dc68a2503ff5207 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 17 Sep 2013 15:12:53 -0700 Subject: [PATCH 197/251] CLOUDSTACK-4698: Check DHCP service in the network before get dhcp service provider --- server/src/com/cloud/network/NetworkManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index fb1dcb10fdd..2c00c5bebd6 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2519,6 +2519,10 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } public boolean isDhcpAccrossMultipleSubnetsSupported(Network network) { + if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) { + return false; + } + DhcpServiceProvider dhcpServiceProvider = getDhcpServiceProvider(network); Map capabilities = dhcpServiceProvider.getCapabilities().get(Network.Service.Dhcp); String supportsMultipleSubnets = capabilities.get(Network.Capability.DhcpAccrossMultipleSubnets); From 066d944c7877f1b46edc1e96e74178769795960a Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 17 Sep 2013 16:30:25 -0700 Subject: [PATCH 198/251] CLOUDSTACK-4688: UI > (1) Notifications widget - pollTimer() - error handling - check if args is null before trying to access args.message property (2) sharedFunctions.js - pollAsyncJobResult() - error handling - pass message argument to args.error(). --- ui/scripts/sharedFunctions.js | 4 +++- ui/scripts/ui/widgets/notifications.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 9287c1cb7da..16ca41fd572 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -107,7 +107,9 @@ var pollAsyncJobResult = function(args) { } }, error: function(XMLHttpResponse) { - args.error(); + args.error({ + message: parseXMLHttpResponse(XMLHttpResponse) + }); } }); } diff --git a/ui/scripts/ui/widgets/notifications.js b/ui/scripts/ui/widgets/notifications.js index fec64c51b18..9b7fc4cce03 100644 --- a/ui/scripts/ui/widgets/notifications.js +++ b/ui/scripts/ui/widgets/notifications.js @@ -111,7 +111,7 @@ }, incomplete: function(args) {}, error: function(args) { - if (args.message) { + if (args && args.message) { cloudStack.dialog.notice({ message: _s(args.message) }); From 9bff36ef45bdb894ce2c4218456af868791f4a63 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 17 Sep 2013 17:03:42 -0700 Subject: [PATCH 199/251] add sourcetemplateid for template created from volume/snapshot --- server/src/com/cloud/template/TemplateManagerImpl.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index fa92a02dd98..f511da29abc 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1403,16 +1403,22 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, //getting the prent volume long parentVolumeId=_snapshotDao.findById(snapshotId).getVolumeId(); VolumeVO parentVolume = _volumeDao.findById(parentVolumeId); - if (parentVolume != null && parentVolume.getIsoId() != null) { + if (parentVolume != null && parentVolume.getIsoId() != null && parentVolume.getIsoId() != 0) { privateTemplate.setSourceTemplateId(parentVolume.getIsoId()); _tmpltDao.update(privateTemplate.getId(), privateTemplate); + } else if (parentVolume != null && parentVolume.getTemplateId() != null) { + privateTemplate.setSourceTemplateId(parentVolume.getTemplateId()); + _tmpltDao.update(privateTemplate.getId(), privateTemplate); } } else if (volumeId != null) { VolumeVO parentVolume = _volumeDao.findById(volumeId); - if (parentVolume.getIsoId() != null) { + if (parentVolume.getIsoId() != null && parentVolume.getIsoId() != 0) { privateTemplate.setSourceTemplateId(parentVolume.getIsoId()); _tmpltDao.update(privateTemplate.getId(), privateTemplate); + } else if (parentVolume.getTemplateId() != null) { + privateTemplate.setSourceTemplateId(parentVolume.getTemplateId()); + _tmpltDao.update(privateTemplate.getId(), privateTemplate); } } TemplateDataStoreVO srcTmpltStore = this._tmplStoreDao.findByStoreTemplate(store.getId(), templateId); From c93e6e216d53b0f80ddc3d4f4254475b34d97cc8 Mon Sep 17 00:00:00 2001 From: Anshul Gangwar Date: Wed, 11 Sep 2013 15:10:46 +0530 Subject: [PATCH 200/251] CLOUDSTACK-2266: marvin tests for IP Address reservation within a network it also adds the ipaddress parameter to virtual machine's create method in base.py Signed-off-by: Prasanna Santhanam (cherry picked from commit 955408166cde19f0cc7142a086d7789e0adad614) --- .../component/test_ip_reservation.py | 316 ++++++++++++++++++ tools/marvin/marvin/integration/lib/base.py | 7 +- 2 files changed, 322 insertions(+), 1 deletion(-) create mode 100755 test/integration/component/test_ip_reservation.py diff --git a/test/integration/component/test_ip_reservation.py b/test/integration/component/test_ip_reservation.py new file mode 100755 index 00000000000..224212f21d2 --- /dev/null +++ b/test/integration/component/test_ip_reservation.py @@ -0,0 +1,316 @@ +# 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. +""" Tests for IP reservation feature +""" +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.cloudstackException import cloudstackAPIException +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +import netaddr + +from nose.plugins.attrib import attr + +class Services(object): + """Test IP Reservation + """ + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance ", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 200, # in MHz + "memory": 256, # In MBs + }, + "isolated_network_offering": { + "name": 'Network offering for Isolated Network', + "displaytext": 'Network offering-DA services', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'VirtualRouter', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "isolated_persistent_network_offering": { + "name": 'Network offering for Isolated Network', + "displaytext": 'Network offering-DA services', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "ispersistent": 'True', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'VirtualRouter', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "isolated_network": { + "name": "Isolated Network", + "displaytext": "Isolated Network", + "netmask": "255.255.255.0", + "gateway": "10.1.1.1" + }, + # update CIDR according to netmask and gateway in isolated_network + "isolated_network_cidr": "10.1.1.0/24", + "virtual_machine": { + "displayname": "Test VM", + }, + "ostype": 'CentOS 5.3 (64-bit)', + # Cent OS 5.3 (64 bit) + "sleep": 90, + "timeout": 10, + "mode": 'advanced' + } + + +class TestIpReservation(cloudstackTestCase): + """Test IP Range Reservation with a Network + """ + @classmethod + def setUpClass(cls): + cls.api_client = super(TestIpReservation, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.name + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.isolated_network_offering = cls.create_isolated_network_offering("isolated_network_offering") + cls.isolated_persistent_network_offering = cls.create_isolated_network_offering("isolated_persistent_network_offering") + cls.isolated_network = cls.create_isolated_network(cls.isolated_network_offering.id) + cls.isolated_persistent_network = cls.create_isolated_network(cls.isolated_persistent_network_offering.id) + # network will be deleted as part of account cleanup + cls._cleanup = [ + cls.account, cls.service_offering, cls.isolated_network_offering, cls.isolated_persistent_network_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def create_isolated_network_offering(cls, network_offering): + isolated_network_offering = NetworkOffering.create( + cls.api_client, + cls.services[network_offering], + conservemode=False + ) + # Update network offering state from disabled to enabled. + network_offering_update_response = NetworkOffering.update( + isolated_network_offering, + cls.api_client, + id=isolated_network_offering.id, + state="enabled" + ) + return isolated_network_offering + + @classmethod + def create_isolated_network(cls, network_offering_id): + isolated_network = Network.create( + cls.api_client, + cls.services["isolated_network"], + networkofferingid=network_offering_id, + accountid=cls.account.name, + domainid=cls.domain.id, + zoneid=cls.zone.id + ) + cls.debug("isolated network is created: " + isolated_network.id) + return isolated_network + + + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [ ] + update_response = Network.update(self.isolated_persistent_network, self.apiclient, id=self.isolated_persistent_network.id, guestvmcidr=self.services["isolated_network_cidr"]) + if self.services["isolated_network_cidr"] <> update_response.cidr: + raise Exception("problem in updating cidr for test setup") + return + + def tearDown(self): + try: + # Clean up, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def create_virtual_machine(self, network_id=None, ip_address=None): + virtual_machine = VirtualMachine.create(self.apiclient, + self.services["virtual_machine"], + networkids=network_id, + serviceofferingid=self.service_offering.id, + accountid=self.account.name, + domainid=self.domain.id, + ipaddress=ip_address + ) + self.debug("Virtual Machine is created: " + virtual_machine.id) + self.cleanup.append(virtual_machine) + return virtual_machine + + @attr(tags=["advanced"]) + def test_network_not_implemented(self): + # steps + # 1. update guestvmcidr of isolated network (non persistent) + # + # validation + # should throw exception as network is not in implemented state as no vm is created + try: + update_response = Network.update(self.isolated_network, self.apiclient, id=isolated_network.id, guestvmcidr="10.1.1.0/26") + self.fail("Network Update of guest VM CIDR is successful withot any VM deployed in network") + except Exception as e: + self.debug("Network Update of guest VM CIDR should fail as there is no VM deployed in network") + + @attr(tags=["advanced"]) + def test_vm_create_after_reservation(self): + # steps + # 1. create vm in persistent isolated network with ip in guestvmcidr + # 2. update guestvmcidr + # 3. create another VM + # + # validation + # 1. guest vm cidr should be successfully updated with correct value + # 2. existing guest vm ip should not be changed after reservation + # 3. newly created VM should get ip in guestvmcidr + guest_vm_cidr = u"10.1.1.0/29" + virtual_machine_1 = None + try: + virtual_machine_1 = self.create_virtual_machine(network_id=self.isolated_persistent_network.id, ip_address=u"10.1.1.3") + except Exception as e: + self.skipTest("VM creation fails in network ") + + update_response = Network.update(self.isolated_persistent_network, self.apiclient, id=self.isolated_persistent_network.id, guestvmcidr=guest_vm_cidr) + self.assertEqual(guest_vm_cidr, update_response.cidr, "cidr in response is not as expected") + vm_list = VirtualMachine.list(self.apiclient, + id=virtual_machine_1.id) + self.assertEqual(isinstance(vm_list, list), + True, + "VM list response in not a valid list") + self.assertEqual(vm_list[0].nic[0].ipaddress, + virtual_machine_1.ipaddress, + "VM IP should not change after reservation") + try: + virtual_machine_2 = self.create_virtual_machine(network_id=self.isolated_persistent_network.id) + if netaddr.IPAddress(virtual_machine_2.ipaddress) not in netaddr.IPNetwork(guest_vm_cidr): + self.fail("Newly created VM doesn't get IP from reserverd CIDR") + except Exception as e: + self.skipTest("VM creation fails, cannot validate the condition") + + @attr(tags=["advanced"]) + def test_reservation_after_router_restart(self): + # steps + # 1. update guestvmcidr of persistent isolated network + # 2. reboot router + # + # validation + # 1. guest vm cidr should be successfully updated with correct value + # 2. network cidr should remain same after router restart + guest_vm_cidr = u"10.1.1.0/29" + + update_response = Network.update(self.isolated_persistent_network, self.apiclient, id=self.isolated_persistent_network.id, guestvmcidr=guest_vm_cidr) + self.assertEqual(guest_vm_cidr, update_response.cidr, "cidr in response is not as expected") + + routers = Router.list(self.apiclient, + networkid=self.isolated_persistent_network.id, + listall=True) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return valid response" + ) + if not routers: + self.skipTest("Router list should not be empty, skipping test") + + Router.reboot(self.apiclient, routers[0].id) + networks = Network.list(self.apiclient, id=self.isolated_persistent_network.id) + self.assertEqual( + isinstance(networks, list), + True, + "list Networks should return valid response" + ) + self.assertEqual(networks[0].cidr, guest_vm_cidr, "guestvmcidr should match after router reboot") + + @attr(tags=["advanced"]) + def test_vm_create_outside_cidr_after_reservation(self): + # steps + # 1. update guestvmcidr of persistent isolated network + # 2. create another VM with ip outside guestvmcidr + # + # validation + # 1. guest vm cidr should be successfully updated with correct value + # 2 newly created VM should not be created and result in exception + guest_vm_cidr = u"10.1.1.0/29" + update_response = Network.update(self.isolated_persistent_network, self.apiclient, id=self.isolated_persistent_network.id, guestvmcidr=guest_vm_cidr) + self.assertEqual(guest_vm_cidr, update_response.cidr, "cidr in response is not as expected") + try: + self.create_virtual_machine(network_id=self.isolated_persistent_network.id, ip_address=u"10.1.1.9") + self.fail("vm should not be created ") + except Exception as e: + self.debug("exception as IP is outside of guestvmcidr") diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 9e460658217..df8140685e6 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -310,7 +310,7 @@ class VirtualMachine: domainid=None, zoneid=None, networkids=None, serviceofferingid=None, securitygroupids=None, projectid=None, startvm=None, diskofferingid=None, affinitygroupnames=None, affinitygroupids=None, group=None, - hostid=None, keypair=None, mode='default', method='GET'): + hostid=None, keypair=None, ipaddress=None, mode='default', method='GET'): """Create the instance""" cmd = deployVirtualMachine.deployVirtualMachineCmd() @@ -370,6 +370,11 @@ class VirtualMachine: elif "keypair" in services: cmd.keypair = services["keypair"] + if ipaddress: + cmd.ipaddress = ipaddress + elif ipaddress in services: + cmd.ipaddress = services["ipaddress"] + if securitygroupids: cmd.securitygroupids = [str(sg_id) for sg_id in securitygroupids] From 8cc37c24705dfcc2ddb973673522fa39bf246ef5 Mon Sep 17 00:00:00 2001 From: Girish Shilamkar Date: Fri, 13 Sep 2013 02:43:33 -0400 Subject: [PATCH 201/251] CLOUDSTACK-1800: Add automation tests for reset sshkey to access VM Reviewed-By: Harikrishna Patnala Signed-off-by: Prasanna Santhanam (cherry picked from commit dd4b1cbf449b63cd2b34197c7c9b19ec1cc4822e) --- .../component/test_reset_ssh_keypair.py | 1518 +++++++++++++++++ 1 file changed, 1518 insertions(+) create mode 100644 test/integration/component/test_reset_ssh_keypair.py diff --git a/test/integration/component/test_reset_ssh_keypair.py b/test/integration/component/test_reset_ssh_keypair.py new file mode 100644 index 00000000000..8b499d017af --- /dev/null +++ b/test/integration/component/test_reset_ssh_keypair.py @@ -0,0 +1,1518 @@ +# 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 reset SSH keypair +""" + +#Import Local Modules +from marvin.cloudstackTestCase import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +#Import System modules +import tempfile +import os +from nose.plugins.attrib import attr + + +class Services: + """Test remote SSH client Services """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "virtual_machine": { + "displayname": "VM", + "username": "root", + # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 128, + "storagetype": "local" + }, + "egress": { + "name": 'web', + "protocol": 'TCP', + "startport": 80, + "endport": 80, + "cidrlist": '0.0.0.0/0', + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "passwordenabled": True, + "ispublic": True, + }, + "ostype": 'CentOS 5.3 (64-bit)', + # Cent OS 5.3 (64 bit) + "SSHEnabledTemplate": "SSHkey", + "SSHPasswordEnabledTemplate": "SSHKeyPassword", + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + } + +class TestResetSSHKeypair(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super( + TestResetSSHKeypair, + cls).getClsTestClient().getApiClient() + + cls.services = Services().services + # Get Zone, Domain and templates + domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + + # Set Zones and disk offerings + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + # Create Account, VMs, NAT Rules etc + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=domain.id + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + networkid = cls.virtual_machine.nic[0].networkid + + # create egress rule to allow wget of my cloud-set-guest-password script + if cls.zone.networktype.lower() == 'advanced': + EgressFireWallRule.create(cls.api_client, + networkid=networkid, + protocol=cls.services["egress"]["protocol"], + startport=cls.services["egress"]["startport"], + endport=cls.services["egress"]["endport"], + cidrlist=cls.services["egress"]["cidrlist"]) + + cls.virtual_machine.password = cls.services["virtual_machine"]["password"] + ssh = cls.virtual_machine.get_ssh_client() + + # below steps are required to get the new password from VR(reset password) + # http://cloudstack.org/dl/cloud-set-guest-password + # Copy this file to /etc/init.d + # chmod +x /etc/init.d/cloud-set-guest-password + # chkconfig --add cloud-set-guest-password + # similar steps to get SSH key from web so as to make it ssh enabled + + cmds = [ + "cd /etc/init.d;wget http://people.apache.org/~tsp/cloud-set-guest-password", + "chmod +x /etc/init.d/cloud-set-guest-password", + "chkconfig --add cloud-set-guest-password", + "cd /etc/init.d;wget http://downloads.sourceforge.net/project/cloudstack/SSH%20Key%20Gen%20Script/" + \ + "cloud-set-guest-sshkey.in?r=http%3A%2F%2Fsourceforge" + \ + ".net%2Fprojects%2Fcloudstack%2Ffiles%2FSSH%2520Key%2520Gen%2520Script%2F&ts=1331225219&use_mirror=iweb", + "chmod +x /etc/init.d/cloud-set-guest-sshkey.in", + "chkconfig --add cloud-set-guest-sshkey.in" + ] + for c in cmds: + result = ssh.execute(c) + + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + # Poll listVM to ensure VM is stopped properly + timeout = cls.services["timeout"] + while True: + time.sleep(cls.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + cls.api_client, + id=cls.virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) " % + vm.id) + + timeout = timeout - 1 + + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + if isinstance(list_volume, list): + cls.volume = list_volume[0] + else: + raise Exception( + "Exception: Unable to find root volume for VM: %s" % + cls.virtual_machine.id) + + cls.services["template"]["ostype"] = cls.services["ostype"] + #Create templates for Edit, Delete & update permissions testcases + cls.pw_ssh_enabled_template = Template.create( + cls.api_client, + cls.services["template"], + cls.volume.id, + account=cls.account.name, + domainid=cls.account.domainid + ) + # Delete the VM - No longer needed + cls.virtual_machine.delete(cls.api_client) + + cls._cleanup = [ + cls.service_offering, + cls.pw_ssh_enabled_template, + cls.account + ] + + @classmethod + def tearDownClass(cls): + # Cleanup VMs, templates etc. + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + + self.keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + # Cleanup + self.cleanup = [] + self.tmp_files = [] + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + #cleanup_resources(self.apiclient, self.cleanup) + for tmp_file in self.tmp_files: + os.remove(tmp_file) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_01_reset_ssh_keys(self): + """Test Reset SSH keys for VM already having SSH key""" + + # Validate the following + # 1. Create a VM having SSH keyPair + # 2. Stop the VM + # 3. Reset SSH key pair. Verify VM is not restarted automatically as + # result of API execution. User should be able to ssh into the VM + # using new keypair when VM is restarted + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + # Clenaup at end of execution + self.tmp_files.append(keyPairFilePath) + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + return + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + self.debug("SSH key path: %s" % str(keyPairFilePath)) + try: + virtual_machine.get_ssh_client(keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_02_reset_ssh_key_password_enabled_template(self): + """Reset SSH keys for VM created from password enabled template and + already having SSH key """ + + # Validate the following + # 1. Create VM from password enabled template and having SSH keyPair + # 2. Stop the VM + # 3. Reset SSH key pair. Verify VM is not restarted automatically + # as a result of API execution + # User should be able to ssh into the VM using new keypair + # User should be able to login into VM using new password + # returned by the API + + self.debug("Deploying the virtual machine in default network offering") + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + + try: + self.debug("SSHing using password") + virtual_machine.get_ssh_client() + except Exception as e: + self.fail("Failed to SSH into VM with password: %s, %s" % + (virtual_machine.name, e)) + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_03_reset_ssh_with_no_key(self): + """Reset SSH key for VM having no SSH key""" + + # Validate the following + # 1.Create a VM + # 2. Stop the VM + # 3. Reset SSH key pair + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_04_reset_key_passwd_enabled_no_key(self): + """Reset SSH keys for VM created from password enabled template and + have no previous SSH key""" + + # Validate the following + # 1.Create a VM from password enabled template + # 2. Stop the VM + # 3. Reset SSH key pair. Verify VM is not restarted automatically as a + # result of API execution + # User should be able to ssh into the VM using new keypair when + # VM is restarted + # User should be able to login into VM using new password returned + # by the API + + self.debug("Deploying the virtual machine in default network offering") + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_05_reset_key_in_running_state(self): + """Reset SSH keys for VM already having SSH key when VM is in running + state""" + + # Validate the following + # 1.Create a VM having SSH keyPair + # 2. Reset SSH key pair. Api returns error message VM is running + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + with self.assertRaises(Exception): + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_06_reset_key_passwd_enabled_vm_running(self): + """Reset SSH keys for VM created from password enabled template and + already having SSH key and VM is in running state""" + + # Validate the following + # 1. Reset SSH keys for VM created from password enabled template + # and already having SSH key and VM is in running state + # 2. APi returns error message Vm is running + + self.debug("Deploying the virtual machine in default network offering") + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + with self.assertRaises(Exception): + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_07_reset_keypair_invalid_params(self): + """Verify API resetSSHKeyForVirtualMachine with incorrect parameters""" + + # Validate the following + # 1. Create the VM + # 2. Stop the VM + # 3. Call resetSSHKeyForVirtualMachine API with incorrect parameter + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + with self.assertRaises(Exception): + virtual_machine.resetSshKey( + self.apiclient, + keypair=random_gen() + ".pem", + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Reset SSH key pair failed due to invalid parameters") + + virtual_machine.delete(self.apiclient) + return + +class TestResetSSHKeyUserRights(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super( + TestResetSSHKeyUserRights, + cls).getClsTestClient().getApiClient() + + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + + # Set Zones and disk offerings + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + # Create Account, VMs, NAT Rules etc + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + networkid = cls.virtual_machine.nic[0].networkid + + # create egress rule to allow wget of my cloud-set-guest-password script + if cls.zone.networktype.lower() == 'advanced': + EgressFireWallRule.create(cls.api_client, + networkid=networkid, + protocol=cls.services["egress"]["protocol"], + startport=cls.services["egress"]["startport"], + endport=cls.services["egress"]["endport"], + cidrlist=cls.services["egress"]["cidrlist"]) + + cls.virtual_machine.password = cls.services["virtual_machine"]["password"] + ssh = cls.virtual_machine.get_ssh_client() + + #below steps are required to get the new password from VR(reset password) + #http://cloudstack.org/dl/cloud-set-guest-password + #Copy this file to /etc/init.d + #chmod +x /etc/init.d/cloud-set-guest-password + #chkconfig --add cloud-set-guest-password + # Do similar steps to get SSH key from web so as to make it ssh enabled + + cmds = [ + "cd /etc/init.d;wget http://downloads.sourceforge.net/project/cloudstack/SSH%20Key%20Gen%20Script/" + \ + "cloud-set-guest-sshkey.in?r=http%3A%2F%2Fsourceforge" + \ + ".net%2Fprojects%2Fcloudstack%2Ffiles%2FSSH%2520Key%2520Gen%2520Script%2F&ts=1331225219&use_mirror=iweb", + "chmod +x /etc/init.d/cloud-set-guest-sshkey.in", + "chkconfig --add cloud-set-guest-sshkey.in" + ] + for c in cmds: + result = ssh.execute(c) + + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + # Poll listVM to ensure VM is stopped properly + timeout = cls.services["timeout"] + while True: + time.sleep(cls.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + cls.api_client, + id=cls.virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) " % + vm.id) + + timeout = timeout - 1 + + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + if isinstance(list_volume, list): + cls.volume = list_volume[0] + else: + raise Exception( + "Exception: Unable to find root volume for VM: %s" % + cls.virtual_machine.id) + + cls.services["template"]["ostype"] = cls.services["ostype"] + #Create templates for Edit, Delete & update permissions testcases + cls.pw_ssh_enabled_template = Template.create( + cls.api_client, + cls.services["template"], + cls.volume.id + ) + # Delete the VM - No longer needed + cls.virtual_machine.delete(cls.api_client) + + cls._cleanup = [ + cls.service_offering, + cls.pw_ssh_enabled_template, + cls.account + ] + + @classmethod + def tearDownClass(cls): + # Cleanup VMs, templates etc. + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + + # Set Zones and disk offerings + self.services["virtual_machine"]["zoneid"] = self.zone.id + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + # Cleanup + self.cleanup = [self.service_offering] + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_01_reset_keypair_normal_user(self): + """Verify API resetSSHKeyForVirtualMachine for non admin non root + domain user""" + + # Validate the following + # 1. Create a VM for non admin non root domain user + # 2. Stop the VM + # 3. Reset SSH key pair for non admin non root domain user. Verify VM + # is not restarted automatically as a result of API execution + # User should be able to ssh into the VM using new keypair when + # VM is restarted + + # Create account, SSH key pair etc. + self.debug("Creating a normal user account") + self.user_account = Account.create( + self.apiclient, + self.services["account"], + admin=False + ) + self.cleanup.append(self.user_account) + self.debug("Account created: %s" % self.user_account.name) + + self.services = Services().services + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.user_account.name, + domainid=self.user_account.domainid, + zoneid=self.zone.id, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.user_account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.user_account.name, + domainid=self.user_account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.user_account.name, + domainid=self.user_account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.user_account.name, + domainid=self.user_account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_02_reset_keypair_domain_admin(self): + """Verify API resetSSHKeyForVirtualMachine for domain admin non root + domain user""" + + # Validate the following + # 1.Create a VM for domain admin non root domain user + # 2. Stop the VM + # 3. Reset SSH key pair for domain admin non root domain user. Verify + # VM is not restarted automatically as a result of API execution + # User should be able to ssh into the VM using new keypair when + # VM is restarted + + # Create account, SSH key pair etc. + self.debug("Creating a domain admin account") + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup.append(self.account) + self.debug("Account created: %s" % self.account.name) + + self.debug("Generating SSH keypair for the account: %s" % + self.account.name) + self.keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + self.keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(self.keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + + virtual_machine.delete(self.apiclient) + return + + @attr(tags=["simulator", "basic", "advanced"]) + def test_03_reset_keypair_root_admin(self): + """Verify API resetSSHKeyForVirtualMachine for domain admin root + domain user""" + + # Validate the following + # 1.Create a VM for domain admin, root domain user + # 2. Stop the VM + # 3. Reset SSH key pair for domain admin non root domain user. Verify + # VM is not restarted automatically as a result of API execution + # User should be able to ssh into the VM using new keypair when + # VM is restarted + + # Create account, SSH key pair etc. + self.debug("Creating a ROOT admin account") + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True + ) + self.cleanup.append(self.account) + self.debug("Account created: %s" % self.account.name) + + self.debug("Generating SSH keypair for the account: %s" % + self.account.name) + self.keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + self.keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(self.keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Deploying the virtual machine in default network offering") + + # Spawn an instance + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.pw_ssh_enabled_template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + keypair=self.keypair.name, + mode=self.services["mode"] + ) + + self.debug("Check if the VM is properly deployed or not?") + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return the valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be running after deployment" + ) + self.debug("Stopping the virtual machine") + try: + virtual_machine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop virtual machine: %s, %s" % + (virtual_machine.id, e)) + + self.debug("Creating a new SSH keypair for account: %s" % + self.account.name) + new_keypair = SSHKeyPair.create( + self.apiclient, + name=random_gen() + ".pem", + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created a new keypair with name: %s" % new_keypair.name) + + self.debug("Writing the private key to local file") + keyPairFilePath = tempfile.gettempdir() + os.sep + new_keypair.name + + self.debug("File path: %s" % keyPairFilePath) + + f = open(keyPairFilePath, "w+") + f.write(new_keypair.privatekey) + f.close() + + os.system("chmod 400 " + keyPairFilePath) + + self.debug("Resetting the SSH key pair for instance: %s" % + virtual_machine.name) + try: + virtual_machine.resetSshKey( + self.apiclient, + keypair=new_keypair.name, + name=new_keypair.name, + account=self.account.name, + domainid=self.account.domainid + ) + except Exception as e: + self.fail("Failed to reset SSH key: %s, %s" % + (virtual_machine.name, e)) + self.debug("Starting the virtual machine after resetting the keypair") + try: + virtual_machine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start virtual machine: %s, %s" % + (virtual_machine.name, e)) + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid, + listall=True + ) + if vms[0].state == "Running": + break + self.debug("Vm not in Running state sleep 60s") + time.sleep(60) + + self.debug("SSHing with new keypair") + try: + virtual_machine.get_ssh_client( + keyPairFileLocation=str(keyPairFilePath)) + except Exception as e: + self.fail("Failed to SSH into VM with new keypair: %s, %s" % + (virtual_machine.name, e)) + virtual_machine.delete(self.apiclient) + return From a9218f341186dcc5914dc23b283636652f83416a Mon Sep 17 00:00:00 2001 From: Harikrishna Patnala Date: Tue, 20 Aug 2013 16:21:47 +0530 Subject: [PATCH 202/251] CLOUDSTACK-2247: Automation - Reset a VM on Reboot Signed-off-by: Prasanna Santhanam (cherry picked from commit 62b0ad03c871c7100433b39593a82d393879124e) --- .../smoke/test_reset_vm_on_reboot.py | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 test/integration/smoke/test_reset_vm_on_reboot.py diff --git a/test/integration/smoke/test_reset_vm_on_reboot.py b/test/integration/smoke/test_reset_vm_on_reboot.py new file mode 100644 index 00000000000..4e52f0fa8d2 --- /dev/null +++ b/test/integration/smoke/test_reset_vm_on_reboot.py @@ -0,0 +1,219 @@ +# 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 reset Vm on reboot +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + +class Services: + """Test VM Life Cycle Services + """ + + def __init__(self): + self.services = { + + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "small": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "SmallInstance_volatile", + "displaytext": "SmallInstance_volatile", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + }, + }, + #Change this + "template": { + "displaytext": "xs", + "name": "xs", + "passwordenabled": False, + }, + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostype": 'CentOS 5.3 (64-bit)', + # CentOS 5.3 (64-bit) + } + + +class TestResetVmOnReboot(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls.api_client = super(TestResetVmOnReboot, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + domain = get_domain(cls.api_client, cls.services) + zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = zone.networktype + + template = get_template( + cls.api_client, + zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings ?? + cls.services["small"]["zoneid"] = zone.id + cls.services["small"]["template"] = template.id + + # Create account, service offerings, vm. + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=domain.id + ) + + cls.small_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["small"], + isvolatile="true" + ) + + #create a virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.small_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.small_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + cls.api_client = super(TestResetVmOnReboot, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created ISOs + cleanup_resources(self.apiclient, self.cleanup) + return + + @attr(hypervisor="xenserver") + @attr(tags=["advanced", "basic"]) + def test_01_reset_vm_on_reboot(self): + """Test reset virtual machine on reboot + """ + # Validate the following + # create vm and list the volume for that VM. Reboot vm and check if root volume is different as before. + + volumelist_before_reboot = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + + self.assertNotEqual( + volumelist_before_reboot, + None, + "Check if volume is in listvolumes" + ) + volume_before_reboot = volumelist_before_reboot[0] + + + self.debug("Rebooting vm %s " % (self.virtual_machine.id)) + + cmd = rebootVirtualMachine.rebootVirtualMachineCmd() + cmd.id = self.virtual_machine.id + self.apiclient.rebootVirtualMachine(cmd) + + volumelist_after_reboot = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + + self.assertNotEqual( + volumelist_after_reboot, + None, + "Check if volume is in listvolumes" + ) + + volume_after_reboot = volumelist_after_reboot[0] + self.assertNotEqual( + volume_after_reboot.id, + volume_before_reboot.id, + "Check whether volumes are different before and after reboot" + ) + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + list_vm_response, + None, + "Check virtual machine is listVirtualMachines" + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.state, + 'Running', + "Check the state of VM" + ) + return From 7d06e77ed9bfacf6d3b74f396c705567d4bf8ba2 Mon Sep 17 00:00:00 2001 From: Girish Shilamkar Date: Fri, 13 Sep 2013 20:43:45 -0400 Subject: [PATCH 203/251] CLOUDSTACK-4637: Add 30sec sleep before router is ssh'd Egress rules testcases access vm via router. Sleep before accessing router else the expect fails since router is not accessible. Also use router.hostid instead of vm.hostid to identify the host. Signed-off-by: venkataswamybabu budumuru --- .../component/test_egress_fw_rules.py | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/integration/component/test_egress_fw_rules.py b/test/integration/component/test_egress_fw_rules.py index ef0fc5a7e9c..5c18f9c10a2 100644 --- a/test/integration/component/test_egress_fw_rules.py +++ b/test/integration/component/test_egress_fw_rules.py @@ -18,7 +18,7 @@ """ """ #Import Local Modules -#import unittest +import unittest from nose.plugins.attrib import attr from marvin.cloudstackTestCase import cloudstackTestCase from marvin.integration.lib.base import (Account, @@ -198,7 +198,6 @@ class TestEgressFWRules(cloudstackTestCase): # Enable Network offering self.network_offering.update(self.apiclient, state='Enabled') - def create_vm(self, pfrule=False, egress_policy=True, RR=False): self.create_network_offering(egress_policy, RR) # Creating network using the network offering created @@ -229,20 +228,7 @@ class TestEgressFWRules(cloudstackTestCase): def exec_script_on_user_vm(self, script, exec_cmd_params, expected_result, negative_test=False): try: - if self.apiclient.hypervisor.lower() == 'vmware': - #SSH is done via management server for Vmware - sourceip = self.apiclient.connection.mgtSvr - else: - #For others, we will have to get the ipaddress of host connected to vm - hosts = list_hosts(self.apiclient, - id=self.virtual_machine.hostid) - self.assertEqual(isinstance(hosts, list), - True, - "Check list response returns a valid list") - host = hosts[0] - sourceip = host.ipaddress - #Once host or mgt server is reached, SSH to the router connected to VM - # look for Router for Cloudstack VM network. + vm_network_id = self.virtual_machine.nic[0].networkid vm_ipaddress = self.virtual_machine.nic[0].ipaddress list_routers_response = list_routers(self.apiclient, @@ -253,6 +239,26 @@ class TestEgressFWRules(cloudstackTestCase): True, "Check for list routers response return valid data") router = list_routers_response[0] + + #Once host or mgt server is reached, SSH to the router connected to VM + # look for Router for Cloudstack VM network. + if self.apiclient.hypervisor.lower() == 'vmware': + #SSH is done via management server for Vmware + sourceip = self.apiclient.connection.mgtSvr + else: + #For others, we will have to get the ipaddress of host connected to vm + hosts = list_hosts(self.apiclient, + id=router.hostid) + self.assertEqual(isinstance(hosts, list), + True, + "Check list response returns a valid list") + host = hosts[0] + sourceip = host.ipaddress + + self.debug("Sleep %s seconds for network on router to be up" + % self.services['sleep']) + time.sleep(self.services['sleep']) + if self.apiclient.hypervisor.lower() == 'vmware': key_file = " -i /var/cloudstack/management/.ssh/id_rsa " else: @@ -266,7 +272,6 @@ class TestEgressFWRules(cloudstackTestCase): "expect \"root@%s's password: \"\n" % (vm_ipaddress) + \ "send \"password\r\"\n" + \ "interact\n" - self.debug("expect_script>>\n%s< Date: Tue, 10 Sep 2013 09:53:19 +0530 Subject: [PATCH 204/251] Added missing test for VPC load balancing rule from old QA repo to cloudstack repo def test_04_VPC_CreateLBRuleInMultipleNetworksVRStoppedState Since VPC now only allows load balancing on a single tier the test case has been updated to check that condition Ref : http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.0.2/html/Installation_Guide/configure-vpc.html Signed-off-by: venkataswamybabu budumuru (cherry picked from commit 5b5a617544fb8160b9429a24f1aa943e04d4f98a) --- .../component/test_vpc_network_lbrules.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/integration/component/test_vpc_network_lbrules.py b/test/integration/component/test_vpc_network_lbrules.py index de29ce19a97..e7cb823954b 100644 --- a/test/integration/component/test_vpc_network_lbrules.py +++ b/test/integration/component/test_vpc_network_lbrules.py @@ -619,6 +619,36 @@ class TestVPCNetworkLBRules(cloudstackTestCase): self.check_wget_from_vm(vm_1, public_ip_1, testnegative=False) return + @attr(tags=["advanced","advancedns", "intervlan"]) + def test_04_VPC_CreateLBRuleInMultipleNetworksVRStoppedState(self): + """ Test case no 222 : Create LB rules for a two/multiple virtual networks of a + VPC using a new Public IP Address available with the VPC when the Virtual Router is in Stopped State + """ + + # Validate the following + # 1. Create a VPC with cidr - 10.1.1.1/16 + # 2. Create a Network offering - NO1 with all supported services + # 3. Add network1(10.1.1.1/24) using N01 to this VPC. + # 4. Add network2(10.1.2.1/24) using N01 to this VPC. + # 5. Deploy vm1, vm2 and vm3 in network1 on primary host. + # 7. Use the Create LB rule for vm1 and vm2 in network1. + # 8. Add vm3 to LB rule. + # 9. wget a file and check for LB rule. + + network_1 = self.create_Network(self.services["network_offering"]) + network_2 = self.create_Network(self.services["network_offering_no_lb"], '10.1.2.1') + vm_1 = self.create_VM_in_Network(network_1) + vm_2 = self.create_VM_in_Network(network_1) + vm_3 = self.create_VM_in_Network(network_2) + public_ip_1 = self.acquire_Public_IP(network_1) + lb_rule = self.create_LB_Rule(public_ip_1, network_1, [vm_1, vm_2], self.services["lbrule_http"]) + # In a VPC, the load balancing service is supported only on a single tier. + # http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.0.2/html/Installation_Guide/configure-vpc.html + with self.assertRaises(Exception): + lb_rule.assign(self.apiclient, [vm_3]) + self.check_wget_from_vm(vm_1, public_ip_1, testnegative=False) + return + @attr(tags=["advanced", "intervlan"]) def test_05_VPC_CreateAndDeleteLBRule(self): """ Test case no 214 : Delete few(not all) LB rules for a single virtual network of a From f4066b51fbbeed3101e017050535f2796a3f0667 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 18 Sep 2013 11:39:37 -0700 Subject: [PATCH 205/251] CLOUDSTACK-4700: UI > Instances > Reset VM action > if the template from which vm is created is password-enabled, pop up "Password hsa been reset to xxxxxxx" dialog. --- ui/scripts/instances.js | 32 ++++++++++++++++++----------- ui/scripts/ui/widgets/detailView.js | 8 +++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 20d3d9a606d..cf8aca5b485 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -605,6 +605,12 @@ }, notification: function(args) { return 'Reset VM'; + }, + complete: function(args) { + if (args.password != null && args.password.length > 0) + return 'Password has been reset to ' + args.password; + else + return null; } }, @@ -613,24 +619,26 @@ url: createURL("restoreVirtualMachine&virtualmachineid=" + args.context.instances[0].id), dataType: "json", async: true, - success: function(json) { - var item = json.restorevmresponse; - args.response.success({ - data: item - }); + success: function(json) { + var jid = json.restorevmresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + }); } }); }, notification: { - poll: function(args) { - args.complete({ - data: { - state: 'Stopped' - } - }); - } + poll: pollAsyncJobResult } }, diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index 3a59d4104f9..0e975e41706 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -223,9 +223,11 @@ } if (messages.complete) { - cloudStack.dialog.notice({ - message: messages.complete(args2.data) - }); + if( messages.complete(args2.data) != null && messages.complete(args2.data).length > 0) { + cloudStack.dialog.notice({ + message: messages.complete(args2.data) + }); + } } if (additional && additional.complete) additional.complete($.extend(true, args, { $detailView: $detailView From 7f08333cc66f5e13bf66d0c4a086c56c69caf858 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 18 Sep 2013 16:43:18 -0700 Subject: [PATCH 206/251] CLOUDSTACK-4702: UI > Network menu > Add Guest Network dialog > UI shouldn't pass null zoneid to listnetworkofferings API when zone dropdown is empty (i.e. when no advanced zone exists). --- ui/scripts/sharedFunctions.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 16ca41fd572..5369cee6678 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -450,7 +450,14 @@ var addGuestNetworkDialog = { label: 'label.network.offering', docID: 'helpGuestNetworkZoneNetworkOffering', dependsOn: ['zoneId', 'scope'], - select: function(args) { + select: function(args) { + if(args.$form.find('.form-item[rel=zoneId]').find('select').val() == null || args.$form.find('.form-item[rel=zoneId]').find('select').val().length == 0) { + args.response.success({ + data: null + }); + return; + } + var data = { state: 'Enabled', zoneid: args.$form.find('.form-item[rel=zoneId]').find('select').val() From 78b912e38f550d93f1cc008236205b4df300e26f Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 19 Sep 2013 10:52:28 +0200 Subject: [PATCH 207/251] CLOUDSTACK-1749: change cloud service name in systemvm --- .../systemvm/debian/config/etc/init.d/cloud | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/patches/systemvm/debian/config/etc/init.d/cloud b/patches/systemvm/debian/config/etc/init.d/cloud index b8e6ed2bf45..83853bcd4ef 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud +++ b/patches/systemvm/debian/config/etc/init.d/cloud @@ -5,9 +5,9 @@ # Required-Stop: $local_fs # Should-Start: # Should-Stop: -# Default-Start: 2 3 4 5 +# Default-Start: # Default-Stop: 0 1 6 -# Short-Description: Start up the cloud.com service +# Short-Description: Start up the CloudStack cloud service ### END INIT INFO # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -74,7 +74,7 @@ _failure() { fi } RETVAL=$? -CLOUD_COM_HOME="/usr/local/cloud" +CLOUDSTACK_HOME="/usr/local/cloud" # mkdir -p /var/log/vmops @@ -82,23 +82,23 @@ get_pids() { local i for i in $(ps -ef| grep java | grep -v grep | awk '{print $2}'); do - echo $(pwdx $i) | grep "$CLOUD_COM_HOME" | awk -F: '{print $1}'; + echo $(pwdx $i) | grep "$CLOUDSTACK_HOME" | awk -F: '{print $1}'; done } start() { local pid=$(get_pids) if [ "$pid" != "" ]; then - echo "cloud.com sevice is already running, PID = $pid" + echo "CloudStack cloud sevice is already running, PID = $pid" return 0 fi - echo -n "Starting cloud.com service (type=$TYPE) " - if [ -f $CLOUD_COM_HOME/systemvm/run.sh ]; + echo -n "Starting CloudStack cloud service (type=$TYPE) " + if [ -f $CLOUDSTACK_HOME/systemvm/run.sh ]; then if [ "$pid" == "" ] then - (cd $CLOUD_COM_HOME/systemvm; nohup ./run.sh > /var/log/cloud/cloud.out 2>&1 & ) + (cd $CLOUDSTACK_HOME/systemvm; nohup ./run.sh > /var/log/cloud/cloud.out 2>&1 & ) pid=$(get_pids) echo $pid > /var/run/cloud.pid fi @@ -107,29 +107,29 @@ start() { _failure fi echo - echo 'start' > $CLOUD_COM_HOME/systemvm/user_request + echo 'start' > $CLOUDSTACK_HOME/systemvm/user_request } stop() { local pid - echo -n "Stopping cloud.com service (type=$TYPE): " + echo -n "Stopping CloudStack cloud service (type=$TYPE): " for pid in $(get_pids) do kill $pid done _success echo - echo 'stop' > $CLOUD_COM_HOME/systemvm/user_request + echo 'stop' > $CLOUDSTACK_HOME/systemvm/user_request } status() { local pids=$(get_pids) if [ "$pids" == "" ] then - echo "cloud.com service is not running" + echo "CloudStack cloud service is not running" return 1 fi - echo "cloud.com service (type=$TYPE) is running: process id: $pids" + echo "CloudStack cloud service (type=$TYPE) is running: process id: $pids" return 0 } From 25cf6c9bed7244d4205d0d0026cc0f36a1c2c5bb Mon Sep 17 00:00:00 2001 From: Girish Shilamkar Date: Thu, 19 Sep 2013 02:28:07 -0400 Subject: [PATCH 208/251] Storage type was set to local due to which tests failed. Removed it. Signed-off-by: Prasanna Santhanam (cherry picked from commit a726bfdba5bc0bdf72bea151c28f40d75f566a85) --- test/integration/component/test_reset_ssh_keypair.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/component/test_reset_ssh_keypair.py b/test/integration/component/test_reset_ssh_keypair.py index 8b499d017af..ace449999f8 100644 --- a/test/integration/component/test_reset_ssh_keypair.py +++ b/test/integration/component/test_reset_ssh_keypair.py @@ -59,7 +59,6 @@ class Services: "cpunumber": 1, "cpuspeed": 100, "memory": 128, - "storagetype": "local" }, "egress": { "name": 'web', From e81e6ef5eb7477c3dae44f16fa9da1cf571d4c99 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 19 Sep 2013 10:19:19 -0700 Subject: [PATCH 209/251] CLOUDSTACK-4709: UI > template > register template > when zone dropdown is selected as All Zones, show all hypervisors supported in cloudstack instead of only hypervisors available in all zones. --- ui/scripts/templates.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index 3f79b09cf3b..dc1a39b545f 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -181,10 +181,13 @@ return; var apiCmd; - if (args.zone == -1) - apiCmd = "listHypervisors&zoneid=-1"; - else + if (args.zone == -1) { //All Zones + //apiCmd = "listHypervisors&zoneid=-1"; //"listHypervisors&zoneid=-1" has been changed to return only hypervisors available in all zones (bug 8809) + apiCmd = "listHypervisors"; + } + else { apiCmd = "listHypervisors&zoneid=" + args.zone; + } $.ajax({ url: createURL(apiCmd), From a14145bb5e063e4545789635943bf28712b8fa83 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 19 Sep 2013 10:26:59 -0700 Subject: [PATCH 210/251] Fixed sysvmadm helper script (responsible for restarting/recreating VRs when needed on upgraded setups due to template changes) to have -v option. When -v is specified, all VPCs in the system will get restarted. As a part of the restart, VPC routers will get recreated --- setup/bindir/cloud-sysvmadm.in | 102 ++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/setup/bindir/cloud-sysvmadm.in b/setup/bindir/cloud-sysvmadm.in index 3cb7858150b..e2a626ef922 100755 --- a/setup/bindir/cloud-sysvmadm.in +++ b/setup/bindir/cloud-sysvmadm.in @@ -23,13 +23,14 @@ #set -x usage() { - printf "\nThe tool stopping/starting running system vms and domain routers \n\nUsage: %s: [-d] [-u] [-p] [-m] [-s] [-r] [-a] [-t] [-n] [-z]\n\n -d - cloud DB server ip address, defaulted to localhost if not specified \n -u - user name to access cloud DB, defaulted to "root" if not specified \n -p - cloud DB user password, defaulted to no password if not specified \n\n -m - the ip address of management server, defaulted to localhost if not specified\n\n -s - stop then start all running SSVMs and Console Proxies \n -r - stop then start all running Virtual Routers\n -a - stop then start all running SSVMs, Console Proxies, and Virtual Routers \n -n - restart all Guest networks \n -t - number of parallel threads used for stopping Domain Routers. Default is 10.\n -l - log file location. Default is cloud.log under current directory.\n -z - do restart only for the instances in the specific zone. If not specified, restart will apply to instances in all zones\n\n" $(basename $0) >&2 + printf "\nThe tool stopping/starting running system vms and domain routers \n\nUsage: %s: [-d] [-u] [-p] [-m] [-s] [-r] [-a] [-t] [-n] [-z] [-v]\n\n -d - cloud DB server ip address, defaulted to localhost if not specified \n -u - user name to access cloud DB, defaulted to "root" if not specified \n -p - cloud DB user password, defaulted to no password if not specified \n\n -m - the ip address of management server, defaulted to localhost if not specified\n\n -s - stop then start all running SSVMs and Console Proxies \n -r - stop then start all running Virtual Routers\n -a - stop then start all running SSVMs, Console Proxies, and Virtual Routers \n -n - restart all Guest networks \n -t - number of parallel threads used for stopping Domain Routers. Default is 10.\n -l - log file location. Default is cloud.log under current directory.\n -z - do restart only for the instances in the specific zone. If not specified, restart will apply to instances in all zones\n -v - do restart all VPCs in the entire system\n\n" $(basename $0) >&2 } system= router= all= +vpc= db=localhost ms=localhost user=root @@ -42,7 +43,7 @@ inzone="" -while getopts 'sarhnd:m:u:p:t:l:z:' OPTION +while getopts 'sarhnvd:m:u:p:t:l:z:' OPTION do case $OPTION in s) system=1 @@ -53,6 +54,8 @@ do ;; a) all=1 ;; + v) vpc=1 + ;; d) db="$OPTARG" ;; u) user="$OPTARG" @@ -317,6 +320,92 @@ restart_network(){ } + +restart_vpc(){ + echo -e "INFO: Restarting vpc with id $1" + echo "INFO: Restarting vpc with id $1" >>$LOGFILE + jobid=`curl -sS "http://$ms:8096/?command=restartVPC&id=$1&response=json" | sed 's/\"//g' | sed 's/ //g' | sed 's/{//g' | sed 's/}//g' | awk -F: {'print $3'}` + if [ "$jobid" == "" ]; then + echo "ERROR: Failed to restart vpc with id $1" >>$LOGFILE + echo 2 + return + fi + + jobresult=$(query_async_job_result $jobid) + + if [ "$jobresult" != "1" ]; then + echo -e "ERROR: Failed to restart vpc with id $1 \n" + echo "ERROR: Failed to restart vpc with id $1" >>$LOGFILE + else + echo -e "INFO: Successfully restarted vpc with id $1 \n" + echo "INFO: Successfully restarted vpc with id $1" >>$LOGFILE + fi +} + + +restart_vpcs(){ + vpcs=(`mysql -h $db --user=$user --password=$password --skip-column-names -U cloud -e "select id from vpc WHERE removed is null$zone"`) + length_vpcs=(${#vpcs[@]}) + + echo -e "\nRestarting $length_vpcs vpcs... " + echo -e "Restarting $length_vpcs vpcs... " >>$LOGFILE + + #Spawn restart vpcs in parallel - run commands in chunks - number of threads is configurable + + pids=() + for d in "${vpcs[@]}"; do + + restart_vpc $d & + + pids=( "${pids[@]}" $! ) + + length_pids=(${#pids[@]}) + unfinishedPids=(${#pids[@]}) + + if [ $maxthreads -gt $length_vpcs ]; then + maxthreads=$length_vpcs + fi + + if [ $length_pids -ge $maxthreads ]; then + while [ $unfinishedPids -gt 0 ]; do + sleep 10 + count=0 + for (( i = 0 ; i < $length_pids; i++ )); do + if ! ps ax | grep -v grep | grep ${pids[$i]} > /dev/null; then + count=`expr $count + 1` + fi + done + + if [ $count -eq $unfinishedPids ]; then + unfinishedPids=0 + fi + + done + + #remove all elements from pids + if [ $unfinishedPids -eq 0 ]; then + pids=() + length_pids=(${#pids[@]}) + fi + + fi + + done + + + if [ "$length_vpcs" == "0" ];then + echo -e "No vpcs found \n" >>$LOGFILE + else + while [ $unfinishedPids -gt 0 ]; do + sleep 10 + done + + echo -e "Done restarting vpcs$inzone. \n" + echo -e "Done restarting vpcs$inzone. \n" >>$LOGFILE + + fi +} + query_async_job_result() { while [ 1 ] do @@ -329,7 +418,7 @@ sleep 5 done } -if [ "$system$router$all$help$redundant" == "" ] +if [ "$system$router$all$help$redundant$vpc" == "" ] then usage exit @@ -361,3 +450,10 @@ if [ "$redundant" == "1" ] then restart_networks fi + +if [ "$vpc" == "1" ] +then + restart_vpcs +fi + + From cea14ce8802f1f61522491f2581abbd776436ccf Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 19 Sep 2013 10:57:34 -0700 Subject: [PATCH 211/251] CLOUDSTACK-4573: fixed resource limit check when acquire public IP in VPC - update resource count for VPC public ip even when network_id is not set yet. --- server/src/com/cloud/network/NetworkManagerImpl.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 2c00c5bebd6..7042e182284 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -551,7 +551,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L addr.getSystem(), addr.getClass().getName(), addr.getUuid()); } // don't increment resource count for direct and dedicated ip addresses - if (addr.getAssociatedWithNetworkId() != null && !isIpDedicated(addr)) { + if (updateIpResourceCount(addr)) { _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.public_ip); } } @@ -3783,7 +3783,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L txn.start(); // don't decrement resource count for direct and dedicated ips - if (ip.getAssociatedWithNetworkId() != null && !isIpDedicated(ip)) { + if (updateIpResourceCount(ip)) { _resourceLimitMgr.decrementResourceCount(_ipAddressDao.findById(addrId).getAllocatedToAccountId(), ResourceType.public_ip); } @@ -3807,7 +3807,10 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return ip; } - + + protected boolean updateIpResourceCount(IPAddressVO ip) { + return (ip.getAssociatedWithNetworkId() != null || ip.getVpcId() != null) && !isIpDedicated(ip); + } Random _rand = new Random(System.currentTimeMillis()); From 798f34a49cbfbb3a475f3dadf62d1ac1b8dfa510 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 19 Sep 2013 13:08:38 -0700 Subject: [PATCH 212/251] CLOUDSTACK-4704: 41-42 db upgrade - populate vpc_service_map table with the services/providers supported by VPC --- .../src/com/cloud/network/vpc/dao/VpcDao.java | 4 +- .../com/cloud/network/vpc/dao/VpcDaoImpl.java | 10 ++-- .../cloud/upgrade/dao/Upgrade410to420.java | 56 +++++++++++++++++++ .../com/cloud/network/vpc/VpcManagerImpl.java | 25 ++++----- .../com/cloud/vpc/dao/MockVpcDaoImpl.java | 4 +- setup/db/db/schema-410to420.sql | 2 +- 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcDao.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcDao.java index 5a33217c028..57a26214028 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcDao.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcDao.java @@ -40,8 +40,8 @@ public interface VpcDao extends GenericDao{ long countByAccountId(long accountId); - VpcVO persist(VpcVO vpc, Map serviceProviderMap); + VpcVO persist(VpcVO vpc, Map> serviceProviderMap); void persistVpcServiceProviders(long vpcId, - Map serviceProviderMap); + Map> serviceProviderMap); } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java index 6560b90ce7d..12868fef8ba 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java @@ -128,7 +128,7 @@ public class VpcDaoImpl extends GenericDaoBase implements VpcDao{ @Override @DB - public VpcVO persist(VpcVO vpc, Map serviceProviderMap) { + public VpcVO persist(VpcVO vpc, Map> serviceProviderMap) { Transaction txn = Transaction.currentTxn(); txn.start(); VpcVO newVpc = super.persist(vpc); @@ -139,12 +139,14 @@ public class VpcDaoImpl extends GenericDaoBase implements VpcDao{ @Override @DB - public void persistVpcServiceProviders(long vpcId, Map serviceProviderMap) { + public void persistVpcServiceProviders(long vpcId, Map> serviceProviderMap) { Transaction txn = Transaction.currentTxn(); txn.start(); for (String service : serviceProviderMap.keySet()) { - VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service), Network.Provider.getProvider(serviceProviderMap.get(service))); - _vpcSvcMap.persist(serviceMap); + for (String provider : serviceProviderMap.get(service)) { + VpcServiceMapVO serviceMap = new VpcServiceMapVO(vpcId, Network.Service.getService(service), Network.Provider.getProvider(provider)); + _vpcSvcMap.persist(serviceMap); + } } txn.commit(); } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 43f03c057f9..646b4062a48 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -114,6 +114,7 @@ public class Upgrade410to420 implements DbUpgrade { setRAWformatForRBDVolumes(conn); migrateVolumeOnSecondaryStorage(conn); createFullCloneFlag(conn); + upgradeVpcServiceMap(conn); } private void createFullCloneFlag(Connection conn) { @@ -2956,4 +2957,59 @@ public class Upgrade410to420 implements DbUpgrade { throw new CloudRuntimeException("Failed to update volume format to RAW for volumes on RBD pools due to exception ", e); } } + + + private void upgradeVpcServiceMap(Connection conn){ + s_logger.debug("Upgrading VPC service Map"); + PreparedStatement listVpc = null; + PreparedStatement listServiceProviders = null; + PreparedStatement insertProviders = null; + ResultSet rs = null; + ResultSet rs1 = null; + try { + //Get all vpc Ids along with vpc offering Id + listVpc = conn.prepareStatement("SELECT id, vpc_offering_id FROM `cloud`.`vpc` where removed is NULL"); + rs = listVpc.executeQuery(); + while (rs.next()) { + long vpc_id = rs.getLong(1); + long offering_id = rs.getLong(2); + //list all services and providers in offering + listServiceProviders = conn.prepareStatement("SELECT service, provider FROM `cloud`.`vpc_offering_service_map` where vpc_offering_id = ?"); + listServiceProviders.setLong(1, offering_id); + rs1 = listServiceProviders.executeQuery(); + //Insert entries in vpc_service_map + while (rs1.next()) { + String service = rs1.getString(1); + String provider = rs1.getString(2); + insertProviders = conn.prepareStatement("INSERT INTO `cloud`.`vpc_service_map` (`vpc_id`, `service`, `provider`, `created`) VALUES (?, ?, ?, now());"); + insertProviders.setLong(1, vpc_id); + insertProviders.setString(2, service); + insertProviders.setString(3, provider); + insertProviders.executeUpdate(); + } + s_logger.debug("Upgraded service map for VPC: "+vpc_id); + } + }catch (SQLException e) { + throw new CloudRuntimeException("Error during VPC service map upgrade", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (rs1 != null) { + rs1.close(); + } + if (listVpc != null) { + listVpc.close(); + } + if (listServiceProviders != null) { + listServiceProviders.close(); + } + if (insertProviders != null) { + insertProviders.close(); + } + } catch (SQLException e) { + } + } + } } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index f81dee93836..8ee249fda27 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -656,18 +656,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return vpc; } - private Map finalizeServicesAndProvidersForVpc(long zoneId, long offeringId) { - Map svcProviders = new HashMap(); - Map> providerSvcs = new HashMap>(); + private Map> finalizeServicesAndProvidersForVpc(long zoneId, long offeringId) { + Map> svcProviders = new HashMap>(); List servicesMap = _vpcOffSvcMapDao.listByVpcOffId(offeringId); for (VpcOfferingServiceMapVO serviceMap : servicesMap) { - if (svcProviders.containsKey(serviceMap.getService())) { - // FIXME - right now we pick up the first provider from the list, need to add more logic based on - // provider load, etc - continue; - } - String service = serviceMap.getService(); String provider = serviceMap.getProvider(); @@ -681,13 +674,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw new InvalidParameterValueException("Provider " + provider + " should be enabled in at least one physical network of the zone specified"); } - - svcProviders.put(service, provider); - List l = providerSvcs.get(provider); - if (l == null) { - providerSvcs.put(provider, l = new ArrayList()); + + List providers = null; + if (svcProviders.get(service) == null) { + providers = new ArrayList(); + } else { + providers = svcProviders.get(service); } - l.add(service); + providers.add(provider); + svcProviders.put(service, providers); } return svcProviders; diff --git a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java index 562d67dc207..5e1c2ecef56 100644 --- a/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java +++ b/server/test/com/cloud/vpc/dao/MockVpcDaoImpl.java @@ -86,12 +86,12 @@ public class MockVpcDaoImpl extends GenericDaoBase implements VpcDa } @Override - public VpcVO persist(VpcVO vpc, Map serviceProviderMap) { + public VpcVO persist(VpcVO vpc, Map> serviceProviderMap) { return null; } @Override - public void persistVpcServiceProviders(long vpcId, Map serviceProviderMap) { + public void persistVpcServiceProviders(long vpcId, Map> serviceProviderMap) { return; } diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index f7f10b97ef7..197f3754b63 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -270,7 +270,7 @@ CREATE TABLE `vpc_service_map` ( `created` datetime COMMENT 'date created', PRIMARY KEY (`id`), CONSTRAINT `fk_vpc_service_map__vpc_id` FOREIGN KEY(`vpc_id`) REFERENCES `vpc`(`id`) ON DELETE CASCADE, - UNIQUE (`vpc_id`, `service`) + UNIQUE (`vpc_id`, `service`, `provider`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`load_balancer_healthcheck_policies` ( From fa297948f0b74edf8e5fc4cdeb1789d381e1d708 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Thu, 19 Sep 2013 13:36:58 -0700 Subject: [PATCH 213/251] CLOUDSTACK-4710: Fix broken help link --- ui/scripts/ui/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/scripts/ui/core.js b/ui/scripts/ui/core.js index c23484b8ba3..a11d14e1aff 100644 --- a/ui/scripts/ui/core.js +++ b/ui/scripts/ui/core.js @@ -302,8 +302,8 @@ .appendTo($options); if (this == 'label.help') { - $link.click(function() { - var helpURL = 'http://docs.cloud.com/CloudStack_Documentation'; + $link.addClass('help').click(function() { + var helpURL = 'http://cloudstack.apache.org/'; window.open(helpURL, '_blank'); @@ -311,7 +311,7 @@ }); } if (this == 'label.about') { - $link.click(function() { + $link.addClass('about').click(function() { var $logo = $('
    ').addClass('logo').html(_l('label.app.name')), $version = $('
    ').addClass('version').html(g_cloudstackversion), $about = $('
    ').addClass('about').append($logo).append($version); From e81a2b4ff29553f458ddabcad7e97236df2b52bc Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 19 Sep 2013 16:44:42 -0700 Subject: [PATCH 214/251] CLOUDSTACK-4713: UI > EIP/ELB Basic Zone - fix a bug that IPs that are acquired are not showing in listView of IP Addresses page under Network menu. --- ui/scripts/network.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index a09e56515aa..9d4cf3897a3 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -2057,6 +2057,8 @@ var data = {}; listViewDataProvider(args, data); + //The following 10 lines caused CLOUDSTACK-4713 (EIP/ELB Basic Zone - unable to see any IPs that are acquired) + /* if (g_supportELB == "guest") // IPs are allocated on guest network $.extend(data, { forvirtualnetwork: false, @@ -2067,6 +2069,7 @@ forvirtualnetwork: true, forloadbalancing: true }); + */ if (args.context.networks) { $.extend(data, { From 24e898b4cddf1a8ebc3b4886526d94921ade869b Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 19 Sep 2013 17:10:43 -0700 Subject: [PATCH 215/251] CLOUDSTACK-4714: EIP/ELB Basic Zone > EIP/ELB Basic Zone > Network page > Add Load Balancer tab > add AutoScale rule > spinning wheel is hanging forever with JS error "args.context.ipAddresses is undefined". --- ui/scripts/autoscaler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/autoscaler.js b/ui/scripts/autoscaler.js index 6fabf6810c7..ab1b459e98c 100644 --- a/ui/scripts/autoscaler.js +++ b/ui/scripts/autoscaler.js @@ -1116,7 +1116,7 @@ var apiCmd, apiCmdRes; if (!('multiRules' in args.context)) { //from a new LB var data = { - zoneid: args.context.ipAddresses[0].zoneid, //args.context.networks[0] doesn't have zoneid property, so use args.context.ipAddresses[0] instead + zoneid: args.context.networks[0].zoneid, //get zoneid from args.context.networks[0] instead of args.context.ipAddresses[0] because args.context.ipAddresses is null when adding AutoScale rule from Add Load Balancer tab in Network page serviceofferingid: args.data.serviceOfferingId, templateid: args.data.templateNames, destroyvmgraceperiod: args.data.destroyVMgracePeriod, From 0bdbb9e72f8590f7cd5384a9ed227e99374f90f8 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 19 Sep 2013 18:34:42 -0700 Subject: [PATCH 216/251] CLOUDSTACK-4713: EIP/ELB Basic Zone: UI > Network > IP Addresses > make extra API call to get IPs allocated at guest network. --- ui/scripts/network.js | 78 +++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 9d4cf3897a3..421582afab7 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -2054,57 +2054,63 @@ }, dataProvider: function(args) { - var data = {}; - listViewDataProvider(args, data); - - //The following 10 lines caused CLOUDSTACK-4713 (EIP/ELB Basic Zone - unable to see any IPs that are acquired) - /* - if (g_supportELB == "guest") // IPs are allocated on guest network - $.extend(data, { - forvirtualnetwork: false, - forloadbalancing: true - }); - else if (g_supportELB == "public") // IPs are allocated on public network - $.extend(data, { - forvirtualnetwork: true, - forloadbalancing: true - }); - */ - + var items = []; + var data = {}; + listViewDataProvider(args, data); if (args.context.networks) { $.extend(data, { associatedNetworkId: args.context.networks[0].id }); - } - if ("vpc" in args.context) { $.extend(data, { vpcid: args.context.vpc[0].id }); - } - + } + $.ajax({ url: createURL('listPublicIpAddresses'), - data: data, + data: $.extend({}, data, { + forvirtualnetwork: true, //IPs are allocated on public network + }), dataType: "json", - async: true, + async: false, success: function(json) { - var items = json.listpublicipaddressesresponse.publicipaddress; - - $(items).each(function() { - getExtaPropertiesForIpObj(this, args); - }); - - args.response.success({ - actionFilter: actionFilters.ipAddress, - data: items - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); + var ips = json.listpublicipaddressesresponse.publicipaddress; + if(ips != null) { + for(var i = 0; i < ips.length; i++) { + getExtaPropertiesForIpObj(ips[i], args); + items.push(ips[i]); + } + } } }); + + if (g_supportELB == "guest") { + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: $.extend({}, data, { + forvirtualnetwork: false, // ELB IPs are allocated on guest network + forloadbalancing: true + }), + dataType: "json", + async: false, + success: function(json) { + var ips = json.listpublicipaddressesresponse.publicipaddress; + if(ips != null) { + for(var i = 0; i < ips.length; i++) { + getExtaPropertiesForIpObj(ips[i], args); + items.push(ips[i]); + } + } + } + }); + } + + args.response.success({ + actionFilter: actionFilters.ipAddress, + data: items + }); }, // Detail view From 8e0faaa406651dcd0025b563ed36acbdb733a0d0 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 19 Sep 2013 22:19:22 -0700 Subject: [PATCH 217/251] Disable VMwareTools detection to avoid unreliable result caused by race-condition when VMwareTools is not ready in guest OS yet and the API is called --- client/pom.xml | 23 ++++--------------- .../vmware/resource/VmwareResource.java | 17 ++++++++++++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index d25576a8089..119c96eddef 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -16,8 +16,8 @@ org.apache.cloudstack cloudstack - 4.2.0-SNAPSHOT - + 4.3.0-SNAPSHOT + org.apache.cloudstack @@ -173,7 +173,6 @@ mysql mysql-connector-java - ${cs.mysql.version} runtime @@ -196,13 +195,6 @@ cloud-engine-components-api ${project.version} - - - org.apache.cloudstack - cloud-engine-compute - ${project.version} - - org.apache.cloudstack cloud-engine-network @@ -290,7 +282,6 @@ - install ru.concerteza.buildnumber @@ -338,8 +329,8 @@ 60000 - ${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml - ${project.build.directory}/${project.build.finalName} + ${project.build.directory}/generated-webapp/WEB-INF/web.xml + ${project.build.directory}/generated-webapp /client ${project.build.directory}/utilities/scripts/db/;${project.build.directory}/utilities/scripts/db/db/ @@ -391,10 +382,7 @@ - - - + @@ -561,7 +549,6 @@ org.apache.maven.plugins maven-dependency-plugin - 2.5.1 copy diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 0e9ce93a8ab..b80fd40de05 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1659,12 +1659,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } +/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools is not installed or not running, cannot add nic to vm " + vmName; s_logger.debug(errMsg); return new PlugNicAnswer(cmd, false, "Unable to execute PlugNicCommand due to " + errMsg); } - +*/ // TODO need a way to specify the control of NIC device type VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.E1000; @@ -1739,12 +1740,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } +/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools not installed or not running, cannot remove nic from vm " + vmName; s_logger.debug(errMsg); return new UnPlugNicAnswer(cmd, false, "Unable to execute unPlugNicCommand due to " + errMsg); } - +*/ VirtualDevice nic = findVirtualNicDevice(vmMo, cmd.getNic().getMac()); if ( nic == null ) { return new UnPlugNicAnswer(cmd, true, "success"); @@ -6869,6 +6871,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa private boolean isVMWareToolsInstalled(VirtualMachineMO vmMo) throws Exception{ GuestInfo guestInfo = vmMo.getVmGuestInfo(); + if(guestInfo == null) { + s_logger.error("null GuestInfo is returned"); + return false; + } + + if(guestInfo.getGuestState() == null) { + s_logger.error("null GuestState is returned"); + return false; + } + + s_logger.info("VMwareTools guest state: " + guestInfo.getGuestState()); return (guestInfo != null && guestInfo.getGuestState() != null && guestInfo.getGuestState().equalsIgnoreCase("running")); } } From ebc95375e23a65d338171d21cf06c31db296d029 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 19 Sep 2013 23:05:17 -0700 Subject: [PATCH 218/251] Revert "Disable VMwareTools detection to avoid unreliable result caused by race-condition when VMwareTools is not ready in guest OS yet and the API is called" This reverts commit 8e0faaa406651dcd0025b563ed36acbdb733a0d0. --- client/pom.xml | 23 +++++++++++++++---- .../vmware/resource/VmwareResource.java | 17 ++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 119c96eddef..d25576a8089 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -16,8 +16,8 @@ org.apache.cloudstack cloudstack - 4.3.0-SNAPSHOT - + 4.2.0-SNAPSHOT + org.apache.cloudstack @@ -173,6 +173,7 @@ mysql mysql-connector-java + ${cs.mysql.version} runtime @@ -195,6 +196,13 @@ cloud-engine-components-api ${project.version} + + + org.apache.cloudstack + cloud-engine-compute + ${project.version} + + org.apache.cloudstack cloud-engine-network @@ -282,6 +290,7 @@ + install ru.concerteza.buildnumber @@ -329,8 +338,8 @@ 60000 - ${project.build.directory}/generated-webapp/WEB-INF/web.xml - ${project.build.directory}/generated-webapp + ${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml + ${project.build.directory}/${project.build.finalName} /client ${project.build.directory}/utilities/scripts/db/;${project.build.directory}/utilities/scripts/db/db/ @@ -382,7 +391,10 @@ - + + + @@ -549,6 +561,7 @@ org.apache.maven.plugins maven-dependency-plugin + 2.5.1 copy diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index b80fd40de05..0e9ce93a8ab 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1659,13 +1659,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } -/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools is not installed or not running, cannot add nic to vm " + vmName; s_logger.debug(errMsg); return new PlugNicAnswer(cmd, false, "Unable to execute PlugNicCommand due to " + errMsg); } -*/ + // TODO need a way to specify the control of NIC device type VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.E1000; @@ -1740,13 +1739,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } -/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools not installed or not running, cannot remove nic from vm " + vmName; s_logger.debug(errMsg); return new UnPlugNicAnswer(cmd, false, "Unable to execute unPlugNicCommand due to " + errMsg); } -*/ + VirtualDevice nic = findVirtualNicDevice(vmMo, cmd.getNic().getMac()); if ( nic == null ) { return new UnPlugNicAnswer(cmd, true, "success"); @@ -6871,17 +6869,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa private boolean isVMWareToolsInstalled(VirtualMachineMO vmMo) throws Exception{ GuestInfo guestInfo = vmMo.getVmGuestInfo(); - if(guestInfo == null) { - s_logger.error("null GuestInfo is returned"); - return false; - } - - if(guestInfo.getGuestState() == null) { - s_logger.error("null GuestState is returned"); - return false; - } - - s_logger.info("VMwareTools guest state: " + guestInfo.getGuestState()); return (guestInfo != null && guestInfo.getGuestState() != null && guestInfo.getGuestState().equalsIgnoreCase("running")); } } From 16a2442c41d87206e9511b842a87475eba0419f6 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 19 Sep 2013 23:18:26 -0700 Subject: [PATCH 219/251] Disable VMwareTools detection to avoid unreliable result caused by race-condition when VMwareTools is not ready in guest OS yet and the API is called. This is the corrected commit --- .../cloud/hypervisor/vmware/resource/VmwareResource.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 0e9ce93a8ab..70b9f545b3d 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1659,12 +1659,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } +/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools is not installed or not running, cannot add nic to vm " + vmName; s_logger.debug(errMsg); return new PlugNicAnswer(cmd, false, "Unable to execute PlugNicCommand due to " + errMsg); } - +*/ // TODO need a way to specify the control of NIC device type VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.E1000; @@ -1739,12 +1740,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa throw new Exception(msg); } +/* if(!isVMWareToolsInstalled(vmMo)){ String errMsg = "vmware tools not installed or not running, cannot remove nic from vm " + vmName; s_logger.debug(errMsg); return new UnPlugNicAnswer(cmd, false, "Unable to execute unPlugNicCommand due to " + errMsg); } - +*/ VirtualDevice nic = findVirtualNicDevice(vmMo, cmd.getNic().getMac()); if ( nic == null ) { return new UnPlugNicAnswer(cmd, true, "success"); From fd49d252763b13deabb79433a5a02dd21208db8c Mon Sep 17 00:00:00 2001 From: radhikap Date: Fri, 20 Sep 2013 15:21:52 +0530 Subject: [PATCH 220/251] https://issues.apache.org/jira/browse/CLOUDSTACK-4245 --- docs/en-US/Release_Notes.xml | 8 ++++---- docs/en-US/zone-add.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index d1def441685..a0b17c41a88 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -1024,15 +1024,15 @@ under the License. >Jira to track its issues. All new features and bugs for 4.2.0 have been tracked in Jira, and have a standard naming convention of "CLOUDSTACK-NNNN" where "NNNN" is the issue number. - For list of issues fixed, see For the list of issues fixed, see Issues Fixed in 4.2.
    Known Issues in 4.2.0 - This section includes a summary of known issues that were fixed in 4.2.0. For list of - known issues, see Known - Issues. + This section includes a summary of known issues that were fixed in 4.2.0. For the list + of known issues, see Known Issues.
    diff --git a/docs/en-US/zone-add.xml b/docs/en-US/zone-add.xml index 841f7ee75ad..4137b671ee2 100644 --- a/docs/en-US/zone-add.xml +++ b/docs/en-US/zone-add.xml @@ -1,4 +1,4 @@ - + %BOOK_ENTITIES; From df52280409af9ae05eb007442277c40cc1109aca Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Fri, 20 Sep 2013 10:21:48 -0700 Subject: [PATCH 221/251] CS-18283: 2.2.x to 4.2 upgrade - corrected the Service Provider name for the Network offeirng that can be used in VPC (from VirtualRouter to VpcVirtualRouter) --- server/src/com/cloud/network/NetworkManagerImpl.java | 2 +- setup/db/db/schema-410to420.sql | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 7042e182284..df3dfeacbb4 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1354,7 +1354,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L Map> defaultVPCOffProviders = new HashMap>(); defaultProviders.clear(); - defaultProviders.add(Network.Provider.VirtualRouter); + defaultProviders.add(Network.Provider.VPCVirtualRouter); defaultVPCOffProviders.put(Service.Dhcp, defaultProviders); defaultVPCOffProviders.put(Service.Dns, defaultProviders); defaultVPCOffProviders.put(Service.UserData, defaultProviders); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 197f3754b63..d9560fd954e 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -2178,3 +2178,7 @@ CREATE VIEW `cloud`.`data_center_view` AS `cloud`.`dedicated_resources` ON data_center.id = dedicated_resources.data_center_id left join `cloud`.`affinity_group` ON dedicated_resources.affinity_group_id = affinity_group.id; + + + +UPDATE `cloud`.`ntwk_offering_service_map` SET Provider='VpcVirtualRouter' WHERE network_offering_id IN (SELECT id from `cloud`.`network_offerings` WHERE name IN ('DefaultIsolatedNetworkOfferingForVpcNetworks', 'DefaultIsolatedNetworkOfferingForVpcNetworksNoLB')); \ No newline at end of file From 92db2d1a441572e820e7e881167303d5b1ae4027 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 20 Sep 2013 10:59:06 -0700 Subject: [PATCH 222/251] CLOUDSTACK-4659: Vmware allows destroying VM that has pending tasks, worker VM GC actually relies on the assumption that Vmware is protected from that which is a false assumption. Recycle Worker VM only it is from previous session --- .../cloud/hypervisor/vmware/manager/VmwareManagerImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index a04a6eb92cd..02b4060f4a0 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -572,12 +572,16 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw s_logger.info("Worker VM's owner management server has changed runid, recycle it"); return true; } - + + // disable time-out check until we have found out a VMware API that can check if + // there are pending tasks on the subject VM +/* if(System.currentTimeMillis() - startTick > _hungWorkerTimeout) { if(s_logger.isInfoEnabled()) s_logger.info("Worker VM expired, seconds elapsed: " + (System.currentTimeMillis() - startTick) / 1000); return true; } +*/ return false; } From 9d00a6da654bcf12ed2ad67b23efc0032abe858c Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 23 Sep 2013 11:09:05 +0200 Subject: [PATCH 223/251] CLOUDSTACK-4726: complete secondary storage configuration in devcloud-advanced.cfg and devcloud-advancedsg.cfg --- tools/devcloud/devcloud-advanced.cfg | 4 +++- tools/devcloud/devcloud-advancedsg.cfg | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/devcloud/devcloud-advanced.cfg b/tools/devcloud/devcloud-advanced.cfg index 75c3a4f7147..fb25d03cf38 100644 --- a/tools/devcloud/devcloud-advanced.cfg +++ b/tools/devcloud/devcloud-advanced.cfg @@ -104,7 +104,9 @@ "internaldns1": "192.168.56.10", "secondaryStorages": [ { - "url": "nfs://192.168.56.10:/opt/storage/secondary" + "url": "nfs://192.168.56.10:/opt/storage/secondary", + "provider": "NFS", + "details": [ ] } ] } diff --git a/tools/devcloud/devcloud-advancedsg.cfg b/tools/devcloud/devcloud-advancedsg.cfg index 6c26b15f5da..c625e79c53f 100644 --- a/tools/devcloud/devcloud-advancedsg.cfg +++ b/tools/devcloud/devcloud-advancedsg.cfg @@ -88,7 +88,9 @@ "internaldns1": "192.168.56.10", "secondaryStorages": [ { - "url": "nfs://192.168.56.10/opt/storage/secondary" + "url": "nfs://192.168.56.10:/opt/storage/secondary", + "provider": "NFS", + "details": [ ] } ] } From 1628cfc663dfe2707c483865f7d7d95f3a53dfc2 Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Mon, 23 Sep 2013 16:16:05 +0530 Subject: [PATCH 224/251] CLOUDSTACK-4727 upgrade steps notes from abhinav has been incorporated --- docs/en-US/Release_Notes.xml | 355 ++++++++++++++++++++--------------- 1 file changed, 199 insertions(+), 156 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index a0b17c41a88..96e99281f15 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -217,8 +217,10 @@ under the License. -
    Support for KVMVPC is now supported on KVM - hypervisors.
    +
    + Support for KVM + VPC is now supported on KVM hypervisors. +
    Load Balancing Support for VPC In a VPC, you can configure two types of load balancing—external LB and @@ -1039,7 +1041,7 @@ under the License. Upgrade Instructions for 4.2 This section contains upgrade instructions from prior versions of CloudStack to Apache CloudStack 4.2.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 + versions of Citrix &PRODUCT; (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@cloudstack.apache.org or dev@cloudstack.apache.org. @@ -1080,11 +1082,11 @@ under the License. Stop your management server or servers. Run this on all management server hosts: - # service cloud-management stop + # service cloudstack-management stop If you are running a usage server or usage servers, stop those as well: - # service cloud-usage stop + # service cloudstack-usage stop Make a backup of your MySQL database. If you run into any issues or need to roll @@ -1130,7 +1132,7 @@ under the License. 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 @@ -1176,28 +1178,11 @@ under the License. Restart the agent: -service cloud-agent stop +service cloudstack-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 - @@ -1285,7 +1270,7 @@ service cloudstack-agent restart If you've created your own packages and yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -1310,31 +1295,25 @@ gpgcheck=0 Now that you have the repository configured, it's time to install the cloudstack-management package by upgrading the older - cloud-client package. - $ sudo yum upgrade cloud-client + cloudstack-management package. + $ sudo yum upgrade cloudstack-management For KVM hosts, you will need to upgrade the cloud-agent package, similarly installing the new version as cloudstack-agent. - $ sudo yum upgrade cloud-agent - During the installation of cloudstack-agent, the RPM will - copy your agent.properties, - log4j-cloud.xml, and - environment.properties from - /etc/cloud/agent to - /etc/cloudstack/agent. + $ sudo yum upgrade cloudstack-agent For CentOS 5.5, perform the following: - + Run the following command: rpm -Uvh http://download.cloud.com/support/jsvc/jakarta-commons-daemon-jsvc-1.0.1-8.9.el6.x86_64.rpm Upgrade the Usage server. - sudo yum upgrade cloud-usage + sudo yum upgrade cloudstack-usage @@ -1348,7 +1327,7 @@ gpgcheck=0 Restart the agent: -service cloud-agent stop +service cloudstack-agent stop killall jsvc service cloudstack-agent start @@ -1421,54 +1400,60 @@ Done restarting router(s). - Field - Value + Hypervisor + Description - Name - systemvm-vmware-4.2 + XenServer + Name: systemvm-xenserver-4.2.0 + Description: systemvm-xenserver-4.2.0 + URL:http://download.cloud.com/templates/4.2/systemvmtemplate-2013-07-12-master-xen.vhd.bz2 + Zone: Choose the zone where this hypervisor is used + Hypervisor: XenServer + Format: VHD + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) + Extractable: no + Password Enabled: no + Public: no + Featured: no + - Description - systemvm-vmware-4.2 + KVM + Name: systemvm-kvm-4.2.0 + Description: systemvm-kvm-4.2.0 + URL: + http://download.cloud.com/templates/4.2/systemvmtemplate-2013-06-12-master-kvm.qcow2.bz2 + Zone: Choose the zone where this hypervisor is used + Hypervisor: KVM + Format: QCOW2 + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) + Extractable: no + Password Enabled: no + Public: no + Featured: no + - 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 + Name: systemvm-vmware-4.2.0 + Description: systemvm-vmware-4.2.0 + URL: + http://download.cloud.com/templates/4.2/systemvmtemplate-4.2-vh7.ova + Zone: Choose the zone where this hypervisor is used + Hypervisor: VMware + Format: OVA + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) + Extractable: no + Password Enabled: no + Public: no + Featured: no + @@ -1480,7 +1465,47 @@ Done restarting router(s). - + + (KVM on RHEL 6.0/6.1 only) If your existing &PRODUCT; deployment includes one or + more clusters of KVM hosts running RHEL 6.0 or RHEL 6.1, perform the following: + + + Ensure that you upgrade the operating system version on those hosts before + upgrading &PRODUCT; + To do that, change the yum repository for each system with &PRODUCT; packages, + that implies that all the Management Servers and any hosts that have the KVM agent. + + + + Open /etc/yum.repos.d/cloudstack.repo on any systems that + have &PRODUCT; packages installed. + + + Edit as follows: + + [upgrade] + name=rhel63 + baseurl=url-of-your-rhel6.3-repo + enabled=1 + gpgcheck=0 + [apache CloudStack] + name= Apache CloudStack + baseurl= http://cloudstack.apt-get.eu/rhel/4.0/ + enabled=1 + gpgcheck=0 + If you are using the community provided package repository, change the baseurl + to http:// cloudstack.apt-get.eu/rhel/4.2/ + If you are using your own package repository, change this line to read as + appropriate for your 4.2.0 repository. + + + Now that you have the repository configured, upgrade the host operating system + from RHEL 6.0 to 6.3: + # yum upgrade + + + + Stop all Usage Servers if running. Run this on all Usage Server hosts. # service cloud-usage stop @@ -1582,32 +1607,6 @@ service cloudstack-agent restart - - (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. - - - Copy the CloudPlatform 4.2 tar file to the host, untar it, and change directory - to the resulting directory. - - - Stop the running agent. - # service cloud-agent stop - - - Update the agent software. - # ./install.sh - - - Choose "U" to update the packages. - - - Start the agent. - # service cloudstack-agent start - - - If you are using CentOS or RHEL, follow this procedure to upgrade your packages. If not, skip to step . @@ -1617,7 +1616,7 @@ service cloudstack-agent restart If you've created your own packages and yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -1766,11 +1765,26 @@ service cloudstack-agent start "com.cloud.hypervisor.kvm.resource.LibvirtComputingResource". - Start the cloud agent and cloud management services. + Upgrade all the existing bridge names to new bridge names by running this + script: + # cloudstack-agent-upgrade + + + Install a libvirt hook with the following commands: + # mkdir /etc/libvirt/hooks + # cp /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu + # chmod +x /etc/libvirt/hooks/qemu + + + Restart libvirtd. + # service libvirtd restart + + + Start the agent. # service cloudstack-agent start - When the Management Server is up and running, log in to the CloudStack UI and + When the Management Server is up and running, log in to the &PRODUCT; UI and restart the virtual router for proper functioning of all the features. @@ -1805,13 +1819,13 @@ service cloudstack-agent start - 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 + Run the cloudstack-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 > + # nohup cloudstack-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 @@ -2040,7 +2054,8 @@ service cloudstack-agent start Zone: Choose the zone where this hypervisor is used Hypervisor: XenServer Format: VHD - OS Type: Debian GNU/Linux 6.0 (32-bit) + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) Extractable: no Password Enabled: no Public: no @@ -2056,7 +2071,8 @@ service cloudstack-agent start Zone: Choose the zone where this hypervisor is used Hypervisor: KVM Format: QCOW2 - OS Type: Debian GNU/Linux 5.0 (32-bit) + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) Extractable: no Password Enabled: no Public: no @@ -2072,7 +2088,8 @@ service cloudstack-agent start Zone: Choose the zone where this hypervisor is used Hypervisor: VMware Format: OVA - OS Type: Debian GNU/Linux 5.0 (32-bit) + OS Type: Debian GNU/Linux 7.0 (32-bit) (or the highest Debian release + number available in the dropdown) Extractable: no Password Enabled: no Public: no @@ -2094,6 +2111,46 @@ service cloudstack-agent start 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. + + (KVM on RHEL 6.0/6.1 only) If your existing &PRODUCT; deployment includes one or + more clusters of KVM hosts running RHEL 6.0 or RHEL 6.1, perform the following: + + + Ensure that you upgrade the operating system version on those hosts before + upgrading &PRODUCT; + To do that, change the yum repository for each system with &PRODUCT; packages, + that implies that all the Management Servers and any hosts that have the KVM agent. + + + + Open /etc/yum.repos.d/cloudstack.repo on any systems that + have &PRODUCT; packages installed. + + + Edit as follows: + + [upgrade] + name=rhel63 + baseurl=url-of-your-rhel6.3-repo + enabled=1 + gpgcheck=0 + [apache CloudStack] + name= Apache CloudStack + baseurl= http://cloudstack.apt-get.eu/rhel/4.0/ + enabled=1 + gpgcheck=0 + If you are using the community provided package repository, change the baseurl + to http:// cloudstack.apt-get.eu/rhel/4.2/ + If you are using your own package repository, change this line to read as + appropriate for your 4.2.0 repository. + + + Now that you have the repository configured, upgrade the host operating system + from RHEL 6.0 to 6.3: + # yum upgrade + + + Stop all Usage Servers if running. Run this on all Usage Server hosts. # service cloud-usage stop @@ -2127,7 +2184,7 @@ service cloudstack-agent start 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 @@ -2206,7 +2263,7 @@ service cloudstack-agent restart If you've created your own packages and yum repository, substitute your own URL for the ones used in these examples. - + The first order of business will be to change the yum repository for each system with &PRODUCT; packages. This means all management servers, and any hosts that have @@ -2287,7 +2344,7 @@ service cloudstack-agent start Merge your changes from the backup file into the new components.xml file. - # vi /etc/cloud/management/components.xml + # vi /etc/cloudstack/management/components.xml @@ -2319,7 +2376,7 @@ service cloudstack-agent start 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: @@ -2332,7 +2389,7 @@ service cloudstack-agent start Merge your changes from the backup file into the new db.properties file. - # vi /etc/cloud/management/db.properties + # vi /etc/cloudstack/management/db.properties @@ -2377,32 +2434,6 @@ service cloudstack-agent start this on each Usage Server host. # service cloudstack-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. - - - Copy the CloudPlatform 4.2 tar file to the host, untar it, and change directory - to the resulting directory. - - - Stop the running agent. - # service cloud-agent stop - - - Update the agent software. - # ./install.sh - - - Choose "U" to update the packages. - - - Start the agent. - # service cloudstack-agent start - - - (KVM only) Perform the following additional steps on each KVM host. These steps will not affect running guests in the cloud. These steps are required @@ -2421,21 +2452,33 @@ service cloudstack-agent start appropriate. # yum update cloud-* - # apt-get update - # apt-get upgrade cloud-* + # apt-get update + # apt-get upgrade cloud-* - - Start the agent. - # service cloudstack-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 + sed -i 's/com.cloud.agent.resource.computing.LibvirtComputingResource/com.cloud.hypervisor.kvm.resource.LibvirtComputingResource/g' /etc/cloudstack/agent/agent.properties - Start the cloud agent and cloud management services. + Upgrade all the existing bridge names to new bridge names by running this + script: + # cloudstack-agent-upgrade + + + Install a libvirt hook with the following commands: + # mkdir /etc/libvirt/hooks + # cp /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu + # chmod +x /etc/libvirt/hooks/qemu + + + Restart libvirtd. + # service libvirtd restart + + + Start the agent. + # service cloudstack-agent start When the Management Server is up and running, log in to the CloudStack UI and @@ -2459,8 +2502,8 @@ service cloudstack-agent start 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 + # nohup cloudstack-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. From 0363b4ae180e377feb74f22dacbabd24a6bde2cd Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Mon, 23 Sep 2013 16:41:33 +0530 Subject: [PATCH 225/251] CLOUDSTACK-4727 upgrade steps IDREF issue fixed --- 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 96e99281f15..6f803b1ed90 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -1505,7 +1505,7 @@ Done restarting router(s). - + Stop all Usage Servers if running. Run this on all Usage Server hosts. # service cloud-usage stop From 38bbfdc89a50bbb9464700d202d1cfa7b7955953 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 23 Sep 2013 14:13:31 +0200 Subject: [PATCH 226/251] CLOUDSTACK-4716: upgrade resource count (cpu,memory,primary_storage,secondary_storage) in the upgrade to 4.2 --- .../cloud/upgrade/dao/Upgrade410to420.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 646b4062a48..cd4a9593ef4 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -115,6 +115,7 @@ public class Upgrade410to420 implements DbUpgrade { migrateVolumeOnSecondaryStorage(conn); createFullCloneFlag(conn); upgradeVpcServiceMap(conn); + upgradeResourceCount(conn); } private void createFullCloneFlag(Connection conn) { @@ -3012,4 +3013,152 @@ public class Upgrade410to420 implements DbUpgrade { } } } + + private void upgradeResourceCount(Connection conn) { + s_logger.debug("upgradeResourceCount start"); + PreparedStatement pstmt1 = null; + PreparedStatement pstmt2 = null; + PreparedStatement pstmt3 = null; + PreparedStatement pstmt4 = null; + PreparedStatement pstmt5 = null; + ResultSet rs = null; + ResultSet rsAccount = null; + ResultSet rsCount = null; + try { + pstmt1 = conn.prepareStatement("select id, domain_id FROM `cloud`.`account` where removed is NULL "); + rsAccount = pstmt1.executeQuery(); + while (rsAccount.next()) { + long account_id = rsAccount.getLong(1); + long domain_id = rsAccount.getLong(2); + // 1. update cpu,memory for all accounts + pstmt2 = conn.prepareStatement( "SELECT SUM(service_offering.cpu), SUM(service_offering.ram_size)" + + " FROM `cloud`.`vm_instance`, `cloud`.`service_offering`" + + " WHERE vm_instance.service_offering_id = service_offering.id AND vm_instance.account_id = ?" + " AND vm_instance.removed is NULL" + + " AND vm_instance.vm_type='User' AND state not in ('Destroyed', 'Error', 'Expunging')"); + pstmt2.setLong(1, account_id); + rsCount = pstmt2.executeQuery(); + if (rsCount.next()) { + upgradeResourceCountforAccount(conn, account_id, domain_id, "cpu", rsCount.getLong(1)); + upgradeResourceCountforAccount(conn, account_id, domain_id, "memory", rsCount.getLong(2)); + } else { + upgradeResourceCountforAccount(conn, account_id, domain_id, "cpu", 0L); + upgradeResourceCountforAccount(conn, account_id, domain_id, "memory", 0L); + } + // 2. update primary_storage for all accounts + pstmt3 = conn.prepareStatement("SELECT sum(size) FROM `cloud`.`volumes` WHERE account_id= ?" + + " AND (path is not NULL OR state in ('Allocated')) AND removed is NULL" + + " AND instance_id IN (SELECT id FROM `cloud`.`vm_instance` WHERE vm_type='User')"); + pstmt3.setLong(1, account_id); + rsCount = pstmt3.executeQuery(); + if (rsCount.next()) { + upgradeResourceCountforAccount(conn, account_id, domain_id, "primary_storage", rsCount.getLong(1)); + } else { + upgradeResourceCountforAccount(conn, account_id, domain_id, "primary_storage", 0L); + } + // 3. update secondary_storage for all accounts + long totalVolumesSize = 0; + long totalSnapshotsSize = 0; + long totalTemplatesSize = 0; + pstmt4 = conn.prepareStatement("SELECT sum(size) FROM `cloud`.`volumes` WHERE account_id= ?" + + " AND path is NULL AND state not in ('Allocated') AND removed is NULL"); + pstmt4.setLong(1, account_id); + rsCount = pstmt4.executeQuery(); + if (rsCount.next()) { + totalVolumesSize = rsCount.getLong(1); + } + pstmt4 = conn.prepareStatement("SELECT sum(size) FROM `cloud`.`snapshots` WHERE account_id= ? AND removed is NULL"); + pstmt4.setLong(1, account_id); + rsCount = pstmt4.executeQuery(); + if (rsCount.next()) { + totalSnapshotsSize = rsCount.getLong(1); + } + pstmt4 = conn.prepareStatement("SELECT sum(template_store_ref.size) FROM `cloud`.`template_store_ref`,`cloud`.`vm_template` WHERE account_id = ?" + + " AND template_store_ref.template_id = vm_template.id AND download_state = 'DOWNLOADED' AND destroyed = false AND removed is NULL"); + pstmt4.setLong(1, account_id); + rsCount = pstmt4.executeQuery(); + if (rsCount.next()) { + totalTemplatesSize = rsCount.getLong(1); + } + upgradeResourceCountforAccount(conn, account_id, domain_id, "secondary_storage", totalVolumesSize + totalSnapshotsSize + totalTemplatesSize); + } + // 4. upgrade cpu,memory,primary_storage,secondary_storage for domains + String resource_types[] = {"cpu","memory", "primary_storage", "secondary_storage"}; + pstmt5 = conn.prepareStatement("select id FROM `cloud`.`domain`"); + rsAccount = pstmt5.executeQuery(); + while (rsAccount.next()) { + long domain_id = rsAccount.getLong(1); + for(int count=0; count < resource_types.length; count++) { + String resource_type = resource_types[count]; + upgradeResourceCountforDomain(conn, domain_id, resource_type, 0L); // reset value to 0 before statistics + } + } + for(int count= 0; count < resource_types.length; count++) { + String resource_type = resource_types[count]; + pstmt5 = conn.prepareStatement("select account.domain_id,sum(resource_count.count) from `cloud`.`account` left join `cloud`.`resource_count` on account.id=resource_count.account_id " + + "where resource_count.type=? group by account.domain_id;"); + pstmt5.setString(1, resource_type); + rsCount = pstmt5.executeQuery(); + while (rsCount.next()) { + long domain_id = rsCount.getLong(1); + long resource_count = rsCount.getLong(2); + upgradeResourceCountforDomain(conn, domain_id, resource_type, resource_count); + } + } + s_logger.debug("upgradeResourceCount finish"); + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to upgrade resource count (cpu,memory,primary_storage,secondary_storage) ", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (rsAccount != null) { + rsAccount.close(); + } + if (rsCount != null) { + rsCount.close(); + } + if (pstmt1 != null) { + pstmt1.close(); + } + if (pstmt2 != null) { + pstmt2.close(); + } + if (pstmt3 != null) { + pstmt3.close(); + } + if (pstmt4 != null) { + pstmt4.close(); + } + if (pstmt5 != null) { + pstmt5.close(); + } + } catch (SQLException e) { + } + } + } + + private static void upgradeResourceCountforAccount(Connection conn, Long account_id, Long domain_id, String type, Long resource_count) throws SQLException { + //update or insert into resource_count table. + PreparedStatement pstmt = null; + pstmt = conn.prepareStatement("INSERT INTO `cloud`.`resource_count` (account_id, type, count) VALUES (?,?,?) ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), count=?"); + pstmt.setLong(1, account_id); + pstmt.setString(2, type); + pstmt.setLong(3, resource_count); + pstmt.setLong(4, resource_count); + pstmt.executeUpdate(); + pstmt.close(); + } + + private static void upgradeResourceCountforDomain(Connection conn, Long domain_id, String type, Long resource_count) throws SQLException { + //update or insert into resource_count table. + PreparedStatement pstmt = null; + pstmt = conn.prepareStatement("INSERT INTO `cloud`.`resource_count` (domain_id, type, count) VALUES (?,?,?) ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), count=?"); + pstmt.setLong(1, domain_id); + pstmt.setString(2, type); + pstmt.setLong(3, resource_count); + pstmt.setLong(4, resource_count); + pstmt.executeUpdate(); + pstmt.close(); + } } From 35e50761562fc558ec7e272cc7dbd1cff564d356 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 23 Sep 2013 11:57:10 -0700 Subject: [PATCH 227/251] CLOUDSTACK-2180: UI > Instances ? Reboot VM action > if the template from which vm is created is password-enabled, pop up "Password has been reset to xxxxxxx" dialog after action is complete. --- ui/scripts/instances.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index cf8aca5b485..a9f6d1c8c5a 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -458,6 +458,12 @@ }, notification: function(args) { return 'label.action.reboot.instance'; + }, + complete: function(args) { + if (args.password != null && args.password.length > 0) + return 'Password has been reset to ' + args.password; + else + return null; } }, notification: { From 3eb899a5ba0b4350e09d3c147762b83291d9f804 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 23 Sep 2013 13:54:20 -0700 Subject: [PATCH 228/251] CLOUDSTACK-4128: UI > zone wizard > secondary storage step > provider "S3" > Create NFS staging is required for S3 at this moment. So, disallow user to uncheck "Create NFS Secondary Staging" checkbox when provider is "S3". --- ui/scripts/zoneWizard.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 7b2679062b1..960fcb734c8 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -1913,13 +1913,7 @@ $fields.filter('[rel=usehttps]').hide(); $fields.filter('[rel=connectiontimeout]').hide(); $fields.filter('[rel=maxerrorretry]').hide(); - $fields.filter('[rel=sockettimeout]').hide(); - - $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); - $fields.filter('[rel=createNfsCache]').find('input').attr("disabled", "disabled"); - $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); + $fields.filter('[rel=sockettimeout]').hide(); } else { $fields.filter('[rel=accesskey]').css('display', 'inline-block'); $fields.filter('[rel=secretkey]').css('display', 'inline-block'); @@ -1929,13 +1923,13 @@ $fields.filter('[rel=connectiontimeout]').css('display', 'inline-block'); $fields.filter('[rel=maxerrorretry]').css('display', 'inline-block'); $fields.filter('[rel=sockettimeout]').css('display', 'inline-block'); - - $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); - $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); - $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); } - + $fields.filter('[rel=createNfsCache]').find('input').attr('checked', 'checked'); + $fields.filter('[rel=createNfsCache]').find('input').attr("disabled", "disabled"); //Create NFS staging is required for S3 at this moment. So, disallow user to uncheck "Create NFS Secondary Staging" checkbox + $fields.filter('[rel=createNfsCache]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCacheNfsServer]').css('display', 'inline-block'); + $fields.filter('[rel=nfsCachePath]').css('display', 'inline-block'); + //Swift $fields.filter('[rel=url]').hide(); $fields.filter('[rel=account]').hide(); From 4280f6e29d832c51a67f8f5427f92fcb6bd656c5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 23 Sep 2013 16:10:36 -0700 Subject: [PATCH 229/251] CLOUDSTACK-4693: UI > Network > Add Guest Network dialog > physical network dropdown - populate only physical networks that have Guest traffic type. --- ui/scripts/sharedFunctions.js | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 5369cee6678..fe3f6730295 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -235,9 +235,9 @@ var addGuestNetworkDialog = { label: 'label.physical.network', dependsOn: 'zoneId', select: function(args) { - if ('physicalNetworks' in args.context) { + if ('physicalNetworks' in args.context) { //Infrastructure menu > zone detail > guest traffic type > network tab (only shown in advanced zone) > add guest network dialog addGuestNetworkDialog.physicalNetworkObjs = args.context.physicalNetworks; - } else { + } else { //Network menu > guest network section > add guest network dialog var selectedZoneId = args.$form.find('.form-item[rel=zoneId]').find('select').val(); $.ajax({ url: createURL('listPhysicalNetworks'), @@ -245,8 +245,33 @@ var addGuestNetworkDialog = { zoneid: selectedZoneId }, async: false, - success: function(json) { - addGuestNetworkDialog.physicalNetworkObjs = json.listphysicalnetworksresponse.physicalnetwork; + success: function(json) { + var items = []; + var physicalnetworks = json.listphysicalnetworksresponse.physicalnetwork; + if (physicalnetworks != null) { + for (var i = 0; i < physicalnetworks.length; i++) { + $.ajax({ + url: createURL('listTrafficTypes'), + data: { + physicalnetworkid: physicalnetworks[i].id + }, + async: false, + success: function(json) { + var traffictypes = json.listtraffictypesresponse.traffictype; + if (traffictypes != null) { + for (var k = 0; k < traffictypes.length; k++) { + if (traffictypes[k].traffictype == 'Guest') { + items.push(physicalnetworks[i]); + break; + } + } + } + } + }); + } + } + + addGuestNetworkDialog.physicalNetworkObjs = items; } }); } From 175549f3ab952bbd39318c16c269c16526255475 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 24 Sep 2013 08:51:58 +0200 Subject: [PATCH 230/251] CLOUDSTACK-4405: additional patch for bridge name and firewall rules issues after KVM upgrade to 4.2 There still exist two issues after Edison's commits. (1) Migration from new hosts to old hosts failed. The bridge name on old host is set to cloudVirBr* if network.bridge.name.schema is set to 3.0 in /etc/cloudstack/agent/agent.properties, but the actual bridge name is breth*-* after running cloudstack-agent-upgrade. (2) all ports of vms (Basic zone, or Advanced zone with security groups) on old hosts are open, because the iptables rules are binding to device (bridge) name which is changed by cloudstack-agent-upgrade. After this, the KVM upgrade steps : a. Install 4.2 cloudstack agent on each kvm host b. Run "cloudstack-agent-upgrade". This script will upgrade all the existing bridge name to new bridge name, and update related firewall rules. c. install a libvirt hook: c1. mkdir /etc/libvirt/hooks c2. cp /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu c3. chmod +x /etc/libvirt/hooks/qemu c4. service libvirtd restart c5. service cloudstack-agent restart --- agent/bindir/cloudstack-agent-upgrade.in | 13 +++++++++++++ .../kvm/resource/BridgeVifDriver.java | 16 +--------------- scripts/vm/network/security_group.py | 19 +++++++++++++------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/agent/bindir/cloudstack-agent-upgrade.in b/agent/bindir/cloudstack-agent-upgrade.in index 4972d3901fe..72b0fae5853 100644 --- a/agent/bindir/cloudstack-agent-upgrade.in +++ b/agent/bindir/cloudstack-agent-upgrade.in @@ -17,6 +17,8 @@ # under the License. from cloudutils.networkConfig import networkConfig from cloudutils.utilities import bash +import logging +import re def isOldStyleBridge(brName): if brName.find("cloudVirBr") == 0: return True @@ -33,6 +35,17 @@ def upgradeBridgeName(brName, enslavedDev): bash("ip link set %s down"%brName) bash("ip link set %s name %s"%(brName, newBrName)) bash("ip link set %s up" %newBrName) + cmd = "iptables-save | grep FORWARD | grep -w " + brName + rules = bash(cmd).stdout.split('\n') + rules.pop() + for rule in rules: + try: + delrule = re.sub("-A", "-D", rule) + newrule = re.sub(" " + brName + " ", " " + newBrName + " ", rule) + bash("iptables " + delrule) + bash("iptables " + newrule) + except: + logging.exception("Ignoring failure to update rules for rule " + rule + " on bridge " + brName) if __name__ == '__main__': netlib = networkConfig() bridges = netlib.listNetworks() diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index e3779a77cd7..42b2df22167 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -45,7 +45,6 @@ public class BridgeVifDriver extends VifDriverBase { private static final Object _vnetBridgeMonitor = new Object(); private String _modifyVlanPath; - private String bridgeNameSchema; @Override public void configure(Map params) throws ConfigurationException { @@ -61,8 +60,6 @@ public class BridgeVifDriver extends VifDriverBase { networkScriptsDir = "scripts/vm/network/vnet"; } - bridgeNameSchema = (String) params.get("network.bridge.name.schema"); - String value = (String) params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000; @@ -147,18 +144,7 @@ public class BridgeVifDriver extends VifDriverBase { } private String setVnetBrName(String pifName, String vnetId) { - String brName = null; - if (bridgeNameSchema != null) { - if (bridgeNameSchema.equalsIgnoreCase("3.0")) { - brName = "cloudVirBr" + vnetId; - } else if (bridgeNameSchema.equalsIgnoreCase("4.0")) { - brName = "br" + pifName + "-"+ vnetId; - } - } else { - brName = "br" + pifName + "-"+ vnetId; - } - - return brName; + return "br" + pifName + "-"+ vnetId; } private String createVlanBr(String vlanId, String nic) diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 0ac8b74a872..674cceb049b 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -250,7 +250,7 @@ def default_network_rules_systemvm(vm_name, localbrname): if bridge != localbrname: if not addFWFramework(bridge): return False - brfw = "BF-" + bridge + brfw = getBrfw(bridge) vifs = getVifsForBridge(vm_name, bridge) for vif in vifs: try: @@ -356,7 +356,7 @@ def default_network_rules(vm_name, vm_id, vm_ip, vm_mac, vif, brname, sec_ips): return False vmName = vm_name - brfw = "BF-" + brname + brfw = getBrfw(brname) domID = getvmId(vm_name) delete_rules_for_vm_in_bridge_firewall_chain(vmName) vmchain = vm_name @@ -549,7 +549,7 @@ def network_rules_for_rebooted_vm(vmName): if brName is None or brName is "": brName = "cloudbr0" else: - brName = re.sub("^BF-", "", brName) + brName = execute("iptables-save |grep physdev-is-bridged |grep FORWARD |grep BF |grep '\-o' |awk '{print $4}' | head -1").strip() if 1 in [ vm_name.startswith(c) for c in ['r-', 's-', 'v-'] ]: @@ -562,8 +562,8 @@ def network_rules_for_rebooted_vm(vmName): vifs = getVifs(vmName) logging.debug(vifs, brName) for v in vifs: - execute("iptables -A " + "BF-" + brName + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j "+ vmchain_default) - execute("iptables -A " + "BF-" + brName + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j "+ vmchain_default) + execute("iptables -A " + getBrfw(brName) + "-IN " + " -m physdev --physdev-is-bridged --physdev-in " + v + " -j "+ vmchain_default) + execute("iptables -A " + getBrfw(brName) + "-OUT " + " -m physdev --physdev-is-bridged --physdev-out " + v + " -j "+ vmchain_default) #change antispoof rule in vmchain try: @@ -871,6 +871,13 @@ def getBridges(vmName): def getvmId(vmName): cmd = "virsh list |grep " + vmName + " | awk '{print $1}'" return bash("-c", cmd).stdout.strip() + +def getBrfw(brname): + cmd = "iptables-save |grep physdev-is-bridged |grep FORWARD |grep BF |grep '\-o' | grep -w " + brname + "|awk '{print $9}' | head -1" + brfwname = bash("-c", cmd).stdout.strip() + if brfwname == "": + brfwname = "BF-" + brname + return brfwname def addFWFramework(brname): try: @@ -885,7 +892,7 @@ def addFWFramework(brname): logging.debug("failed to turn on bridge netfilter") return False - brfw = "BF-" + brname + brfw = getBrfw(brname) try: execute("iptables -L " + brfw) except: From a0988780ad88bb56becb0a13efedcd79c1bee142 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 24 Sep 2013 09:14:15 +0200 Subject: [PATCH 231/251] CLOUDSTACK-4405: change rpm and debian packaging to support automatic update (KVM upgrade) Including following steps: b. Run "cloudstack-agent-upgrade". This script will upgrade all the existing bridge name to new bridge name, and update related firewall rules. c. install a libvirt hook: c1. mkdir /etc/libvirt/hooks c2. cp /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu c3. chmod +x /etc/libvirt/hooks/qemu c4. service libvirtd restart --- debian/cloudstack-agent.postinst | 10 +++++++++- debian/rules | 2 ++ packaging/centos63/cloud.spec | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/debian/cloudstack-agent.postinst b/debian/cloudstack-agent.postinst index 499ae6a695a..9bad1380bf0 100644 --- a/debian/cloudstack-agent.postinst +++ b/debian/cloudstack-agent.postinst @@ -34,7 +34,15 @@ case "$1" in fi done fi + + # Running cloudstack-agent-upgrade to update bridge name for upgrade from CloudStack 4.0.x (and before) to CloudStack 4.1 (and later) + /usr/bin/cloudstack-agent-upgrade + if [ ! -d "/etc/libvirt/hooks" ] ; then + mkdir /etc/libvirt/hooks + fi + cp -a /usr/share/cloudstack-agent/lib/libvirtqemuhook /etc/libvirt/hooks/qemu + /etc/init.d/libvirt-bin restart ;; esac -exit 0 \ No newline at end of file +exit 0 diff --git a/debian/rules b/debian/rules index 5e3d58c4da3..26a3495d1c0 100755 --- a/debian/rules +++ b/debian/rules @@ -71,6 +71,8 @@ install: install -D packaging/debian/init/cloud-agent $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-agent install -D agent/target/transformed/cloud-setup-agent $(DESTDIR)/usr/bin/cloudstack-setup-agent install -D agent/target/transformed/cloud-ssh $(DESTDIR)/usr/bin/cloudstack-ssh + install -D agent/target/transformed/cloudstack-agent-upgrade $(DESTDIR)/usr/bin/cloudstack-setup-agent + install -D agent/target/transformed/libvirtqemuhook $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent # cloudstack-management diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 5f8a2a50d16..1e88ea7a37a 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -476,6 +476,13 @@ fi %post agent if [ "$1" == "1" ] ; then + echo "Running %{_bindir}/%{name}-agent-upgrade to update bridge name for upgrade from CloudStack 4.0.x (and before) to CloudStack 4.1 (and later)" + %{_bindir}/%{name}-agent-upgrade + if [ ! -d %{_sysconfdir}/libvirt/hooks ] ; then + mkdir %{_sysconfdir}/libvirt/hooks + fi + cp -a ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook %{_sysconfdir}/libvirt/hooks/qemu + /sbin/service libvirtd restart /sbin/chkconfig --add cloudstack-agent > /dev/null 2>&1 || true /sbin/chkconfig --level 345 cloudstack-agent on > /dev/null 2>&1 || true fi From a206f423a3851a4d090f58b05e2936dd68c528d0 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 24 Sep 2013 11:59:05 +0200 Subject: [PATCH 232/251] fix issue: listHosts with hahost throws exception --- server/src/com/cloud/api/query/QueryManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 615e207d1a4..86276b3f040 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -1571,7 +1571,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { } if (haHosts != null && haTag != null && !haTag.isEmpty()) { - sc.setJoinParameters("hostTagSearch", "tag", haTag); + sc.setParameters("tag", haTag); } // search host details by ids From 5225bd434cfce108913e06f842630e346496fcb2 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 24 Sep 2013 15:01:14 +0200 Subject: [PATCH 233/251] CLOUDSTACK-4732: search network name for virtual router when listRouters by keyword (cherry picked from commit 98c79cc3138a1c2e3d85ce1f04c4431ba99272bd) --- server/src/com/cloud/api/query/QueryManagerImpl.java | 1 + ui/scripts/system.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 86276b3f040..6104ec0c863 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -1144,6 +1144,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("instanceName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("networkName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("instanceName", SearchCriteria.Op.SC, ssc); } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 1ddbc21cb8a..77c387c4e15 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -7049,7 +7049,7 @@ var listView = $.extend(true, {}, cloudStack.sections.system.subsections.virtualRouters.listView, { dataProvider: function(args) { var searchByArgs = args.filterBy.search.value.length ? - '&name=' + args.filterBy.search.value : ''; + '&keyword=' + args.filterBy.search.value : ''; var routers = []; $.ajax({ From 143fb72e8bf6dac9f9e0765943bc28d001a7f1d1 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 24 Sep 2013 11:58:34 -0700 Subject: [PATCH 234/251] System vm not coming up in multizone upgraded enviornment if a system vm template is register twice per zone. --- .../com/cloud/storage/dao/VMTemplateDaoImpl.java | 14 +++++++++++++- .../secondary/SecondaryStorageManagerImpl.java | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java index 2aca203ca6d..7abb58c5dbc 100755 --- a/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -30,6 +30,9 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.storage.VMTemplateStorageResourceAssoc; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.log4j.Logger; @@ -70,6 +73,8 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem VMTemplateZoneDao _templateZoneDao; @Inject VMTemplateDetailsDao _templateDetailsDao; + @Inject + DataStoreManager _dataStoreMgr; @Inject ConfigurationDao _configDao; @@ -338,6 +343,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem readySystemTemplateSearch.and("templateType", readySystemTemplateSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); SearchBuilder templateDownloadSearch = _templateDataStoreDao.createSearchBuilder(); templateDownloadSearch.and("downloadState", templateDownloadSearch.entity().getDownloadState(), SearchCriteria.Op.EQ); + templateDownloadSearch.and("dataStoreId", templateDownloadSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); readySystemTemplateSearch.join("vmTemplateJoinTemplateStoreRef", templateDownloadSearch, templateDownloadSearch.entity().getTemplateId(), readySystemTemplateSearch.entity().getId(), JoinBuilder.JoinType.INNER); SearchBuilder hostHyperSearch2 = _hostDao.createSearchBuilder(); @@ -795,7 +801,13 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem sc.setJoinParameters("tmplHyper", "type", Host.Type.Routing); sc.setJoinParameters("tmplHyper", "zoneId", zoneId); sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState", VMTemplateStorageResourceAssoc.Status.DOWNLOADED); - + DataStore secStore = this._dataStoreMgr.getImageStore(zoneId); + if (secStore != null) { + sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "dataStoreId", secStore.getId()); + } else{ + s_logger.warn("No secondary storage is available in data center " + zoneId); + } + // order by descending order of id List tmplts = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index ba677a04df2..04fb8449135 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -743,7 +743,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar DataStore store = templateMgr.getImageStore(dataCenterId, template.getId()); if (store == null) { if (s_logger.isDebugEnabled()) { - s_logger.debug("No secondary storage available in zone " + dataCenterId + ", wait until it is ready to launch secondary storage vm"); + s_logger.debug("Secondary storage VM template " + template.getId() + " is not available in zone " + dataCenterId + ", wait until it is ready to launch secondary storage vm"); } return false; } From 2be3678602e71b723a241afbe1e2365618ade4fe Mon Sep 17 00:00:00 2001 From: Abhinandan Prateek Date: Wed, 25 Sep 2013 21:09:24 +0530 Subject: [PATCH 235/251] Cloudstack-2997: the maxconn setting is masked by wrong conditions --- .../src/com/cloud/configuration/ConfigurationManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index d32c693f372..b09b8ca43ca 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -4001,7 +4001,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } validateLoadBalancerServiceCapabilities(lbServiceCapabilityMap); - if (!serviceProviderMap.containsKey(Service.Lb) && lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { + if (lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { maxconn = cmd.getMaxconnections(); if (maxconn == null) { maxconn=Integer.parseInt(_configDao.getValue(Config.NetworkLBHaproxyMaxConn.key())); From 7f988d4e99bc6dc0e8d02b1eed9a501645ee14e1 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 26 Sep 2013 22:47:23 +0200 Subject: [PATCH 236/251] fix silly typo (cherry picked from commit 522860c03de5d05126f92fc44b6e3f50ed8439f0) --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 26a3495d1c0..7cbb9ed182d 100755 --- a/debian/rules +++ b/debian/rules @@ -71,7 +71,7 @@ install: install -D packaging/debian/init/cloud-agent $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-agent install -D agent/target/transformed/cloud-setup-agent $(DESTDIR)/usr/bin/cloudstack-setup-agent install -D agent/target/transformed/cloud-ssh $(DESTDIR)/usr/bin/cloudstack-ssh - install -D agent/target/transformed/cloudstack-agent-upgrade $(DESTDIR)/usr/bin/cloudstack-setup-agent + install -D agent/target/transformed/cloudstack-agent-upgrade $(DESTDIR)/usr/bin/cloudstack-agent-upgrade install -D agent/target/transformed/libvirtqemuhook $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent From 92e9f6daf8e7bcbe646a41132c69d615b0f9af60 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Thu, 26 Sep 2013 13:59:35 -0700 Subject: [PATCH 237/251] CLOUDSTACK-4745: Add StaticNatRule through firewall manager This would fix CreateIpForwardingCmd API on 4.2+. --- .../network/firewall/FirewallManagerImpl.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index d250a08d477..8c4583ebac8 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -544,6 +544,8 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, throws ResourceUnavailableException { boolean handled = false; switch (purpose){ + /* StaticNatRule would be applied by Firewall provider, since the incompatible of two object */ + case StaticNat: case Firewall: for (FirewallServiceProvider fwElement: _firewallElements) { Network.Provider provider = fwElement.getProvider(); @@ -568,18 +570,6 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, break; } break; - case StaticNat: - for (StaticNatServiceProvider element: _staticNatElements) { - Network.Provider provider = element.getProvider(); - boolean isSnatProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.StaticNat, provider); - if (!isSnatProvider) { - continue; - } - handled = element.applyStaticNats(network, (List) rules); - if (handled) - break; - } - break; /* case NetworkACL: for (NetworkACLServiceProvider element: _networkAclElements) { Network.Provider provider = element.getProvider(); From c9aa8800ceca288675242fd7311b5190faf6019a Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 27 Sep 2013 10:31:54 +0200 Subject: [PATCH 238/251] add missing cloudstack-agent-upgrade in cloudstack-agent debian packages (cherry picked from commit a6bfd9602129d8ae308ba58f36623c04826e15ca) --- debian/cloudstack-agent.install | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/cloudstack-agent.install b/debian/cloudstack-agent.install index a3cc86964dd..d708514fd14 100644 --- a/debian/cloudstack-agent.install +++ b/debian/cloudstack-agent.install @@ -21,6 +21,7 @@ /etc/init.d/cloudstack-agent /usr/bin/cloudstack-setup-agent /usr/bin/cloudstack-ssh +/usr/bin/cloudstack-agent-upgrade /var/log/cloudstack/agent /usr/share/cloudstack-agent/lib/* /usr/share/cloudstack-agent/plugins From 94c8e28f8d92ce711a5aaeabed52bb69ef3a6700 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 27 Sep 2013 15:11:07 -0700 Subject: [PATCH 239/251] CLOUDSTACK-4734: Validate and Fail-over to another VmwareContext object when it is from the pool --- .../vmware/resource/VmwareContextFactory.java | 9 +++- .../VmwareSecondaryStorageContextFactory.java | 10 ++++ .../hypervisor/vmware/mo/DatacenterMO.java | 8 ++- .../hypervisor/vmware/util/VmwareClient.java | 51 ++++++++++++++++--- .../hypervisor/vmware/util/VmwareContext.java | 4 ++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java index ed607e118d2..3079998198c 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java @@ -80,8 +80,15 @@ public class VmwareContextFactory { public static VmwareContext getContext(String vCenterAddress, String vCenterUserName, String vCenterPassword) throws Exception { VmwareContext context = s_pool.getContext(vCenterAddress, vCenterUserName); - if(context == null) + if(context == null) { context = create(vCenterAddress, vCenterUserName, vCenterPassword); + } else { + if(!context.validate()) { + s_logger.info("Validation of the context faild. dispose and create a new one"); + context.close(); + context = create(vCenterAddress, vCenterUserName, vCenterPassword); + } + } if(context != null) { context.registerStockObject(VmwareManager.CONTEXT_STOCK_NAME, s_vmwareMgr); diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java index 5365e58e78e..253d6fd3517 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java @@ -16,11 +16,15 @@ // under the License. package com.cloud.storage.resource; +import org.apache.log4j.Logger; + import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.hypervisor.vmware.util.VmwareContextPool; public class VmwareSecondaryStorageContextFactory { + private static final Logger s_logger = Logger.getLogger(VmwareSecondaryStorageContextFactory.class); + private static volatile int s_seq = 1; private static VmwareContextPool s_pool; @@ -51,6 +55,12 @@ public class VmwareSecondaryStorageContextFactory { VmwareContext context = s_pool.getContext(vCenterAddress, vCenterUserName); if(context == null) { context = create(vCenterAddress, vCenterUserName, vCenterPassword); + } else { + if(!context.validate()) { + s_logger.info("Validation of the context faild. dispose and create a new one"); + context.close(); + context = create(vCenterAddress, vCenterUserName, vCenterPassword); + } } if(context != null) { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java index cabb60abc5d..6d82aefad4b 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java @@ -20,6 +20,8 @@ package com.cloud.hypervisor.vmware.mo; import java.util.ArrayList; import java.util.List; +import org.apache.log4j.Logger; + import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.utils.Pair; import com.vmware.vim25.CustomFieldStringValue; @@ -37,6 +39,7 @@ import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo; import edu.emory.mathcs.backport.java.util.Arrays; public class DatacenterMO extends BaseMO { + private static final Logger s_logger = Logger.getLogger(DatacenterMO.class); public DatacenterMO(VmwareContext context, ManagedObjectReference morDc) { super(context, morDc); @@ -50,7 +53,9 @@ public class DatacenterMO extends BaseMO { super(context, null); _mor = _context.getVimClient().getDecendentMoRef(_context.getRootFolder(), "Datacenter", dcName); - assert(_mor != null); + if(_mor == null) { + s_logger.error("Unable to locate DC " + dcName); + } } @Override @@ -61,7 +66,6 @@ public class DatacenterMO extends BaseMO { public void registerTemplate(ManagedObjectReference morHost, String datastoreName, String templateName, String templateFileName) throws Exception { - ManagedObjectReference morFolder = (ManagedObjectReference)_context.getVimClient().getDynamicProperty( _mor, "vmFolder"); assert(morFolder != null); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java index 6ab9700a619..ff13b1c7662 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java @@ -109,7 +109,6 @@ public class VmwareClient { } private ManagedObjectReference SVC_INST_REF = new ManagedObjectReference(); - private ManagedObjectReference propCollectorRef; private static VimService vimService; private VimPortType vimPort; private String serviceCookie; @@ -153,8 +152,6 @@ public class VmwareClient { vimPort.login(serviceContent.getSessionManager(), userName, password, null); isConnected = true; - - propCollectorRef = serviceContent.getPropertyCollector(); } /** @@ -199,7 +196,7 @@ public class VmwareClient { * @return Service property collector */ public ManagedObjectReference getPropCol() { - return propCollectorRef; + return getServiceContent().getPropertyCollector(); } /** @@ -209,6 +206,43 @@ public class VmwareClient { return getServiceContent().getRootFolder(); } + public boolean validate() { + // + // There is no official API to validate an open vCenter API session. This is hacking way to tell if + // an open vCenter API session is still valid for making calls. + // + // It will give false result if there really does not exist data-center in the inventory, however, I consider + // this really is not possible in production deployment + // + + // Create PropertySpecs + PropertySpec pSpec = new PropertySpec(); + pSpec.setType("Datacenter"); + pSpec.setAll(false); + pSpec.getPathSet().add("name"); + + ObjectSpec oSpec = new ObjectSpec(); + oSpec.setObj(getRootFolder()); + oSpec.setSkip(false); + oSpec.getSelectSet().addAll(constructCompleteTraversalSpec()); + + PropertyFilterSpec spec = new PropertyFilterSpec(); + spec.getPropSet().add(pSpec); + spec.getObjectSet().add(oSpec); + List specArr = new ArrayList(); + specArr.add(spec); + + try { + List ocary = vimPort.retrieveProperties(getPropCol(), specArr); + if(ocary != null && ocary.size() > 0) + return true; + } catch(Exception e) { + return false; + } + + return false; + } + /** * Get the property value of a managed object. * @@ -268,7 +302,7 @@ public class VmwareClient { List specArr = new ArrayList(); specArr.add(spec); - return vimPort.retrieveProperties(propCollectorRef, specArr); + return vimPort.retrieveProperties(getPropCol(), specArr); } public boolean waitForTask2(ManagedObjectReference task) throws RuntimeFaultFaultMsg, RemoteException, InterruptedException { @@ -416,7 +450,8 @@ public class VmwareClient { pSpec.setType(objmor.getType()); spec.getPropSet().add(pSpec); - ManagedObjectReference filterSpecRef = vimPort.createFilter(propCollectorRef, spec, true); + ManagedObjectReference propertyCollector = this.getPropCol(); + ManagedObjectReference filterSpecRef = vimPort.createFilter(propertyCollector, spec, true); boolean reached = false; @@ -425,7 +460,7 @@ public class VmwareClient { List objupary = null; List propchgary = null; while (!reached) { - updateset = vimPort.waitForUpdates(propCollectorRef, version); + updateset = vimPort.waitForUpdates(propertyCollector, version); if (updateset == null || updateset.getFilterSet() == null) { continue; } @@ -628,7 +663,7 @@ public class VmwareClient { List specArr = new ArrayList(); specArr.add(spec); - List ocary = vimPort.retrieveProperties(propCollectorRef, specArr); + List ocary = vimPort.retrieveProperties(getPropCol(), specArr); if (ocary == null || ocary.size() == 0) { return null; diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareContext.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareContext.java index 6c3dab7f790..c499576f5f5 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareContext.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareContext.java @@ -102,6 +102,10 @@ public class VmwareContext { if(s_logger.isInfoEnabled()) s_logger.info("New VmwareContext object, current outstanding count: " + getOutstandingContextCount()); } + + public boolean validate() { + return _vimClient.validate(); + } public void registerStockObject(String name, Object obj) { synchronized(_stockMap) { From 583279f3373d3e9893c03468753b5ec4cb68a2ad Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 27 Sep 2013 16:09:22 -0700 Subject: [PATCH 240/251] CLOUDSTACK-4754: it's a race condition: delete template, and deploy vm from the template happened at the same time, --- .../cloudstack/storage/motion/DataMotionServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java index c1cbdc772cc..9f0f5311a28 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java @@ -41,6 +41,9 @@ public class DataMotionServiceImpl implements DataMotionService { @Override public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + if (srcData.getDataStore() == null || destData.getDataStore() == null) { + throw new CloudRuntimeException("can't find data store"); + } if (srcData.getDataStore().getDriver().canCopy(srcData, destData)) { srcData.getDataStore().getDriver().copyAsync(srcData, destData, callback); From 94f741bbf5e025c230172df2977a0986c5b73ec2 Mon Sep 17 00:00:00 2001 From: SrikanteswaraRao Talluri Date: Tue, 17 Sep 2013 15:59:34 +0530 Subject: [PATCH 241/251] CLOUDSTACK-4344: pass delay and retries explicitly to remoteSSHclient to reduce time taken by the tests Signed-off-by: venkataswamybabu budumuru --- test/integration/smoke/test_network.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index 042ac84ae53..f2045959697 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -464,7 +464,9 @@ class TestPortForwarding(cloudstackTestCase): src_nat_ip_addr.ipaddress, self.virtual_machine.ssh_port, self.virtual_machine.username, - self.virtual_machine.password + self.virtual_machine.password, + retries=2, + delay=0 ) return @@ -580,7 +582,9 @@ class TestPortForwarding(cloudstackTestCase): ip_address.ipaddress.ipaddress, self.virtual_machine.ssh_port, self.virtual_machine.username, - self.virtual_machine.password + self.virtual_machine.password, + retries=2, + delay=0 ) return @@ -883,7 +887,9 @@ class TestReleaseIP(cloudstackTestCase): self.ip_addr.ipaddress, self.services["natrule"]["publicport"], self.virtual_machine.username, - self.virtual_machine.password + self.virtual_machine.password, + retries=2, + delay=0 ) return From a34bf457dfb3726842c1fde002be61c53b0a60d7 Mon Sep 17 00:00:00 2001 From: sanjeevneelarapu Date: Mon, 23 Sep 2013 16:41:11 +0530 Subject: [PATCH 242/251] CLOUDSTACK-702: Adding test for deploying vm in new cidr 1. Add guest ip range in new cidr and deploy vm in that Signed-off-by: sanjeevneelarapu Signed-off-by: venkataswamybabu budumuru (cherry picked from commit ff2bd3d44325ef915cb3d7b11a966c9c7c387cb3) --- .../maint/test_multiple_ip_ranges.py | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 test/integration/component/maint/test_multiple_ip_ranges.py diff --git a/test/integration/component/maint/test_multiple_ip_ranges.py b/test/integration/component/maint/test_multiple_ip_ranges.py new file mode 100644 index 00000000000..782957c6717 --- /dev/null +++ b/test/integration/component/maint/test_multiple_ip_ranges.py @@ -0,0 +1,337 @@ +# 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. +""" Tests for Multiple IP Ranges feature +""" +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.cloudstackException import cloudstackAPIException +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +#from netaddr import * +import netaddr + +from nose.plugins.attrib import attr + +class Services: + """Test Multiple IP Ranges + """ + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 200, # in MHz + "memory": 256, # In MBs + }, + "disk_offering": { + "displaytext": "Small Disk", + "name": "Small Disk", + "disksize": 1 + }, + "templates": { + "displaytext": 'Template', + "name": 'Template', + "ostype": "CentOS 5.3 (64-bit)", + "templatefilter": 'self', + }, + "vlan_ip_range": { + "startip": "", + "endip": "", + "netmask": "", + "gateway": "", + "forvirtualnetwork": "false", + "vlan": "untagged", + }, + "server_without_disk": { + "displayname": "Test VM-No Disk", + "username": "root", + "password": "password", + "hypervisor": 'XenServer', + }, + "cidr": { + "name": "cidr1 -Test", + "gateway" :"10.147.43.1", + "netmask" :"255.255.255.128", + "startip" :"10.147.43.3", + "endip" :"10.147.43.10", + }, + "ostype": "CentOS 5.3 (64-bit)", + "sleep": 60, + "timeout": 10, + } + +class TestMultipleIpRanges(cloudstackTestCase): + """Test Multiple IP Ranges for guest network + """ + + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestMultipleIpRanges, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod(cls.api_client, cls.zone.id, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.name + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["templates"]["ostypeid"] = cls.template.ostypeid + cls.services["diskoffering"] = cls.disk_offering.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, terminate the resources created + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def verify_vlan_range(self,vlan,services): + #compare vlan_list response with configured values + self.assertEqual( + isinstance(vlan, list), + True, + "Check list response returned a valid list" + ) + self.assertNotEqual( + len(vlan), + 0, + "check list vlan response" + ) + self.assertEqual( + str(vlan[0].startip), + str(services["startip"]), + "Start IP in vlan ip range is not matched with the configured start ip" + ) + self.assertEqual( + str(vlan[0].endip), + str(services["endip"]), + "End IP in vlan ip range is not matched with the configured end ip" + ) + self.assertEqual( + str(vlan[0].gateway), + str(services["gateway"]), + "gateway in vlan ip range is not matched with the configured gateway" + ) + self.assertEqual( + str(vlan[0].netmask), + str(services["netmask"]), + "netmask in vlan ip range is not matched with the configured netmask" + ) + return + + def list_Routers(self): + """Check if any VR is already present in the setup + Will return True if yes else return False + """ + list_zone = Zone.list(self.apiclient) + network_type = list_zone[0].networktype + sg_enabled = list_zone[0].securitygroupsenabled + if network_type == "Basic": + vr_list = Router.list(self.apiclient, listall='true') + self.debug("vr list {}".format(vr_list)) + if isinstance(vr_list,list) and len(vr_list) > 0: + self.debug("VR is running in the setup") + return True + else: + self.debug("VR is not present in the setup") + return False + elif network_type == "Advanced" and sg_enabled == True: + nw_list = Network.list( + self.apiclient, + supportedservices='SecurityGroup', + ) + nw_id = nw_list[0].id + vr_list = Router.list( + self.apiclient, + networkid = nw_id, + listall = 'true', + ) + if isinstance(vr_list, list) and len(vr_list) > 0: + self.debug("VR is present in the setup") + return True + else : + self.debug("VR is not present in the setup") + return False + else : + self.debug("Network type is not shared") + return None + + def test_01_deploy_vm_in_new_cidr(self): + """Deploy guest vm after adding guest IP range in new CIDR + + 1.Add IP range in new CIDR + 2.Deploy guest vm + """ + dc_id = self.dbclient.execute( + "select id from data_center where uuid = '%s';" % str(self.services["zoneid"]) + ) + dc_id = dc_id[0][0] + id_list = self.dbclient.execute( + "select id from user_ip_address where allocated is null and data_center_id = '%s';" % str(dc_id) + ) + ip_list = [] + for i in range(len(id_list)): + ip_list.append(id_list[i][0]) + #Check if VR is already present in the setup + vr_state = self.list_Routers(); + if vr_state is True : + for id in ip_list: + self.dbclient.execute( + "update user_ip_address set allocated=now() where id = '%s';" % str(id) + ) + else : + ip_list = ip_list[:-2] + for id in ip_list: + self.dbclient.execute( + "update user_ip_address set allocated=now() where id = '%s';" % str(id) + ) + #Add IP range in the new CIDR + test_gateway = self.services["cidr"]["gateway"] + test_startIp = self.services["cidr"]["startip"] + test_endIp = self.services["cidr"]["endip"] + test_netmask = self.services["cidr"]["netmask"] + #Populating services with new IP range + self.services["vlan_ip_range"]["startip"] = test_startIp + self.services["vlan_ip_range"]["endip"] = test_endIp + self.services["vlan_ip_range"]["gateway"] = test_gateway + self.services["vlan_ip_range"]["netmask"] = test_netmask + self.services["vlan_ip_range"]["zoneid"] = self.zone.id + self.services["vlan_ip_range"]["podid"] = self.pod.id + #create new vlan ip range + new_vlan = PublicIpRange.create(self.apiclient, self.services["vlan_ip_range"]) + self.debug("Created new vlan range with startip:%s and endip:%s" %(test_startIp,test_endIp)) + self.cleanup.append(new_vlan) + new_vlan_res = new_vlan.list(self.apiclient,id=new_vlan.vlan.id) + #Compare list output with configured values + self.verify_vlan_range(new_vlan_res,self.services["vlan_ip_range"]) + #Deploy vm in existing subnet if VR is not present + if vr_state is False : + vm_res = VirtualMachine.create( + self.apiclient, + self.services["server_without_disk"], + templateid = self.template.id, + accountid = self.account.name, + domainid = self.services["domainid"], + zoneid = self.services["zoneid"], + serviceofferingid = self.service_offering.id, + mode = self.services["mode"], + ) + self.cleanup.append(vm_res) + #Deploy guest vm + try : + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server_without_disk"], + templateid = self.template.id, + accountid = self.account.name, + domainid = self.services["domainid"], + zoneid = self.services["zoneid"], + serviceofferingid = self.service_offering.id, + mode = self.services["mode"], + ) + except Exception as e : + raise Exception("Warning: Exception during vm deployment: {}".format(e)) + finally : + #Mark ip_Adddresses allocated state to Null which were marked as allocated at the beginning of the test + for id in ip_list : + self.dbclient.execute( + "update user_ip_address set allocated=default where id = '%s';" % str(id) + ) + self.vm_response = VirtualMachine.list( + self.apiclient, + id = self.virtual_machine.id + ) + self.assertEqual( + isinstance(self.vm_response, list), + True, + "Check VM list response returned a valid list" + ) + self.ip_range = list(netaddr.iter_iprange(unicode(self.services["cidr"]["startip"]), unicode(self.services["cidr"]["endip"]))) + self.nic_ip = netaddr.IPAddress(unicode(self.vm_response[0].nic[0].ipaddress)) + self.debug("vm got {} as ip address".format(self.nic_ip)) + self.assertIn( + self.nic_ip, + self.ip_range, + "VM did not get the ip address from the new ip range" + ) + self.virtual_machine.delete(self.apiclient) + expunge_del = Configurations.list( + self.apiclient, + name = 'expunge.delay' + ) + expunge_int = Configurations.list( + self.apiclient, + name = 'expunge.interval' + ) + wait_time = int(expunge_del[0].value) + int(expunge_int[0].value) + int(30) + + self.debug("Waiting for {} seconds for the vm to expunge".format(wait_time)) + #wait for the vm to expunge + time.sleep(wait_time) + return + From 6b28e03d6d88e9b6bd9d0521a45c93d5b4af6501 Mon Sep 17 00:00:00 2001 From: SrikanteswaraRao Talluri Date: Wed, 25 Sep 2013 15:16:13 +0530 Subject: [PATCH 243/251] CLOUDSTACK-4691: move egress rules host maintenance tests to maint folder Signed-off-by: venkataswamybabu budumuru --- .../test_egress_rules_host_maintenance.py | 290 ++++++++++++++++++ .../maint/test_host_high_availability.py | 4 +- .../component/test_egress_rules.py | 12 +- tools/marvin/marvin/integration/lib/base.py | 10 +- .../demo/simulator/testcase/libs/base.py | 8 +- 5 files changed, 307 insertions(+), 17 deletions(-) create mode 100644 test/integration/component/maint/test_egress_rules_host_maintenance.py diff --git a/test/integration/component/maint/test_egress_rules_host_maintenance.py b/test/integration/component/maint/test_egress_rules_host_maintenance.py new file mode 100644 index 00000000000..6f0f768d37c --- /dev/null +++ b/test/integration/component/maint/test_egress_rules_host_maintenance.py @@ -0,0 +1,290 @@ +# 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 for Egresss & Ingress rules +""" +#Import Local Modules +import marvin +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * + +#Import System modules +import time +import subprocess + + +class Services: + """Test Security groups Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "virtual_machine": { + # Create a small virtual machine instance with disk offering + "displayname": "Test VM", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + "userdata": 'This is sample data', + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "security_group": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0', + }, + "ostype": 'CentOS 5.3 (64-bit)', + # CentOS 5.3 (64-bit) + "sleep": 60, + "timeout": 10, + } + + +class TestEgressAfterHostMaintenance(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestEgressAfterHostMaintenance, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.pod = get_pod( + cls.api_client, + zoneid=cls.zone.id + ) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + @attr(speed = "slow") + @attr(tags = ["sg", "eip", "maintenance"]) + def test_egress_after_host_maintenance(self): + """Test maintenance case for egress + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 5. deployVirtualMachine into this security group (ssh) + # 6. deployed VM should be Running, ssh should be allowed into the VM + # 7. Enable maintenance mode for host, cance maintenance mode + # 8. User should be able to SSH into VM after maintainace + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.name, + domainid=self.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.name, + domainid=self.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.name, + domainid=self.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.name) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + ssh = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "Check list VMs response for valid host" + ) + vm = vms[0] + + self.debug("Enabling host maintenance for ID: %s" % vm.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = vm.hostid + self.apiclient.prepareHostForMaintenance(cmd) + + self.debug("Canceling host maintenance for ID: %s" % vm.hostid) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = vm.hostid + self.apiclient.cancelHostMaintenance(cmd) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + self.debug("Starting VM: %s" % self.virtual_machine.id) + + self.virtual_machine.start(self.apiclient) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + ssh = self.virtual_machine.get_ssh_client(reconnect=True) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + return diff --git a/test/integration/component/maint/test_host_high_availability.py b/test/integration/component/maint/test_host_high_availability.py index 5fb047ba6cb..b4c50c7114d 100644 --- a/test/integration/component/maint/test_host_high_availability.py +++ b/test/integration/component/maint/test_host_high_availability.py @@ -616,7 +616,7 @@ class TestHostHighAvailability(cloudstackTestCase): "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" ) - #put the Host in maintainance mode + #put the Host in maintenance mode self.debug("Enabling maintenance mode for host %s" % vm_with_ha_enabled.hostid) cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = vm_with_ha_enabled.hostid @@ -748,7 +748,7 @@ class TestHostHighAvailability(cloudstackTestCase): "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" ) - #put the Host in maintainance mode + #put the Host in maintenance mode self.debug("Enabling maintenance mode for host %s" % vm_with_ha_disabled.hostid) cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = vm_with_ha_disabled.hostid diff --git a/test/integration/component/test_egress_rules.py b/test/integration/component/test_egress_rules.py index 10e0d0356a5..05b45448765 100644 --- a/test/integration/component/test_egress_rules.py +++ b/test/integration/component/test_egress_rules.py @@ -2149,7 +2149,7 @@ class TestInvalidParametersForEgress(cloudstackTestCase): return -class TestEgressAfterHostMaintainance(cloudstackTestCase): +class TestEgressAfterHostMaintenance(cloudstackTestCase): def setUp(self): @@ -2171,7 +2171,7 @@ class TestEgressAfterHostMaintainance(cloudstackTestCase): def setUpClass(cls): cls.services = Services().services cls.api_client = super( - TestEgressAfterHostMaintainance, + TestEgressAfterHostMaintenance, cls ).getClsTestClient().getApiClient() @@ -2222,7 +2222,7 @@ class TestEgressAfterHostMaintainance(cloudstackTestCase): @attr(speed = "slow") @attr(tags = ["sg", "eip", "maintenance"]) - def test_egress_after_host_maintainance(self): + def test_egress_after_host_maintenance(self): """Test maintenance case for egress """ @@ -2234,7 +2234,7 @@ class TestEgressAfterHostMaintainance(cloudstackTestCase): # CIDR: 0.0.0.0/0 # 5. deployVirtualMachine into this security group (ssh) # 6. deployed VM should be Running, ssh should be allowed into the VM - # 7. Enable maintainance mode for host, cance maintainance mode + # 7. Enable maintenance mode for host, cance maintenance mode # 8. User should be able to SSH into VM after maintainace security_group = SecurityGroup.create( @@ -2329,12 +2329,12 @@ class TestEgressAfterHostMaintainance(cloudstackTestCase): ) vm = vms[0] - self.debug("Enabling host maintainance for ID: %s" % vm.hostid) + self.debug("Enabling host maintenance for ID: %s" % vm.hostid) cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = vm.hostid self.apiclient.prepareHostForMaintenance(cmd) - self.debug("Canceling host maintainance for ID: %s" % vm.hostid) + self.debug("Canceling host maintenance for ID: %s" % vm.hostid) cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() cmd.id = vm.hostid self.apiclient.cancelHostMaintenance(cmd) diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index df8140685e6..0d52224c535 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -1803,7 +1803,7 @@ class Host: return def enableMaintenance(self, apiclient): - """enables maintainance mode Host""" + """enables maintenance mode Host""" cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = self.id @@ -1811,14 +1811,14 @@ class Host: @classmethod def enableMaintenance(cls, apiclient, id): - """enables maintainance mode Host""" + """enables maintenance mode Host""" cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = id return apiclient.prepareHostForMaintenance(cmd) def cancelMaintenance(self, apiclient): - """Cancels maintainance mode Host""" + """Cancels maintenance mode Host""" cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() cmd.id = self.id @@ -1826,7 +1826,7 @@ class Host: @classmethod def cancelMaintenance(cls, apiclient, id): - """Cancels maintainance mode Host""" + """Cancels maintenance mode Host""" cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() cmd.id = id @@ -1895,7 +1895,7 @@ class StoragePool: return def enableMaintenance(self, apiclient): - """enables maintainance mode Storage pool""" + """enables maintenance mode Storage pool""" cmd = enableStorageMaintenance.enableStorageMaintenanceCmd() cmd.id = self.id diff --git a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/base.py b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/base.py index 0b5da5c162f..7c8546c092c 100644 --- a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/base.py +++ b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/base.py @@ -1053,8 +1053,8 @@ class Host: return def enableMaintenance(self, apiclient): - """enables maintainance mode Host""" - + """enables maintenance mode Host""" + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = self.id return apiclient.prepareHostForMaintenance(cmd) @@ -1113,8 +1113,8 @@ class StoragePool: return def enableMaintenance(self, apiclient): - """enables maintainance mode Storage pool""" - + """enables maintenance mode Storage pool""" + cmd = enableStorageMaintenance.enableStorageMaintenanceCmd() cmd.id = self.id return apiclient.enableStorageMaintenance(cmd) From 07a6daf6cb8d096ee661362d57632302cbf12422 Mon Sep 17 00:00:00 2001 From: SrikanteswaraRao Talluri Date: Mon, 23 Sep 2013 14:28:24 +0530 Subject: [PATCH 244/251] CLOUDSTACK-4646: Increase the wait time for the routers to go into stopped state Signed-off-by: venkataswamybabu budumuru (cherry picked from commit 55ef4b8c3c6d583c7e4de4b11613db718c590b82) --- test/integration/component/test_redundant_router_cleanups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/component/test_redundant_router_cleanups.py b/test/integration/component/test_redundant_router_cleanups.py index 303ca8b8da6..e30c1020243 100644 --- a/test/integration/component/test_redundant_router_cleanups.py +++ b/test/integration/component/test_redundant_router_cleanups.py @@ -653,7 +653,7 @@ class TestRedundantRouterNetworkCleanups(cloudstackTestCase): self.debug("Sleeping for network gc wait + interval time") # Sleep to ensure that all resources are deleted - time.sleep((delay + exp) * 2) + time.sleep((delay + exp) * 3) routers = Router.list( self.apiclient, From 503fe75dc93be209f0382ddf9c0b548009342565 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Wed, 18 Sep 2013 20:50:37 -0400 Subject: [PATCH 245/251] CLOUDSTACK: 4706 - Adding missing method get_region in common.py Signed-off-by: venkataswamybabu budumuru --- tools/marvin/marvin/integration/lib/common.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index e8958850ce5..164ef2052dd 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -94,6 +94,22 @@ def add_netscaler(apiclient, zoneid, NSservice): return netscaler +def get_region(apiclient, services=None): + "Returns a default region" + + cmd = listRegions.listRegionsCmd() + if services: + if "regionid" in services: + cmd.id = services["regionid"] + + regions = apiclient.listRegions(cmd) + + if isinstance(regions, list): + assert len(regions) > 0 + return regions[0] + else: + raise Exception("Failed to find specified region.") + def get_domain(apiclient, services=None): "Returns a default domain" From dfc0180477e4bf405b54c8362413a0db0a80c8b6 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 30 Sep 2013 11:10:09 -0700 Subject: [PATCH 246/251] CLOUDSTACK-4767: UI > Intances > Attach ISO action - pass zoneId to listIsos API --- ui/scripts/instances.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index a9f6d1c8c5a..f4507027a7c 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -825,10 +825,14 @@ label: 'ISO', select: function(args) { var items = []; - var map = {}; + var map = {}; $.ajax({ - url: createURL("listIsos&isReady=true&isofilter=featured"), - dataType: "json", + url: createURL("listIsos"), + data: { + isofilter: 'featured', + isReady: true, + zoneid: args.context.instances[0].zoneid + }, async: false, success: function(json) { var isos = json.listisosresponse.iso; @@ -842,8 +846,12 @@ } }); $.ajax({ - url: createURL("listIsos&isReady=true&isofilter=community"), - dataType: "json", + url: createURL("listIsos"), + data: { + isofilter: 'community', + isReady: true, + zoneid: args.context.instances[0].zoneid + }, async: false, success: function(json) { var isos = json.listisosresponse.iso; @@ -859,8 +867,12 @@ } }); $.ajax({ - url: createURL("listIsos&isReady=true&isofilter=selfexecutable"), - dataType: "json", + url: createURL("listIsos"), + data: { + isofilter: 'selfexecutable', + isReady: true, + zoneid: args.context.instances[0].zoneid + }, async: false, success: function(json) { var isos = json.listisosresponse.iso; From f0b4f894695939270a4415352070e33aff02763b Mon Sep 17 00:00:00 2001 From: Sowmya Krishnan Date: Wed, 18 Sep 2013 11:43:11 +0530 Subject: [PATCH 247/251] CLOUDSTACK-4696 Handle Netscaler Provider enable for both zones Signed-off-by: venkataswamybabu budumuru (cherry picked from commit a5ede3af7f5837f9e8a1fb80e2a1f20f24ecb870) --- .../component/test_netscaler_nw_off.py | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/test/integration/component/test_netscaler_nw_off.py b/test/integration/component/test_netscaler_nw_off.py index cb49dbe258d..3139257dd67 100644 --- a/test/integration/component/test_netscaler_nw_off.py +++ b/test/integration/component/test_netscaler_nw_off.py @@ -408,13 +408,12 @@ class TestAddMultipleNSDiffZone(cloudstackTestCase): for zone in zones: if zone.networktype == 'Advanced': zone_list.append(zone) - self.assertGreater( len(zone_list), 1, "Atleast 2 advanced mode zones should be present for this test" ) - + zoneid=zone_list[0].id physical_networks = PhysicalNetwork.list( self.apiclient, zoneid=zone_list[0].id @@ -424,43 +423,12 @@ class TestAddMultipleNSDiffZone(cloudstackTestCase): True, "There should be atleast one physical network for advanced zone" ) - physical_network = physical_networks[0] self.debug("Adding netscaler device: %s" % self.services["netscaler_1"]["ipaddress"]) - netscaler_1 = NetScaler.add( - self.apiclient, - self.services["netscaler_1"], - physicalnetworkid=physical_network.id - ) + netscaler_1 = add_netscaler(self.apiclient, zoneid, self.services["netscaler_1"]) self.cleanup.append(netscaler_1) - self.debug("Checking if Netscaler network service provider is enabled?") - - nw_service_providers = NetworkServiceProvider.list( - self.apiclient, - name='Netscaler', - physicalnetworkid=physical_network.id - ) - self.assertEqual( - isinstance(nw_service_providers, list), - True, - "Network service providers list should not be empty" - ) - netscaler_provider = nw_service_providers[0] - if netscaler_provider.state != 'Enabled': - self.debug("Netscaler provider is not enabled. Enabling it..") - response = NetworkServiceProvider.update( - self.apiclient, - id=netscaler_provider.id, - state='Enabled' - ) - self.assertEqual( - response.state, - "Enabled", - "Network service provider should be in enabled state" - ) - else: - self.debug("Netscaler service provider is already enabled.") + physical_network = physical_networks[0] ns_list = NetScaler.list( self.apiclient, lbdeviceid=netscaler_1.lbdeviceid @@ -492,6 +460,7 @@ class TestAddMultipleNSDiffZone(cloudstackTestCase): self.apiclient, zoneid=zone_list[1].id ) + zoneid=zone_list[1].id self.assertEqual( isinstance(physical_networks, list), True, @@ -501,11 +470,7 @@ class TestAddMultipleNSDiffZone(cloudstackTestCase): self.debug("Adding netscaler device: %s" % self.services["netscaler_2"]["ipaddress"]) - netscaler_2 = NetScaler.add( - self.apiclient, - self.services["netscaler_2"], - physicalnetworkid=physical_network.id - ) + netscaler_2 = add_netscaler(self.apiclient, zoneid, self.services["netscaler_2"]) self.cleanup.append(netscaler_2) ns_list = NetScaler.list( self.apiclient, From 7d15b50704b3b177d2b39fe55e30a9bba0f7b40a Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Tue, 10 Sep 2013 00:25:21 -0400 Subject: [PATCH 248/251] CLOUDSTACK-4634: Fixed issue arising due to wrong indentation --- .../cpu_limits/test_maximum_limits.py | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/integration/component/cpu_limits/test_maximum_limits.py b/test/integration/component/cpu_limits/test_maximum_limits.py index 23025044777..9161ceeeedb 100644 --- a/test/integration/component/cpu_limits/test_maximum_limits.py +++ b/test/integration/component/cpu_limits/test_maximum_limits.py @@ -137,34 +137,34 @@ class TestMaxCPULimits(cloudstackTestCase): if api_client is None: api_client = self.apiclient - self.debug("Deploying instance") - try: - if account: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=account.name, - domainid=account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - elif project: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - projectid=project.id, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.debug("Deploying instance") + try: + if account: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + elif project: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", + "Vm state should be running after deployment") + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self, account_limit=2, domain_limit=2, project_limit=2): From b35e76132d280496c1c32bdfb371cb993829b426 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Tue, 10 Sep 2013 01:42:50 -0400 Subject: [PATCH 249/251] CLOUDSTACK: 4635 - Fixed test case issues due to wrong indentation --- .../cpu_limits/test_domain_limits.py | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/test/integration/component/cpu_limits/test_domain_limits.py b/test/integration/component/cpu_limits/test_domain_limits.py index 2668204361f..4e8fc6d7d7e 100644 --- a/test/integration/component/cpu_limits/test_domain_limits.py +++ b/test/integration/component/cpu_limits/test_domain_limits.py @@ -142,26 +142,26 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): if api_client is None: api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % + self.debug("Deploying an instance in account: %s" % self.account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=self.account.name, - domainid=self.account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, - list, - "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, + list, + "List VMs should return a valid response") + self.assertEqual(vms[0].state, "Running", "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): @@ -547,26 +547,26 @@ class TestMultipleChildDomains(cloudstackTestCase): if api_client is None: api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % + self.debug("Deploying an instance in account: %s" % account.name) - try: - vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - accountid=account.name, - domainid=account.domainid, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, + try: + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=account.name, + domainid=account.domainid, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, list, "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", + self.assertEqual(vms[0].state, "Running", "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): From 219c64eef38ca1b44724abd299c79a9eb508bb73 Mon Sep 17 00:00:00 2001 From: Gaurav Aradhye Date: Tue, 10 Sep 2013 03:00:10 -0400 Subject: [PATCH 250/251] CLOUDSTACK: 4640 - Fixed indentation issues in function createInstance --- .../component/cpu_limits/test_cpu_limits.py | 36 +++++++++---------- .../cpu_limits/test_project_limits.py | 28 +++++++-------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/integration/component/cpu_limits/test_cpu_limits.py b/test/integration/component/cpu_limits/test_cpu_limits.py index 8acf8b7036a..d721a456b53 100644 --- a/test/integration/component/cpu_limits/test_cpu_limits.py +++ b/test/integration/component/cpu_limits/test_cpu_limits.py @@ -154,10 +154,10 @@ class TestCPULimits(cloudstackTestCase): if api_client is None: api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % + self.debug("Deploying an instance in account: %s" % self.account.name) - try: - vm = VirtualMachine.create( + try: + vm = VirtualMachine.create( api_client, self.services["virtual_machine"], templateid=self.template.id, @@ -165,15 +165,15 @@ class TestCPULimits(cloudstackTestCase): domainid=self.account.domainid, networkids=networks, serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, list, "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", + self.assertEqual(vms[0].state, "Running", "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) @attr(tags=["advanced", "advancedns","simulator"]) def test_01_multiplecore_start_stop_instance(self): @@ -401,10 +401,10 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): if api_client is None: api_client = self.apiclient - self.debug("Deploying an instance in account: %s" % + self.debug("Deploying an instance in account: %s" % self.account.name) - try: - vm = VirtualMachine.create( + try: + vm = VirtualMachine.create( api_client, self.services["virtual_machine"], templateid=self.template.id, @@ -412,15 +412,15 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): domainid=self.account.domainid, networkids=networks, serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=vm.id, listall=True) - self.assertIsInstance(vms, + vms = VirtualMachine.list(api_client, id=vm.id, listall=True) + self.assertIsInstance(vms, list, "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", + self.assertEqual(vms[0].state, "Running", "Vm state should be running after deployment") - return vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + return vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): diff --git a/test/integration/component/cpu_limits/test_project_limits.py b/test/integration/component/cpu_limits/test_project_limits.py index 3c432db7db7..63d1a986356 100644 --- a/test/integration/component/cpu_limits/test_project_limits.py +++ b/test/integration/component/cpu_limits/test_project_limits.py @@ -162,23 +162,23 @@ class TestProjectsCPULimits(cloudstackTestCase): if api_client is None: api_client = self.api_client - try: - self.vm = VirtualMachine.create( - api_client, - self.services["virtual_machine"], - templateid=self.template.id, - projectid=project.id, - networkids=networks, - serviceofferingid=service_off.id) - vms = VirtualMachine.list(api_client, id=self.vm.id, listall=True) - self.assertIsInstance(vms, + try: + self.vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=project.id, + networkids=networks, + serviceofferingid=service_off.id) + vms = VirtualMachine.list(api_client, id=self.vm.id, listall=True) + self.assertIsInstance(vms, list, "List VMs should return a valid response") - self.assertEqual(vms[0].state, "Running", + self.assertEqual(vms[0].state, "Running", "Vm state should be running after deployment") - return self.vm - except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + return self.vm + except Exception as e: + self.fail("Failed to deploy an instance: %s" % e) def setupProjectAccounts(self): From 4533d2d88fa22ac05633df10dc5a1a2c157f3490 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Wed, 2 Oct 2013 17:52:39 -0700 Subject: [PATCH 251/251] CLOUDSTACK-4790: Skip reserved VMware scsi device number for SCSI disks --- .../vmware/resource/VmwareResource.java | 3 +- .../vmware/mo/VirtualMachineMO.java | 15 +++++-- .../hypervisor/vmware/util/VmwareHelper.java | 44 ++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 70b9f545b3d..de2eb9bebc5 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -2857,7 +2857,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String[] diskChain = syncDiskChain(dcMo, vmMo, vmSpec, vol, matchingExistingDisk, dataStoresDetails); - + if(controllerKey == scsiControllerKey && VmwareHelper.isReservedScsiDeviceNumber(scsiUnitNumber)) + scsiUnitNumber++; device = VmwareHelper.prepareDiskDevice(vmMo, null, controllerKey, diskChain, volumeDsDetails.first(), diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index f64c3c0d1e8..abc5bf8a8bf 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -908,8 +908,9 @@ public class VirtualMachineMO extends BaseMO { assert(vmdkDatastorePath != null); assert(morDs != null); + int ideControllerKey = getIDEDeviceControllerKey(); if(controllerKey < 0) { - controllerKey = getIDEDeviceControllerKey(); + controllerKey = ideControllerKey; } VirtualDisk newDisk = new VirtualDisk(); @@ -952,6 +953,8 @@ public class VirtualMachineMO extends BaseMO { } int deviceNumber = getNextDeviceNumber(controllerKey); + if(controllerKey != ideControllerKey && VmwareHelper.isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; newDisk.setControllerKey(controllerKey); newDisk.setKey(-deviceNumber); @@ -1714,9 +1717,13 @@ public class VirtualMachineMO extends BaseMO { public int getNextScsiDiskDeviceNumber() throws Exception { int scsiControllerKey = getScsiDeviceControllerKey(); - return getNextDeviceNumber(scsiControllerKey); + int deviceNumber = getNextDeviceNumber(scsiControllerKey); + if(VmwareHelper.isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; + + return deviceNumber; } - + public int getScsiDeviceControllerKey() throws Exception { List devices = (List)_context.getVimClient(). getDynamicProperty(_mor, "config.hardware.device"); @@ -1732,7 +1739,7 @@ public class VirtualMachineMO extends BaseMO { assert(false); throw new Exception("SCSI Controller Not Found"); } - + public int getScsiDeviceControllerKeyNoException() throws Exception { List devices = (List)_context.getVimClient(). getDynamicProperty(_mor, "config.hardware.device"); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java index bcf9f1419b3..1927b9fa841 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java @@ -72,6 +72,10 @@ import com.cloud.utils.exception.ExceptionUtil; public class VmwareHelper { private static final Logger s_logger = Logger.getLogger(VmwareHelper.class); + + public static boolean isReservedScsiDeviceNumber(int deviceNumber) { + return deviceNumber == 7; + } public static VirtualDevice prepareNicDevice(VirtualMachineMO vmMo, ManagedObjectReference morNetwork, VirtualEthernetCardType deviceType, String portGroupName, String macAddress, int deviceNumber, int contextNumber, boolean conntected, boolean connectOnStart) throws Exception { @@ -178,11 +182,15 @@ public class VmwareHelper { backingInfo.setFileName(vmdkDatastorePath); disk.setBacking(backingInfo); + int ideControllerKey = vmMo.getIDEDeviceControllerKey(); if(controllerKey < 0) - controllerKey = vmMo.getIDEDeviceControllerKey(); - if(deviceNumber < 0) + controllerKey = ideControllerKey; + if(deviceNumber < 0) { deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - disk.setControllerKey(controllerKey); + if(controllerKey != ideControllerKey && isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; + } + disk.setControllerKey(controllerKey); disk.setKey(-contextNumber); disk.setUnitNumber(deviceNumber); @@ -246,12 +254,16 @@ public class VmwareHelper { throw new Exception("Unsupported disk backing: " + parentBacking.getClass().getCanonicalName()); } + int ideControllerKey = vmMo.getIDEDeviceControllerKey(); if(controllerKey < 0) - controllerKey = vmMo.getIDEDeviceControllerKey(); + controllerKey = ideControllerKey; disk.setControllerKey(controllerKey); - if(deviceNumber < 0) + if(deviceNumber < 0) { deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - + if(controllerKey != ideControllerKey && isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; + } + disk.setKey(-contextNumber); disk.setUnitNumber(deviceNumber); disk.setCapacityInKB(sizeInMb*1024); @@ -282,11 +294,15 @@ public class VmwareHelper { backingInfo.setDiskMode(VirtualDiskMode.PERSISTENT.value()); disk.setBacking(backingInfo); + int ideControllerKey = vmMo.getIDEDeviceControllerKey(); if(controllerKey < 0) - controllerKey = vmMo.getIDEDeviceControllerKey(); - if(deviceNumber < 0) + controllerKey = ideControllerKey; + if(deviceNumber < 0) { deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - + if(controllerKey != ideControllerKey && isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; + } + disk.setControllerKey(controllerKey); disk.setKey(-contextNumber); disk.setUnitNumber(deviceNumber); @@ -332,11 +348,15 @@ public class VmwareHelper { disk.setBacking(backingInfo); + int ideControllerKey = vmMo.getIDEDeviceControllerKey(); if(controllerKey < 0) - controllerKey = vmMo.getIDEDeviceControllerKey(); - if(deviceNumber < 0) + controllerKey = ideControllerKey; + if(deviceNumber < 0) { deviceNumber = vmMo.getNextDeviceNumber(controllerKey); - + if(controllerKey != ideControllerKey && isReservedScsiDeviceNumber(deviceNumber)) + deviceNumber++; + } + disk.setControllerKey(controllerKey); disk.setKey(-contextNumber); disk.setUnitNumber(deviceNumber);