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/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/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/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()); } } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index d4e55f7372c..492661ef409 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -617,7 +617,8 @@ listUcsManagers=1 listUcsProfiles=1 listUcsBlades=1 associateUcsProfileToBlade=1 -removedeleteUcsManager=1 +deleteUcsManager=1 +disassociateUcsProfileFromBlade=1 #### New Load Balancer commands createLoadBalancer=15 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 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/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 e88b2265370..e4c51fb5207 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-agent-upgrade + install -D agent/target/transformed/libvirtqemuhook $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent # cloudstack-management @@ -145,7 +147,8 @@ install: mkdir $(DESTDIR)/usr/share/$(PACKAGE)-usage/plugins install -D usage/target/cloud-usage-$(VERSION).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 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/change-affinity-button.png b/docs/en-US/images/change-affinity-button.png new file mode 100644 index 00000000000..c21ef758dc2 Binary files /dev/null and b/docs/en-US/images/change-affinity-button.png differ 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 00000000000..0ac38e00eca Binary files /dev/null and b/docs/en-US/images/dedicate-resource-button.png differ 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 00000000000..16cda947fdb Binary files /dev/null and b/docs/en-US/images/edit-traffic-type.png differ diff --git a/docs/en-US/images/plugin1.jpg b/docs/en-US/images/plugin1.jpg new file mode 100644 index 00000000000..970233d8475 Binary files /dev/null and b/docs/en-US/images/plugin1.jpg differ diff --git a/docs/en-US/images/plugin2.jpg b/docs/en-US/images/plugin2.jpg new file mode 100644 index 00000000000..9c8a6107ba9 Binary files /dev/null and b/docs/en-US/images/plugin2.jpg differ diff --git a/docs/en-US/images/plugin3.jpg b/docs/en-US/images/plugin3.jpg new file mode 100644 index 00000000000..07fae790e22 Binary files /dev/null and b/docs/en-US/images/plugin3.jpg differ diff --git a/docs/en-US/images/plugin4.jpg b/docs/en-US/images/plugin4.jpg new file mode 100644 index 00000000000..2bcec9f773a Binary files /dev/null and b/docs/en-US/images/plugin4.jpg differ diff --git a/docs/en-US/images/plugin_intro.jpg b/docs/en-US/images/plugin_intro.jpg new file mode 100644 index 00000000000..113ffb32781 Binary files /dev/null and b/docs/en-US/images/plugin_intro.jpg differ diff --git a/docs/en-US/images/traffic-type.png b/docs/en-US/images/traffic-type.png new file mode 100644 index 00000000000..10d5ddb25ed Binary files /dev/null and b/docs/en-US/images/traffic-type.png differ 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 00000000000..bce270bf258 Binary files /dev/null and b/docs/en-US/images/view-systemvm-details.png differ diff --git a/docs/en-US/images/workloads.png b/docs/en-US/images/workloads.png new file mode 100644 index 00000000000..a8334d97546 Binary files /dev/null and b/docs/en-US/images/workloads.png differ diff --git a/docs/en-US/third-party-ui-plugin.xml b/docs/en-US/third-party-ui-plugin.xml new file mode 100644 index 00000000000..297fdaa857f --- /dev/null +++ b/docs/en-US/third-party-ui-plugin.xml @@ -0,0 +1,364 @@ + + +%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. + + +
+
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 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/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/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/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 09d69f3cec1..cd4a9593ef4 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); @@ -115,6 +115,7 @@ public class Upgrade410to420 implements DbUpgrade { migrateVolumeOnSecondaryStorage(conn); createFullCloneFlag(conn); upgradeVpcServiceMap(conn); + upgradeResourceCount(conn); } private void createFullCloneFlag(Connection conn) { @@ -899,7 +900,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; @@ -908,7 +909,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=?"); @@ -927,7 +928,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(); @@ -2239,6 +2240,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(); @@ -3007,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(); + } } 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..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); } @@ -439,6 +441,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 (needCache && srcData != null) { + cacheMgr.deleteCacheObject(srcData); + } return answer; } 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); 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/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 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..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); } @@ -577,12 +582,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; } @@ -1297,22 +1302,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; 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 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" 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 } 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 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..5c4c855a957 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 @@ -147,18 +147,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/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..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()); @@ -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; } 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..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 { @@ -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); diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml index 3d172870d42..2ce5b72f2df 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..4000e987683 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")) { @@ -361,9 +375,9 @@ 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 (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; + } +} 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..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 @@ -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,54 @@ 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; + } + + // 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; + } @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..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 @@ -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); @@ -75,14 +80,22 @@ 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); 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 26ebaa06f3e..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 @@ -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; @@ -2863,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(), @@ -4549,7 +4544,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"); @@ -5704,7 +5699,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"); } @@ -5761,7 +5756,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"); } @@ -5796,34 +5791,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() { } @@ -5850,70 +5817,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."); @@ -6694,6 +6644,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/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/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..05ffec96325 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(); @@ -658,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(); @@ -982,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/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/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); 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..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 @@ -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()); @@ -4814,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(); @@ -4868,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); } } @@ -7788,37 +7776,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 +7841,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/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"); 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 { 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. + } } 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") 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]: 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: 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/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 879e74da271..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); } @@ -1571,7 +1572,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 @@ -2508,7 +2509,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 +2538,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 +2549,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 +2589,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 +2618,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()); } } diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 241c0738bc0..7c16cc06382 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; @@ -166,35 +165,25 @@ 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()) + "%"); - 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()); } } } 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/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())); diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index b73aad21893..6d36a070291 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; @@ -1052,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) { @@ -1076,11 +1083,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 +1108,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"); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index fe5f8aa1062..df3dfeacbb4 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); } @@ -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); } } @@ -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); @@ -3779,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); } @@ -3803,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()); 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(); 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())); 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; } diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 1d6b44fd7db..232b640afd6 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); @@ -2638,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; } } 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; } 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); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 73d46028e29..812f8384aba 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(); @@ -4879,6 +4877,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 +} 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..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"); @@ -1775,7 +1791,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 +1830,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 +1840,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); } 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 diff --git a/test/integration/component/cpu_limits/test_cpu_limits.py b/test/integration/component/cpu_limits/test_cpu_limits.py index 4c8e8e6a149..d721a456b53 100644 --- a/test/integration/component/cpu_limits/test_cpu_limits.py +++ b/test/integration/component/cpu_limits/test_cpu_limits.py @@ -5,9 +5,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,13 +24,13 @@ from marvin.integration.lib.base import ( Account, ServiceOffering, VirtualMachine, - Domain + Domain, + Resources ) from marvin.integration.lib.common import (get_domain, get_zone, get_template, cleanup_resources, - get_updated_resource_count, find_suitable_host, get_resource_type ) @@ -96,9 +96,9 @@ class TestCPULimits(cloudstackTestCase): 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.zone = get_zone(cls.api_client, cls.services) cls.services["mode"] = cls.zone.networktype - + cls.template = get_template( cls.api_client, cls.zone.id, @@ -106,11 +106,11 @@ class TestCPULimits(cloudstackTestCase): ) 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 @@ -149,44 +149,53 @@ class TestCPULimits(cloudstackTestCase): 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 + """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) + 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) + 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") + list, + "List VMs should return a valid response") self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") + "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.fail("Failed to deploy an instance: %s" % e) - @attr(tags=["advanced", "advancedns","simulator"]) - def test_01_multiplecore_reboot_instance(self): + @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 limit of Root Admin Account - # 3. Start VM & verify the update resource limit of Root Admin Account - # 4. Resource count should list properly. + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -194,8 +203,15 @@ class TestCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to stop instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -203,21 +219,37 @@ class TestCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to start instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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"]) + @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 update resource limit of Root Admin Account - # 3. Resource count should list properly. + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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)) @@ -225,22 +257,38 @@ class TestCPULimits(cloudstackTestCase): self.vm.migrate(self.apiclient, host.id) except Exception as e: self.fail("Failed to migrate instance: %s" % e) - - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + + 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"]) + @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 limit of Root Admin Account + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -248,23 +296,27 @@ class TestCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to delete instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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(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. Update Resource count for the root admin CPU usage + # 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 limit + # 5. Destroy one VM among multiple VM's and verify the resource count # 6. Migrate VM from & verify resource updates - # 7. List resources of Root Admin for CPU + # 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") @@ -279,31 +331,18 @@ class TestCPULimits(cloudstackTestCase): 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) + self.createInstance(service_off=self.service_offering) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(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) - - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(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) - - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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): @@ -357,214 +396,262 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): 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 + """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) + 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) + 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") + list, + "List VMs should return a valid response") self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") + "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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) + 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.domain = 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.admin = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=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) - # Cleanup the resources created at end of test - self.cleanup.append(self.admin) - self.cleanup.append(self.domain) - return @attr(tags=["advanced", "advancedns","simulator"]) - @attr(configuration='max.account.cpus') - def test_01_reboot_instance(self): + def test_01_stop_start_instance(self): """Test Deploy VM with 4 core CPU & verify the usage""" - #keep the configuration value - max.account.cpus number = 10 + # Validate the following # 1. Create compute offering with 4 core CPU & Deploy VM - # 2. Update Resource count CPU usage - # 3. Reboot instance, check resource count. + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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.debug("Stopping instance: %s" % vm.name) - try: - vm.stop(self.apiclient) - except Exception as e: - self.fail("Failed to stop instance: %s" % e) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + self.assertEqual(resource_count, expected_resource_count, + "Initial resource count should match with the expected resource count") - 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("Stopping instance: %s" % vm.name) + try: + vm.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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"]) - @attr(configuration='max.account.cpus') def test_02_migrate_instance(self): """Test Deploy VM with 4 core CPU & verify the usage""" - #keep the configuration value - max.account.cpus number = 10 + # Validate the following # 1. Create compute offering with 4 core CPU & Deploy VM - # 2. Update Resource count + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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 - 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) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(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) + + 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"]) - @attr(configuration='max.account.cpus') def test_03_delete_instance(self): """Test Deploy VM with 4 core CPU & verify the usage""" - #keep the configuration value - max.account.cpus number = 10 + # Validate the following # 1. Create compute offering with 4 core CPU & Deploy VM - # 2. Update Resource count for te CPU usage + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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.debug("Destroying instance: %s" % vm.name) - try: - vm.delete(self.apiclient) - except Exception as e: - self.fail("Failed to delete instance: %s" % e) + expected_resource_count = int(self.services["service_offering"]["cpunumber"]) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + 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') + @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 - # 3. Update Resource count for the root admin CPU usage + # 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") @@ -577,19 +664,28 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.debug("Setting up account and domain hierarchy") self.setupAccounts() - users = {self.domain: self.admin, - self.child_domain: self.child_do_admin + 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) + 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) + 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) @@ -599,8 +695,17 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): with self.assertRaises(Exception): self.createInstance(service_off=self.service_offering, api_client=api_client) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -608,8 +713,17 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): except Exception as e: self.fail("Failed to delete instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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, @@ -619,6 +733,13 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) - return + 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") diff --git a/test/integration/component/cpu_limits/test_domain_limits.py b/test/integration/component/cpu_limits/test_domain_limits.py index 05deaff6270..4e8fc6d7d7e 100644 --- a/test/integration/component/cpu_limits/test_domain_limits.py +++ b/test/integration/component/cpu_limits/test_domain_limits.py @@ -5,9 +5,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,7 +31,6 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, cleanup_resources, - get_updated_resource_count, find_suitable_host, get_resource_type ) @@ -140,89 +139,90 @@ 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) + 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) + 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") + list, + "List VMs should return a valid response") self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") + "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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"]) - def test_01_multiple_core_vm_reboot_instance(self): + @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 + # 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. @@ -236,16 +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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -253,8 +262,15 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): except Exception as e: self.fail("Failed to stop instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -262,20 +278,27 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): except Exception as e: self.fail("Failed to start instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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"]) + @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 + # 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. + # 5. Resource count should list properly. self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -286,39 +309,55 @@ 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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)) + (vm.name, host.name)) try: vm.migrate(self.apiclient, host.id) except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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"]) + @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 + # 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. + # 5. Resource count should list properly. self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -329,16 +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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -346,12 +394,18 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): except Exception as e: self.fail("Failed to delete instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) - 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_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"]) + @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""" @@ -378,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) @@ -393,8 +447,17 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): with self.assertRaises(Exception): self.createInstance(service_off=self.service_offering, api_client=api_client) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -402,8 +465,17 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): except Exception as e: self.fail("Failed to delete instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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, @@ -413,8 +485,15 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=8)#CPU - self.debug(resource_count) + 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): @@ -465,120 +544,120 @@ 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) + 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) + 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") + list, + "List VMs should return a valid response") self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") + "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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=4, - 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=2, - account=self.cadmin_2.name, - domainid=self.cadmin_2.domainid) + self.cadmin_1.name) + Resources.updateLimit(self.apiclient, + resourcetype=8, + max=2, + account=self.cadmin_1.name, + domainid=self.cadmin_1.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 - } + 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"]) + @attr(tags=["advanced", "advancedns","simulator"]) def test_01_multiple_child_domains(self): """Test CPU limits with multiple child domains""" @@ -589,7 +668,7 @@ class TestMultipleChildDomains(cloudstackTestCase): # 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 + # 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 @@ -605,41 +684,54 @@ 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) - resource_count_cadmin_1 = get_updated_resource_count(self.apiclient, account=self.cadmin_1, rtype=8)#CPU + + 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) - resource_count_cadmin_2 = get_updated_resource_count(self.apiclient, account=self.cadmin_2, rtype=8)#CPU + 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 parent domain") + "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") + "Creating instance when CPU limit is fully used in child domain 2") with self.assertRaises(Exception): - self.createInstance(account=self.cadmin_1, - service_off=self.service_offering, api_client=api_client_cadmin_1) + 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) @@ -648,12 +740,25 @@ class TestMultipleChildDomains(cloudstackTestCase): self.fail("Failed to delete instance: %s" % e) self.debug("Checking resource count for account: %s" % self.cadmin_1.name) - resource_count_cadmin_1 = get_updated_resource_count(self.apiclient, account=self.cadmin_1, rtype=8)#CPU + + 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.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) - resource_count_cadmin_2 = get_updated_resource_count(self.apiclient, account=self.cadmin_2, rtype=8)#CPU + 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 + 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 914ececaf91..9161ceeeedb 100644 --- a/test/integration/component/cpu_limits/test_maximum_limits.py +++ b/test/integration/component/cpu_limits/test_maximum_limits.py @@ -5,9 +5,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,8 +31,7 @@ from marvin.integration.lib.base import ( from marvin.integration.lib.common import (get_domain, get_zone, get_template, - cleanup_resources, - get_updated_resource_count + cleanup_resources ) class Services: @@ -135,28 +134,28 @@ 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) + 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) + 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, @@ -165,67 +164,66 @@ class TestMaxCPULimits(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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 @@ -241,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"]) @@ -273,35 +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) - self.debug("Deploying instance with account: %s" % - self.child_do_admin.name) + 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) + service_off=self.service_offering, api_client=api_client_admin) - resource_count = get_updated_resource_count(self.apiclient, account=self.child_do_admin, rtype=8)#CPU - self.debug(resource_count) - - self.debug("Deploying instance when CPU limit is reached in account") + 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"]) @@ -316,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"]) @@ -350,32 +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) - - resource_count = get_updated_resource_count(self.apiclient, account=self.child_do_admin, rtype=8)#CPU - self.debug(resource_count) + 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 c4ae1e9e726..63d1a986356 100644 --- a/test/integration/component/cpu_limits/test_project_limits.py +++ b/test/integration/component/cpu_limits/test_project_limits.py @@ -5,9 +5,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,7 +31,6 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, cleanup_resources, - get_updated_resource_count, find_suitable_host, get_resource_type ) @@ -109,7 +108,7 @@ class TestProjectsCPULimits(cloudstackTestCase): cls.service_offering = ServiceOffering.create( cls.api_client, cls.services["service_offering"] - ) + ) cls._cleanup = [cls.service_offering, ] return @@ -137,15 +136,15 @@ 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 def tearDown(self): @@ -160,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) + 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") + list, + "List VMs should return a valid response") self.assertEqual(vms[0].state, "Running", - "Vm state should be running after deployment") + "Vm state should be running after deployment") return self.vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.fail("Failed to deploy an instance: %s" % e) def setupProjectAccounts(self): @@ -217,20 +216,26 @@ class TestProjectsCPULimits(cloudstackTestCase): return @attr(tags=["advanced", "advancedns","simulator"]) - @attr(configuration='max.projects.cpus') - def test_01_project_counts_reboot_instance(self): - """Test max.projects.cpus global configuration""" + def test_01_project_counts_start_stop_instance(self): # Validate the following - # 1. Set (max.project.cpus=10) as the max limit to - # Domain1 (max.account.cpus=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Stop VM of an accounts added to the project. - # 5. Resource count should list properly. + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -238,8 +243,15 @@ class TestProjectsCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to stop instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -247,25 +259,37 @@ class TestProjectsCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to start instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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"]) - @attr(configuration='max.projects.cpus') def test_02_project_counts_migrate_instance(self): - """Test max.projects.cpus global configuration""" # Validate the following - # 1. Set (max.project.cpus=10) as the max limit to - # Domain1 (max.account.cpus=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Migrate VM of an accounts added to the project to a new host - # 5. Resource count should list properly. + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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" % @@ -275,25 +299,37 @@ class TestProjectsCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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"]) - @attr(configuration='max.projects.cpus') def test_03_project_counts_delete_instance(self): - """Test max.projects.cpus global configuration""" # Validate the following - # 1. Set (max.project.cpus=10) as the max limit to - # Domain1 (max.account.cpus=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Destroy VM of an accounts added to the project to a new host - # 5. Resource count should list properly. + # 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. - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) + 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: @@ -301,7 +337,11 @@ class TestProjectsCPULimits(cloudstackTestCase): except Exception as e: self.fail("Failed to delete instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=8)#CPU - self.debug(resource_count) - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=8))#CPU + 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/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/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 + diff --git a/test/integration/component/memory_limits/test_domain_limits.py b/test/integration/component/memory_limits/test_domain_limits.py index ee997f37151..479ec0ba3e9 100644 --- a/test/integration/component/memory_limits/test_domain_limits.py +++ b/test/integration/component/memory_limits/test_domain_limits.py @@ -30,8 +30,7 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, cleanup_resources, - wait_for_cleanup, - get_updated_resource_count, + wait_for_cleanup, find_suitable_host, get_resource_type ) @@ -97,7 +96,7 @@ class TestDomainMemoryLimits(cloudstackTestCase): # 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.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, @@ -127,7 +126,7 @@ class TestDomainMemoryLimits(cloudstackTestCase): def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() - + self.cleanup = [] return @@ -144,8 +143,8 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug("Deploying an instance in account: %s" % self.account.name) - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient try: vm = VirtualMachine.create( api_client, @@ -163,63 +162,60 @@ class TestDomainMemoryLimits(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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, + self.child_domain_1 = Domain.create(self.apiclient, services=self.services["domain"], parentdomainid=self.domain.id) - self.child_do_admin = Account.create( + self.child_do_admin_1 = Account.create( self.apiclient, self.services["account"], admin=True, - domainid=self.child_domain.id + domainid=self.child_domain_1.id ) # Cleanup the resources created at end of test - self.cleanup.append(self.child_do_admin) - self.cleanup.append(self.child_domain) + self.cleanup.append(self.child_do_admin_1) + self.cleanup.append(self.child_domain_1) Resources.updateLimit(self.apiclient, resourcetype=9, - max=5120, - account=self.child_do_admin.name, - domainid=self.child_do_admin.domainid) - - self.domain = Domain.create(self.apiclient, + 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.admin = Account.create( + self.child_do_admin_2 = Account.create( self.apiclient, self.services["account"], admin=True, - domainid=self.domain.id) - + domainid=self.child_domain_2.id) + # Cleanup the resources created at end of test - self.cleanup.append(self.admin) - self.cleanup.append(self.domain) + self.cleanup.append(self.child_do_admin_2) + self.cleanup.append(self.child_domain_2) Resources.updateLimit(self.apiclient, resourcetype=9, - max=5120, - account=self.admin.name, - domainid=self.admin.domainid) + max=15360, + account=self.child_do_admin_2.name, + domainid=self.child_do_admin_2.domainid) return @attr(tags=["advanced", "advancedns","simulator"]) - @attr(configuration='max.account.memory') def test_01_change_service_offering(self): """Test Deploy VM with 5 GB RAM & verify the usage""" - #Keep max account memory (configuration) as 5 GB + # Validate the following # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain - # 2. Update Resource count for the root admin Memory usage + # 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 - - #Keep max account memory (configuration) as 5 GB + # 4. Resource count should list properly for the domain self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -232,21 +228,38 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug("Creating an instance with service offering: %s" % self.service_offering.name) - api_client = self.testClient.createUserApiClient( + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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 @@ -269,8 +282,18 @@ class TestDomainMemoryLimits(cloudstackTestCase): except Exception as e: self.fail("Failed to change service offering of vm %s - %s" % (vm.name, e)) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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" % @@ -284,30 +307,43 @@ class TestDomainMemoryLimits(cloudstackTestCase): except Exception as e: self.fail("Failed to change service offering of vm %s - %s" % (vm.name, e)) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - + 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"]) - @attr(configuration='max.account.memory') def test_02_migrate_vm(self): """Test Deploy VM with 5 GB RAM & verify the usage""" - #Keep max account memory (configuration) as 5 GB + # Validate the following # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain - # 2. Update Resource count for the root admin Memory usage + # 2. List Resource count for the root admin Memory usage # 3. Migrate vm to another host, resource count should list properly. - - #Keep max account memory (configuration) as 5 GB + self.debug("Setting up account and domain hierarchy") self.setupAccounts() users = { self.domain: self.admin, @@ -319,14 +355,23 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug("Creating an instance with service offering: %s" % self.service_offering.name) - api_client = self.testClient.createUserApiClient( + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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" % @@ -335,21 +380,25 @@ class TestDomainMemoryLimits(cloudstackTestCase): vm.migrate(self.apiclient, host.id) except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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"]) - @attr(configuration='max.account.memory') def test_03_delete_vm(self): """Test Deploy VM with 5 GB RAM & verify the usage""" - #Keep max account memory (configuration) as 5 GB + # Validate the following # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain - # 2. Update Resource count for the root admin Memory usage + # 2. List Resource count for the root admin Memory usage # 3. Delete vm, resource count should list as 0 after delete operation. - - #Keep max account memory (configuration) as 5 GB self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -362,14 +411,23 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug("Creating an instance with service offering: %s" % self.service_offering.name) - api_client = self.testClient.createUserApiClient( + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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: @@ -377,23 +435,26 @@ class TestDomainMemoryLimits(cloudstackTestCase): 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"]) + # Wait for expunge interval to cleanup Memory + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + 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"]) - @attr(configuration='max.account.memory') def test_04_deploy_multiple_vm(self): """Test Deploy multiple VM with 5 GB RAM & verify the usage""" - #Keep max account memory (configuration) as 5 GB + # Validate the following # 1. Create compute offering with 5 GB RAM # 2. Deploy multiple VMs with this service offering - # 3. Update Resource count for the root admin Memory usage + # 3. List Resource count for the root admin Memory usage # 4. Memory usage should list properly self.debug("Setting up account and domain hierarchy") @@ -407,39 +468,32 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug("Creating an instance with service offering: %s" % self.service_offering.name) - api_client = self.testClient.createUserApiClient( + 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) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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 - 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - vm_2.delete(self.apiclient) - vm_3.delete(self.apiclient) + 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 @@ -514,7 +568,7 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.fail("Failed to deploy an instance: %s" % e) def setupAccounts(self): @@ -528,7 +582,7 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): admin=True, domainid=self.domain.id ) - + self.debug("Updating the Memory resource count for domain: %s" % self.domain.name) Resources.updateLimit(self.apiclient, @@ -552,12 +606,12 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): 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, + max=5120, domainid=self.cadmin_1.domainid) self.debug("Updating the Memory resource count for account: %s" % @@ -579,7 +633,7 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.cdomain_2.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=5120, + max=5120, domainid=self.cadmin_2.domainid) self.debug("Updating the Memory resource count for domain: %s" % @@ -597,7 +651,7 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): 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, @@ -649,10 +703,20 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): vm_2 = self.createInstance(account=self.cadmin_2, service_off=self.service_offering, api_client=api_client_cadmin_2) - resource_count_cadmin_1 = get_updated_resource_count(self.apiclient, account=self.cadmin_1, rtype=9)#RAM + 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) - resource_count_cadmin_2 = get_updated_resource_count(self.apiclient, account=self.cadmin_2, rtype=9)#RAM + 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( @@ -664,8 +728,8 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.debug( "Creating instance when Memory limit is fully used in child domain") with self.assertRaises(Exception): - self.createInstance(account=self.cadmin_1, - service_off=self.service_offering, api_client=api_client_cadmin_1) + 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) @@ -675,15 +739,23 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.debug("Checking resource count for account: %s" % self.cadmin_1.name) - resource_count_cadmin_1 = get_updated_resource_count(self.apiclient, account=self.cadmin_1, rtype=9)#RAM - self.debug(resource_count_cadmin_1) + 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) - resource_count_cadmin_2 = get_updated_resource_count(self.apiclient, account=self.cadmin_2, rtype=9)#RAM - self.debug(resource_count_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_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 index 9aaadfffe70..b1ebbb429b6 100644 --- a/test/integration/component/memory_limits/test_maximum_limits.py +++ b/test/integration/component/memory_limits/test_maximum_limits.py @@ -94,7 +94,7 @@ class TestMaxMemoryLimits(cloudstackTestCase): # 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.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, @@ -124,14 +124,14 @@ class TestMaxMemoryLimits(cloudstackTestCase): def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() - self.account = Account.create( + 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 @@ -149,27 +149,20 @@ class TestMaxMemoryLimits(cloudstackTestCase): self.debug("Deploying an instance in account: %s" % self.account.name) - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient try: - if account: - vm = VirtualMachine.create( + 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, + 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, @@ -178,182 +171,182 @@ class TestMaxMemoryLimits(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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, + 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.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, + # 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 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 Memory resource count for domain: %s" % + 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, + # 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) + 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, + # 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) + 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, + # 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 - + 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 + """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" + # 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) + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=8, domain_limit=4) - api_client = self.testClient.createUserApiClient( + 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)) + 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, + with self.assertRaises(Exception): + self.createInstance(account=self.child_do_admin, service_off=self.service_offering, api_client=api_client) - return + 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 + """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" + # 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) + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=7, domain_limit=14) - api_client = self.testClient.createUserApiClient( + 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.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, + self.createInstance(account=self.child_do_admin, service_off=self.service_offering, api_client=api_client) - return + + 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""" + """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" + # 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) + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=8,domain_limit=8, project_limit=4) - api_client = self.testClient.createUserApiClient( + 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, + 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 + 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 + """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" + # 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) + self.debug("Setting up account and domain hierarchy") + self.setupAccounts(account_limit=6, project_limit=12, domain_limit=12) - api_client = self.testClient.createUserApiClient( + 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.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.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.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, + with self.assertRaises(Exception): + self.createInstance(project=self.project, account=self.child_do_admin, service_off=self.service_offering, api_client=api_client) - return + return diff --git a/test/integration/component/memory_limits/test_memory_limits.py b/test/integration/component/memory_limits/test_memory_limits.py index 3276407a4f6..18eab4c2383 100644 --- a/test/integration/component/memory_limits/test_memory_limits.py +++ b/test/integration/component/memory_limits/test_memory_limits.py @@ -31,9 +31,8 @@ from marvin.integration.lib.common import (get_domain, get_template, cleanup_resources, wait_for_cleanup, - get_updated_resource_count, find_suitable_host, - get_resource_type + get_resource_type ) class Services: @@ -97,7 +96,8 @@ class TestMemoryLimits(cloudstackTestCase): # 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.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, @@ -137,9 +137,6 @@ class TestMemoryLimits(cloudstackTestCase): self.service_offering.name) self.vm = self.createInstance(service_off=self.service_offering) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - self.cleanup = [self.account, ] return @@ -156,8 +153,8 @@ class TestMemoryLimits(cloudstackTestCase): self.debug("Deploying an instance in account: %s" % self.account.name) - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient try: vm = VirtualMachine.create( @@ -176,61 +173,119 @@ class TestMemoryLimits(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.fail("Failed to deploy an instance: %s" % e) - @attr(tags=["advanced", "advancedns","simulator"]) - def test_01_reboot_instance(self): + @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. Update Resource count for the root admin Memory usage - # 3. Stop and start instance, resource count should list properly. - + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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"]) + @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. Update Resource count for the root admin Memory usage + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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"]) + @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. Update Resource count for the root admin Memory usage - # 3. Delete instance, resource count should be 0 after delete operation. + # 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: @@ -238,51 +293,87 @@ class TestMemoryLimits(cloudstackTestCase): 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"]) + # Wait for expunge interval to cleanup Memory + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + 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"]) + @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. Update Resource count for the root admin Memory usage - # 4. Memory usage should list properly + # 3. List Resource count for the root admin Memory usage + # 4. Memory usage should list properly - self.debug("Creating instances with service offering: %s" % + 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) - - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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 TestMemoryUpdateResources(cloudstackTestCase): +class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): @classmethod def setUpClass(cls): - cls.api_client = super(TestMemoryUpdateResources, + 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.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, @@ -312,7 +403,7 @@ class TestMemoryUpdateResources(cloudstackTestCase): def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() - + self.cleanup = [] return @@ -329,8 +420,8 @@ class TestMemoryUpdateResources(cloudstackTestCase): self.debug("Deploying an instance in account: %s" % self.account.name) - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient try: vm = VirtualMachine.create( @@ -349,166 +440,153 @@ class TestMemoryUpdateResources(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % 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 = 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.child_domain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) - self.debug("Updating the Memory resource count for domain: %s" % - self.domain.name) - Resources.updateLimit(self.apiclient, - resourcetype=9, - max=15360, - account=self.child_do_admin.name, - domainid=self.child_do_admin.domainid) + 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.domain = Domain.create(self.apiclient, - services=self.services["domain"], - parentdomainid=self.domain.id) - self.admin = Account.create( + 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.domain.id) - # Cleanup the resources created at end of test - self.cleanup.append(self.admin) - self.cleanup.append(self.domain) + 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.admin.name, - domainid=self.admin.domainid) return - @attr(tags=["advanced", "advancedns","simulator"]) - def test_01_change_service_offering(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + @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 RAM & Deploy VM as root admin - # 2. Update Resource count for the root admin Memory usage - # 3. Upgrade service offering, resource count should list properly. - # 4. Downgrade service offering, resource count should list properly. + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) - - 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - 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) + 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.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)) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - - 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)) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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"]) + @attr(tags=["advanced", "advancedns","simulator"]) def test_02_migrate_instance(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with 5 GB memory & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin - # 2. Update Resource count for the root admin Memory usage - # 3. Migrate vm, resource count should list properly. + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) - - 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - + 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)) @@ -516,43 +594,56 @@ class TestMemoryUpdateResources(cloudstackTestCase): vm.migrate(self.apiclient, host.id) except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - self.debug("Assigning VM to account: %s in domain: %s" % - (self.admin.name, - self.admin.domain)) - # TODO: Assign Virtual Machine function implementation + 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"]) + @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. Update Resource count for the root admin Memory usage - # 3. Delete instance, resource count should be 0 after delete operation. + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) - - 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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: @@ -560,56 +651,97 @@ class TestMemoryUpdateResources(cloudstackTestCase): 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"]) - - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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"]) - def test_04_deploy_multiple_vm_with_5gb_ram(self): - """Test Deploy multiple VM with 5 GB RAM& verify the usage""" + @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 - # 3. Update Resource count for the root admin Memory usage - # 4. Memory usage should list properly + # 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.domain: self.admin, - self.child_domain: self.child_do_admin + 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) + 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) - vm_3 = 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") + self.debug("Deploying instance - memory capacity is fully utilized") with self.assertRaises(Exception): self.createInstance(service_off=self.service_offering, api_client=api_client) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) + + 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, @@ -618,9 +750,15 @@ class TestMemoryUpdateResources(cloudstackTestCase): vm_2.migrate(self.apiclient, host.id) except Exception as e: self.fail("Failed to migrate instance: %s" % e) - resource_count = get_updated_resource_count(self.apiclient, account=self.account, rtype=9)#RAM - self.debug(resource_count) - vm_2.delete(self.apiclient) - vm_3.delete(self.apiclient) - return + 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 index 505f0312514..1c0ed92d89e 100644 --- a/test/integration/component/memory_limits/test_project_limits.py +++ b/test/integration/component/memory_limits/test_project_limits.py @@ -30,8 +30,7 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, cleanup_resources, - wait_for_cleanup, - get_updated_resource_count, + wait_for_cleanup, find_suitable_host, get_resource_type ) @@ -97,7 +96,7 @@ class TestProjectsMemoryLimits(cloudstackTestCase): # 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.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, @@ -127,7 +126,7 @@ class TestProjectsMemoryLimits(cloudstackTestCase): def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() - self.account = Account.create( + self.account = Account.create( self.apiclient, self.services["account"], admin=True @@ -137,14 +136,14 @@ class TestProjectsMemoryLimits(cloudstackTestCase): self.debug("Setting up account and domain hierarchy") self.setupProjectAccounts() - api_client = self.testClient.createUserApiClient( + 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) + service_off=self.service_offering, api_client=api_client) return def tearDown(self): @@ -160,8 +159,8 @@ class TestProjectsMemoryLimits(cloudstackTestCase): self.debug("Deploying an instance in account: %s" % self.account.name) - if api_client is None: - api_client = self.apiclient + if api_client is None: + api_client = self.apiclient try: vm = VirtualMachine.create( @@ -179,7 +178,7 @@ class TestProjectsMemoryLimits(cloudstackTestCase): "Vm state should be running after deployment") return vm except Exception as e: - self.fail("Failed to deploy an instance: %s" % e) + self.fail("Failed to deploy an instance: %s" % e) def setupProjectAccounts(self): @@ -217,24 +216,27 @@ class TestProjectsMemoryLimits(cloudstackTestCase): return @attr(tags=["advanced", "advancedns","simulator"]) - @attr(configuration='max.projects.memory') - def test_01_project_vmlifecycle_reboot_instance(self): - """Test max.projects.memory global configuration""" + def test_01_project_vmlifecycle_start_stop_instance(self): # Validate the following - # 1. Set (max.project.memory=10) as the max limit to - # Domain1 (max.account.memory=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Stop VM of an accounts added to the project to a new host - # 5. Resource count should list properly - # 6. Start VM of an accounts added to the project to a new host - # 7. Resource count should list properly + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM + 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) @@ -242,8 +244,15 @@ class TestProjectsMemoryLimits(cloudstackTestCase): self.fail("Failed to stop instance: %s" % e) self.debug("Checking memory resource count for project: %s" % self.project.name) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) + 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: @@ -252,26 +261,35 @@ class TestProjectsMemoryLimits(cloudstackTestCase): self.fail("Failed to start instance: %s" % e) self.debug("Checking memory resource count for project: %s" % self.project.name) - get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) + 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"]) - @attr(configuration='max.projects.memory') def test_02_project_vmlifecycle_migrate_instance(self): - """Test max.projects.memory global configuration""" # Validate the following - # 1. Set (max.project.memory=10) as the max limit to - # Domain1 (max.account.memory=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Migrate VM of an accounts added to the project to a new host - # 5. Resource count should list properly. + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) + 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" % @@ -280,28 +298,37 @@ class TestProjectsMemoryLimits(cloudstackTestCase): 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) + 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"]) - @attr(configuration='max.projects.memory') def test_03_project_vmlifecycle_delete_instance(self): - """Test max.projects.memory global configuration""" # Validate the following - # 1. Set (max.project.memory=10) as the max limit to - # Domain1 (max.account.memory=10) - # 2. Assign account to projects and verify the resource updates - # 3. Deploy VM with the accounts added to the project - # 4. Destroy VM of an accounts added to the project - # 5. Resource count should list as 0 after destroying the instance + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) + 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: @@ -309,13 +336,15 @@ class TestProjectsMemoryLimits(cloudstackTestCase): 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"]) + # 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) - resource_count = get_updated_resource_count(self.apiclient, account=self.admin, project=self.project, rtype=9)#RAM - self.debug(resource_count) - self.assertEqual(resource_count, 0 , "Resource count for %s should be 0" % get_resource_type(resource_id=9))#RAM + 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 - - 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 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< 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/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) 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 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..3139257dd67 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 @@ -385,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 @@ -401,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 @@ -469,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, @@ -478,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, @@ -2348,3 +2336,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/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/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, 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..ace449999f8 --- /dev/null +++ b/test/integration/component/test_reset_ssh_keypair.py @@ -0,0 +1,1517 @@ +# 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, + }, + "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 diff --git a/test/integration/component/test_shared_networks.py b/test/integration/component/test_shared_networks.py index fec14a1514a..88bb018d404 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 """ 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): 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/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 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 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 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( 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": [ ] } ] } 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 diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 99882fdbb2e..0d52224c535 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] @@ -411,8 +416,13 @@ class VirtualMachine: 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): @@ -902,6 +912,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): @@ -1493,6 +1514,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()] @@ -1778,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 @@ -1786,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 @@ -1801,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 @@ -1870,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 @@ -2340,7 +2365,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 a3ad1d209f0..164ef2052dd 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() @@ -68,12 +68,6 @@ def add_netscaler(apiclient, zoneid, services=None): if isinstance(physical_networks, list): physical_network = physical_networks[0] - netscaler = NetScaler.add( - apiclient, - services["netscaler"], - physicalnetworkid=physical_network.id - ) - cmd = listNetworkServiceProviders.listNetworkServiceProvidersCmd() cmd.name = 'Netscaler' cmd.physicalnetworkid=physical_network.id @@ -81,6 +75,17 @@ def add_netscaler(apiclient, zoneid, services=None): 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 @@ -89,6 +94,22 @@ def add_netscaler(apiclient, zoneid, services=None): 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" @@ -265,6 +286,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): @@ -606,8 +646,9 @@ def list_vpc_offerings(apiclient, **kwargs): [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listVPCOfferings(cmd)) -def get_updated_resource_count(apiclient, rtype, account, project=None): - """Validates the resource count +def update_resource_count(apiclient, domainid, accountid=None, + projectid=None, rtype=None): + """updates the resource count 0 - VM 1 - Public IP 2 - Volume @@ -620,39 +661,25 @@ def get_updated_resource_count(apiclient, rtype, account, project=None): 9 - RAM 10 - Primary (shared) storage (Volumes) 11 - Secondary storage (Snapshots, Templates & ISOs) - 12 - Network bandwidth rate (in bps) - 13 - Number of times a OS template can be deployed""" + """ - if project: - responses = Resources.updateCount(apiclient, - domainid=account.domainid, - projectid = project.id, - resourcetype=rtype - ) - - else: - responses = Resources.updateCount(apiclient, - domainid=account.domainid, - account=account.name, - resourcetype=rtype - ) - - if isinstance(responses, list): - assert len(responses) > 0, "Update resource count should return valid response" - else: - raise Exception("Exception: Update resource count should return valid response") - - return responses[0].resourcecount + 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) + listall=True) if isinstance(hosts, list): - assert len(hosts) > 0, "List host should return valid response" + assert len(hosts) > 0, "List host should return valid response" else: raise Exception("Exception: List host should return valid response") return hosts[0] @@ -664,16 +691,49 @@ def get_resource_type(resource_id): 1: "Public IP", 2: "Volume", 3: "Snapshot", - 4: "Template", - 5: "Projects", - 6: "Network", + 4: "Template", + 5: "Projects", + 6: "Network", 7: "VPC", 8: "CPUs", 9: "RAM", 10: "Primary (shared) storage (Volumes)", - 11: "Secondary storage (Snapshots, Templates & ISOs)", - 12: "Network bandwidth rate (in bps)", - 13: "Number of times a OS template can be deployed" + 11: "Secondary storage (Snapshots, Templates & ISOs)" } 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 diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index 1512d52f868..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") @@ -283,7 +292,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, 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) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 0371899b631..394771c2de1 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; } @@ -11893,13 +11894,15 @@ 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; } .detach:hover .icon, .detachISO:hover .icon, -.detachDisk:hover .icon { +.detachDisk:hover .icon, +.disassociateProfileFromBlade:hover .icon { background-position: -101px -647px; } 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.
-
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 20d3d9a606d..f4507027a7c 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: { @@ -605,6 +611,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 +625,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 } }, @@ -811,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; @@ -828,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; @@ -845,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; diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 0afa3608177..421582afab7 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -2054,54 +2054,63 @@ }, dataProvider: function(args) { - var data = {}; - listViewDataProvider(args, data); - - 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 @@ -3842,10 +3851,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) { diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 3d45ad01c6a..fe3f6730295 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) + }); } }); } @@ -233,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'), @@ -243,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; } }); } @@ -448,7 +475,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() @@ -1101,7 +1135,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 +1157,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) { @@ -1259,65 +1300,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 = { diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8b092600f27..77c387c4e15 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -6096,30 +6096,40 @@ }); $.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)}); - }); - - // for testing only (begin) - /* - selectedZoneObj.vmwaredcName = "datacenter"; - selectedZoneObj.vmwaredcVcenter = "10.10.20.20"; - selectedZoneObj.vmwaredcId = "c3c2562d-65e9-4fc7-92e2-773c2efe8f37"; - */ - // for testing only (end) - + 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; + } + } + } + } + }); + args.response.success({ actionFilter: zoneActionfilter, data: selectedZoneObj @@ -7039,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({ @@ -13621,7 +13631,7 @@ } }, url: { - label: 'label.url', + label: 'label.ip', //CLOUDSTACK-4629 validation: { required: true } @@ -13823,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 }); } }); @@ -13971,7 +13979,8 @@ }; */ //for testing only (end) - + + addExtraPropertiesToUcsBladeObject(json.queryasyncjobresultresponse.jobresult.ucsblade); return json.queryasyncjobresultresponse.jobresult.ucsblade; } } @@ -13982,7 +13991,79 @@ 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": "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": "f8d08575-7a1c-4f79-a588-d129c38bcc4f", + "ucsmanagerid": "0d87c1a6-5664-425c-9024-2ddd9605d260", + "bladedn": "sys/chassis-1/blade-1" + } + }, + "created": "2013-09-13T22:17:29-0700", + "jobid": "2c3698a8-39ac-43e6-8ade-86eb2d3726a0" + } + }; + */ + //for testing only (end) + + addExtraPropertiesToUcsBladeObject(json.queryasyncjobresultresponse.jobresult.ucsblade); + return json.queryasyncjobresultresponse.jobresult.ucsblade; + } + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } } } } @@ -16074,7 +16155,9 @@ var allowedActions = []; if(jsonObj.profiledn == null) { allowedActions.push("associateProfileToBlade"); - } + } else { + allowedActions.push("disassociateProfileFromBlade"); + } return allowedActions; } 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), diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index 695534ffe0a..cf52107ed33 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: { @@ -1004,6 +1006,7 @@ args.action({ data: data, + wizard: $wizard, startFn: $wizard.data('startfn'), uiSteps: $.map( $wizard.find('.steps > div'), 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 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 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) }); diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 58119092db0..960fcb734c8 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) + } + } } } @@ -1787,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'); @@ -1869,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(); @@ -1876,20 +1905,31 @@ $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(); + } 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]').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(); @@ -1924,11 +1964,10 @@ $fields.filter('[rel=username]').css('display', 'inline-block'); $fields.filter('[rel=key]').css('display', 'inline-block'); } - }); - - args.$select.change(); - } - }); + }); + args.$select.change(); + } + }); } }, @@ -2050,7 +2089,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; @@ -4103,62 +4144,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; 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/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/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..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); @@ -1545,29 +1548,25 @@ 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); 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(); @@ -1580,6 +1579,7 @@ public class VirtualMachineMO extends BaseMO { vmConfigSpec.getDeviceChange().add(deviceConfigSpec); clonedVmMo.configureVm(vmConfigSpec); bSuccess = true; + return clonedVmMo; } finally { if(!bSuccess) { clonedVmMo.detachAllDisks(); @@ -1717,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"); @@ -1735,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/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) { 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);