From 07d555db37e638012caabb92dac5d3012b4d0ae9 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 30 Oct 2012 16:35:49 +0530 Subject: [PATCH 01/88] Summary: secondary storage tests refactored Detail: The secondary storage tests are now simplified. Since before running tests we already ensure the health checks - template, builtin, system vms are ready. there is no need to add additonal wait/sleep here. Signed-off-by: Prasanna Santhanam 1351595163 +0530 --- .../smoke/test_secondary_storage.py | 294 ++++-------------- 1 file changed, 67 insertions(+), 227 deletions(-) diff --git a/test/integration/smoke/test_secondary_storage.py b/test/integration/smoke/test_secondary_storage.py index d345bcb56b8..80b7bfb8fe3 100644 --- a/test/integration/smoke/test_secondary_storage.py +++ b/test/integration/smoke/test_secondary_storage.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 @@ -28,35 +28,14 @@ from nose.plugins.attrib import attr #Import System modules import time -class Services: - """Test secondary storage Services - """ - - def __init__(self): - self.services = { - "storage": { - "url": "nfs://192.168.100.131/SecStorage" - # Format: File_System_Type/Location/Path - }, - "hypervisors": { - 0: { - "hypervisor": "XenServer", - "templatefilter": "self", - }, - }, - "sleep": 60, - "timeout": 5, - } - class TestSecStorageServices(cloudstackTestCase): - + @classmethod def setUpClass(cls): cls.api_client = super(TestSecStorageServices, cls).getClsTestClient().getApiClient() - cls.services = Services().services cls._cleanup = [] return - + @classmethod def tearDownClass(cls): try: @@ -65,16 +44,32 @@ class TestSecStorageServices(cloudstackTestCase): except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return - - def setUp(self): + def setUp(self): self.apiclient = self.testClient.getApiClient() self.cleanup = [] - self.services = Services().services # Get Zone and pod - self.domain = get_domain(self.apiclient, self.services) - self.zone = get_zone(self.apiclient, self.services) - self.pod = get_pod(self.apiclient, self.zone.id) + self.zones = [] + self.pods = [] + for zone in self.config.zones: + cmd = listZones.listZonesCmd() + cmd.name = zone.name + z = self.apiclient.listZones(cmd) + if isinstance(z, list) and len(z) > 0: + self.zones.append(z[0].id) + for pod in zone.pods: + podcmd = listPods.listPodsCmd() + podcmd.zoneid = z[0].id + p = self.apiclient.listPods(podcmd) + if isinstance(p, list) and len(p) >0: + self.pods.append(p[0].id) + + self.domains = [] + dcmd = listDomains.listDomainsCmd() + domains = self.apiclient.listDomains(dcmd) + assert isinstance(domains, list) and len(domains) > 0 + for domain in domains: + self.domains.append(domain.id) return def tearDown(self): @@ -85,62 +80,8 @@ class TestSecStorageServices(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @unittest.skip("skipped - do not add secondary storage") - def test_01_add_sec_storage(self): - """Test secondary storage - """ - - # Validate the following: - # 1. secondary storage should be added to the zone. - # 2. Verify with listHosts and type secondarystorage - - cmd = addSecondaryStorage.addSecondaryStorageCmd() - cmd.zoneid = self.zone.id - cmd.url = self.services["storage"]["url"] - sec_storage = self.apiclient.addSecondaryStorage(cmd) - - self.debug("Added secondary storage to zone: %s" % self.zone.id) - # Cleanup at the end - self._cleanup.append(sec_storage) - - self.assertEqual( - sec_storage.zoneid, - self.zone.id, - "Check zoneid where sec storage is added" - ) - - list_hosts_response = list_hosts( - self.apiclient, - type='SecondaryStorage', - id=sec_storage.id - ) - self.assertEqual( - isinstance(list_hosts_response, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(list_hosts_response), - 0, - "Check list Hosts response" - ) - - host_response = list_hosts_response[0] - #Check if host is Up and running - self.assertEqual( - host_response.id, - sec_storage.id, - "Check ID of secondary storage" - ) - self.assertEqual( - sec_storage.type, - host_response.type, - "Check type of host from list hosts response" - ) - return - @attr(tags = ["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) - def test_02_sys_vm_start(self): + def test_01_sys_vm_start(self): """Test system VM start """ @@ -152,8 +93,6 @@ class TestSecStorageServices(cloudstackTestCase): list_hosts_response = list_hosts( self.apiclient, type='Routing', - zoneid=self.zone.id, - podid=self.pod.id ) self.assertEqual( isinstance(list_hosts_response, list), @@ -176,8 +115,6 @@ class TestSecStorageServices(cloudstackTestCase): # ListStoragePools shows all primary storage pools in UP state list_storage_response = list_storage_pools( self.apiclient, - zoneid=self.zone.id, - podid=self.pod.id ) self.assertEqual( isinstance(list_storage_response, list), @@ -197,58 +134,11 @@ class TestSecStorageServices(cloudstackTestCase): "Check state of primary storage pools is Up or not" ) - # Secondary storage is added successfully - timeout = self.services["timeout"] - while True: - list_hosts_response = list_hosts( - self.apiclient, - type='SecondaryStorageVM', - zoneid=self.zone.id, - ) + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + ) - if not isinstance(list_hosts_response, list): - # Sleep to ensure Secondary storage is Up - time.sleep(int(self.services["sleep"])) - timeout = timeout - 1 - elif timeout == 0 or isinstance(list_hosts_response, list): - break - - self.assertEqual( - isinstance(list_hosts_response, list), - True, - "Check list response returns a valid list" - ) - - self.assertNotEqual( - len(list_hosts_response), - 0, - "Check list Hosts response" - ) - - host_response = list_hosts_response[0] - #Check if host is Up and running - self.assertEqual( - host_response.state, - 'Up', - "Check state of secondary storage" - ) - self.debug("Checking SSVM status in zone: %s" % self.zone.id) - - timeout = self.services["timeout"] - - while True: - list_ssvm_response = list_ssvms( - self.apiclient, - systemvmtype='secondarystoragevm', - zoneid=self.zone.id, - ) - if not isinstance(list_ssvm_response, list): - # Sleep to ensure SSVMs are Up and Running - time.sleep(int(self.services["sleep"])) - timeout = timeout - 1 - elif timeout == 0 or isinstance(list_ssvm_response, list): - break - self.assertEqual( isinstance(list_ssvm_response, list), True, @@ -270,7 +160,7 @@ class TestSecStorageServices(cloudstackTestCase): return @attr(tags = ["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) - def test_03_sys_template_ready(self): + def test_02_sys_template_ready(self): """Test system templates are ready """ @@ -279,100 +169,50 @@ class TestSecStorageServices(cloudstackTestCase): # 1. wait for listTemplates to show all builtin templates downloaded and # in Ready state - for k, v in self.services["hypervisors"].items(): + hypervisors = {} + for zone in self.config.zones: + for pod in zone.pods: + for cluster in pod.clusters: + hypervisors[cluster.hypervisor] = "self" - self.debug("Downloading BUILTIN templates in zone: %s" % - self.zone.id) - - list_template_response = list_templates( - self.apiclient, - hypervisor=v["hypervisor"], - zoneid=self.zone.id, - templatefilter=v["templatefilter"], - listall=True, - account='system', - domainid=self.domain.id - ) + for zid in self.zones: + for k, v in hypervisors.items(): + self.debug("Checking BUILTIN templates in zone: %s" %zid) + list_template_response = list_templates( + self.apiclient, + hypervisor=k, + zoneid=zid, + templatefilter=v, + listall=True, + account='system' + ) - # Ensure all BUILTIN templates are downloaded - templateid = None - for template in list_template_response: - if template.templatetype == "BUILTIN": - templateid = template.id + # Ensure all BUILTIN templates are downloaded + templateid = None + for template in list_template_response: + if template.templatetype == "BUILTIN": + templateid = template.id - # Wait to start a downloading of template - time.sleep(self.services["sleep"]) - - while True and (templateid != None): - - timeout = self.services["timeout"] - while True: template_response = list_templates( self.apiclient, id=templateid, - zoneid=self.zone.id, - templatefilter=v["templatefilter"], + zoneid=zid, + templatefilter=v, listall=True, - account='system', - domainid=self.domain.id + account='system' ) - if isinstance(template_response, list): template = template_response[0] - break - - elif timeout == 0: - raise Exception("List template API call failed.") - - time.sleep(1) - timeout = timeout - 1 - - # If template is ready, - # template.status = Download Complete - # Downloading - x% Downloaded - # Error - Any other string - if template.status == 'Download Complete' : - break - elif 'Downloaded' not in template.status.split(): - raise Exception - elif 'Downloaded' in template.status.split(): - time.sleep(self.services["sleep"]) + else: + raise Exception("ListTemplate API returned invalid list") - #Ensuring the template is in ready state - time.sleep(self.services["sleep"]) - - timeout = self.services["timeout"] - while True: - template_response = list_templates( - self.apiclient, - id=templateid, - zoneid=self.zone.id, - templatefilter=v["templatefilter"], - listall=True, - account='system', - domainid=self.domain.id - ) - - if isinstance(template_response, list): - template = template_response[0] - break - - elif timeout == 0: - raise Exception("List template API call failed.") - - time.sleep(1) - timeout = timeout - 1 - - self.assertEqual( - isinstance(template_response, list), - True, - "Check list response returns a valid list" - ) - template = template_response[0] + if template.status == 'Download Complete': + self.debug("Template %s is ready in zone %s"%(template.templatetype, zid)) + elif 'Downloaded' not in template.status.split(): + self.debug("templates status is %s"%template.status) - self.assertEqual( - template.isready, - True, - "Check whether state of template is ready or not" - ) - return + self.assertEqual( + template.isready, + True, + "Builtin template is not ready %s in zone %s"%(template.status, zid) + ) From bd58ceccd8d08a2484384a7eef6ef3c681a1e188 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Mon, 29 Oct 2012 18:02:34 +0100 Subject: [PATCH 02/88] Summary: Make the authenticator responsible for encoding the password and add a SHA256 salted authenticator The authenticators now have an encode function that cloudstack will use to encode the user supplied password before storing it in the database. This makes it easier to add other authenticators with other hashing algorithms. The requires a two step approach to creating the admin account at first start as the authenticators are only present in the management-server component locator. The SHA256 salted authenticator make use of this new system and adds a hashing algorithm based on SHA256 with a salt. This type of hash is far less susceptible to rainbow table attacks. To make use of these new features the users password will be sent over the wire just as he typed it and it will be transformed into a hash on the server and compared with the stored password. This means that the hash will not go over the wire anymore. The default authenticator in components.xml is still set to md5 for backwards compatibility. For new installations the sha256 could be enabled. --- client/pom.xml | 5 + client/tomcatconf/components.xml.in | 1 + plugins/pom.xml | 1 + .../server/auth/LDAPUserAuthenticator.java | 17 +++ .../server/auth/MD5UserAuthenticator.java | 51 ++++---- .../auth/PlainTextUserAuthenticator.java | 6 + .../user-authenticators/sha256salted/pom.xml | 29 +++++ .../auth/SHA256SaltedUserAuthenticator.java | 122 ++++++++++++++++++ .../server/auth/test/AuthenticatorTest.java | 46 +++++++ .../cloud/server/ConfigurationServerImpl.java | 30 ++--- .../com/cloud/server/ManagementServer.java | 2 + .../cloud/server/ManagementServerImpl.java | 33 +++++ .../cloud/server/auth/UserAuthenticator.java | 6 + .../cloud/servlet/CloudStartupServlet.java | 1 + .../com/cloud/user/AccountManagerImpl.java | 28 +++- ui/scripts/sharedFunctions.js | 4 +- 16 files changed, 331 insertions(+), 51 deletions(-) create mode 100644 plugins/user-authenticators/sha256salted/pom.xml create mode 100644 plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java create mode 100644 plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java diff --git a/client/pom.xml b/client/pom.xml index c3a048604b1..6e13cc7ada9 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -40,6 +40,11 @@ cloud-plugin-user-authenticator-plaintext ${project.version} + + org.apache.cloudstack + cloud-plugin-user-authenticator-sha256salted + ${project.version} + org.apache.cloudstack cloud-plugin-network-nvp diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index 2953eb781a6..5957b61c0fb 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -109,6 +109,7 @@ under the License. + diff --git a/plugins/pom.xml b/plugins/pom.xml index dbdea24e17b..2009302423e 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -45,6 +45,7 @@ user-authenticators/ldap user-authenticators/md5 user-authenticators/plain-text + user-authenticators/sha256salted diff --git a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java b/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java index 7c6e52f6659..43874f61cb7 100644 --- a/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java +++ b/plugins/user-authenticators/ldap/src/com/cloud/server/auth/LDAPUserAuthenticator.java @@ -15,6 +15,8 @@ // package com.cloud.server.auth; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; @@ -31,6 +33,7 @@ import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import org.apache.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; import com.cloud.api.ApiConstants.LDAPParams; import com.cloud.configuration.Config; @@ -40,6 +43,7 @@ import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.exception.CloudRuntimeException; @Local(value={UserAuthenticator.class}) @@ -159,4 +163,17 @@ public class LDAPUserAuthenticator extends DefaultUserAuthenticator { _userAccountDao = locator.getDao(UserAccountDao.class); return true; } + + @Override + public String encode(String password) { + // Password is not used, so set to a random string + try { + SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); + byte bytes[] = new byte[20]; + randomGen.nextBytes(bytes); + return Base64.encode(bytes).toString(); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Failed to generate random password",e); + } + } } diff --git a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java index f4b6f021f3b..b0cf0b03cd6 100644 --- a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java +++ b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java @@ -15,6 +15,9 @@ package com.cloud.server.auth; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Map; import javax.ejb.Local; @@ -26,6 +29,7 @@ import com.cloud.server.ManagementServer; import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.exception.CloudRuntimeException; /** * Simple UserAuthenticator that performs a MD5 hash of the password before @@ -49,31 +53,7 @@ public class MD5UserAuthenticator extends DefaultUserAuthenticator { return false; } - /** - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Error", e); - } - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - - // make sure our MD5 hash value is 32 digits long... - StringBuffer sb = new StringBuffer(); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - for (int i = 0; i < padding; i++) { - sb.append('0'); - } - sb.append(pwStr); - **/ - - // Will: The MD5Authenticator is now a straight pass-through comparison of the - // the passwords because we will not assume that the password passed in has - // already been MD5 hashed. I am keeping the above code in case this requirement changes - // or people need examples of how to MD5 hash passwords in java. - if (!user.getPassword().equals(password)) { + if (!user.getPassword().equals(encode(password))) { s_logger.debug("Password does not match"); return false; } @@ -87,4 +67,25 @@ public class MD5UserAuthenticator extends DefaultUserAuthenticator { _userAccountDao = locator.getDao(UserAccountDao.class); return true; } + + @Override + public String encode(String password) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } + + md5.reset(); + BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); + String pwStr = pwInt.toString(16); + int padding = 32 - pwStr.length(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < padding; i++) { + sb.append('0'); // make sure the MD5 password is 32 digits long + } + sb.append(pwStr); + return sb.toString(); + } } diff --git a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java index 006daf98e9b..59e12e50048 100644 --- a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java +++ b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java @@ -87,4 +87,10 @@ public class PlainTextUserAuthenticator extends DefaultUserAuthenticator { _userAccountDao = locator.getDao(UserAccountDao.class); return true; } + + @Override + public String encode(String password) { + // Plaintext so no encoding at all + return password; + } } diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml new file mode 100644 index 00000000000..3f530f76e17 --- /dev/null +++ b/plugins/user-authenticators/sha256salted/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-user-authenticator-sha256salted + Apache CloudStack Plugin - User Authenticator SHA256 Salted + + org.apache.cloudstack + cloudstack-plugins + 4.1.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java new file mode 100644 index 00000000000..26c33a5a9ec --- /dev/null +++ b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.server.auth; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; + +import com.cloud.server.ManagementServer; +import com.cloud.servlet.CloudStartupServlet; +import com.cloud.user.UserAccount; +import com.cloud.user.dao.UserAccountDao; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value={UserAuthenticator.class}) +public class SHA256SaltedUserAuthenticator extends DefaultUserAuthenticator { + public static final Logger s_logger = Logger.getLogger(SHA256SaltedUserAuthenticator.class); + + @Inject + private UserAccountDao _userAccountDao; + private static int s_saltlen = 20; + + public boolean configure(String name, Map params) + throws ConfigurationException { + super.configure(name, params); + return true; + } + + /* (non-Javadoc) + * @see com.cloud.server.auth.UserAuthenticator#authenticate(java.lang.String, java.lang.String, java.lang.Long, java.util.Map) + */ + @Override + public boolean authenticate(String username, String password, + Long domainId, Map requestParameters) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Retrieving user: " + username); + } + UserAccount user = _userAccountDao.getUserAccount(username, domainId); + if (user == null) { + s_logger.debug("Unable to find user with " + username + " in domain " + domainId); + return false; + } + + try { + String storedPassword[] = user.getPassword().split(":"); + if (storedPassword.length != 2) { + s_logger.warn("The stored password for " + username + " isn't in the right format for this authenticator"); + return false; + } + byte salt[] = Base64.decode(storedPassword[0]); + String hashedPassword = encode(password, salt); + return storedPassword[1].equals(hashedPassword); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } + } + + /* (non-Javadoc) + * @see com.cloud.server.auth.UserAuthenticator#encode(java.lang.String) + */ + @Override + public String encode(String password) { + // 1. Generate the salt + SecureRandom randomGen; + try { + randomGen = SecureRandom.getInstance("SHA1PRNG"); + + byte salt[] = new byte[s_saltlen]; + randomGen.nextBytes(salt); + + String saltString = new String(Base64.encode(salt)); + String hashString = encode(password, salt); + + // 3. concatenate the two and return + return saltString + ":" + hashString; + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } + } + + public String encode(String password, byte[] salt) throws UnsupportedEncodingException, NoSuchAlgorithmException { + byte[] passwordBytes = password.getBytes("UTF-8"); + byte[] hashSource = new byte[passwordBytes.length + s_saltlen]; + System.arraycopy(passwordBytes, 0, hashSource, 0, passwordBytes.length); + System.arraycopy(salt, 0, hashSource, passwordBytes.length, s_saltlen); + + // 2. Hash the password with the salt + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(hashSource); + byte[] digest = md.digest(); + + return new String(Base64.encode(digest)); + } +} diff --git a/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java b/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java new file mode 100644 index 00000000000..cf990248f39 --- /dev/null +++ b/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java @@ -0,0 +1,46 @@ +package src.com.cloud.server.auth.test; + +import static org.junit.Assert.*; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; + +import javax.naming.ConfigurationException; + +import org.bouncycastle.util.encoders.Base64; +import org.junit.Before; +import org.junit.Test; + +import com.cloud.server.auth.SHA256SaltedUserAuthenticator; + +public class AuthenticatorTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public void testEncode() throws UnsupportedEncodingException, NoSuchAlgorithmException { + SHA256SaltedUserAuthenticator authenticator = + new SHA256SaltedUserAuthenticator(); + + try { + authenticator.configure("SHA256", Collections.emptyMap()); + } catch (ConfigurationException e) { + fail(e.toString()); + } + + String encodedPassword = authenticator.encode("password"); + + String storedPassword[] = encodedPassword.split(":"); + assertEquals ("hash must consist of two components", storedPassword.length, 2); + + byte salt[] = Base64.decode(storedPassword[0]); + String hashedPassword = authenticator.encode("password", salt); + + assertEquals("compare hashes", storedPassword[1], hashedPassword); + + } + +} diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 3368c9ba116..904e8c59f6e 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -32,6 +32,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -85,6 +86,7 @@ import com.cloud.offerings.NetworkOfferingServiceMapVO; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.server.auth.UserAuthenticator; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -96,6 +98,7 @@ import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.component.Adapters; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.DB; @@ -342,30 +345,13 @@ public class ConfigurationServerImpl implements ConfigurationServer { } catch (SQLException ex) { } - // insert admin user + // insert admin user, but leave the account disabled until we set a + // password with the user authenticator long id = 2; String username = "admin"; String firstname = "admin"; String lastname = "cloud"; - String password = "password"; - - MessageDigest md5 = null; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - return; - } - - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < padding; i++) { - sb.append('0'); // make sure the MD5 password is 32 digits long - } - sb.append(pwStr); - + // create an account for the admin user first insertSql = "INSERT INTO `cloud`.`account` (id, account_name, type, domain_id) VALUES (" + id + ", '" + username + "', '1', '1')"; txn = Transaction.currentTxn(); @@ -376,8 +362,8 @@ public class ConfigurationServerImpl implements ConfigurationServer { } // now insert the user - insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created) " + - "VALUES (" + id + ",'" + username + "','" + sb.toString() + "', 2, '" + firstname + "','" + lastname + "',now())"; + insertSql = "INSERT INTO `cloud`.`user` (id, username, account_id, firstname, lastname, created, state) " + + "VALUES (" + id + ",'" + username + "', 2, '" + firstname + "','" + lastname + "',now(), 'disabled')"; txn = Transaction.currentTxn(); try { diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index 473b0ee9b3a..91f82f874d5 100755 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -95,4 +95,6 @@ public interface ManagementServer extends ManagementService { Pair, Integer> searchForStoragePools(Criteria c); String getHashKey(); + + public void enableAdminUser(String password); } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index a916eb69696..117be5797b0 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -177,6 +177,7 @@ import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.projects.ProjectManager; import com.cloud.resource.ResourceManager; import com.cloud.server.ResourceTag.TaggedResourceType; +import com.cloud.server.auth.UserAuthenticator; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -215,7 +216,9 @@ import com.cloud.user.AccountVO; import com.cloud.user.SSHKeyPair; import com.cloud.user.SSHKeyPairVO; import com.cloud.user.User; +import com.cloud.user.UserAccount; import com.cloud.user.UserContext; +import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; @@ -338,6 +341,8 @@ public class ManagementServerImpl implements ManagementServer { private final StatsCollector _statsCollector; private final Map _availableIdsMap; + + private Adapters _userAuthenticators; private String _hashKey = null; @@ -417,6 +422,11 @@ public class ManagementServerImpl implements ManagementServer { for (String id : availableIds) { _availableIdsMap.put(id, true); } + + _userAuthenticators = locator.getAdapters(UserAuthenticator.class); + if (_userAuthenticators == null || !_userAuthenticators.isSet()) { + s_logger.error("Unable to find an user authenticator."); + } } protected Map getConfigs() { @@ -3587,5 +3597,28 @@ public class ManagementServerImpl implements ManagementServer { } } + + public void enableAdminUser(String password) { + String encodedPassword = null; + + UserVO adminUser = _userDao.getUser(2); + if (adminUser.getState() == Account.State.disabled) { + // This means its a new account, set the password using the authenticator + + for (Enumeration en = _userAuthenticators.enumeration(); en.hasMoreElements();) { + UserAuthenticator authenticator = en.nextElement(); + encodedPassword = authenticator.encode(password); + if (encodedPassword != null) { + break; + } + } + + adminUser.setPassword(encodedPassword); + adminUser.setState(Account.State.enabled); + _userDao.persist(adminUser); + s_logger.info("Admin user enabled"); + } + + } } diff --git a/server/src/com/cloud/server/auth/UserAuthenticator.java b/server/src/com/cloud/server/auth/UserAuthenticator.java index 725516c096f..95c4f0e4707 100644 --- a/server/src/com/cloud/server/auth/UserAuthenticator.java +++ b/server/src/com/cloud/server/auth/UserAuthenticator.java @@ -34,4 +34,10 @@ public interface UserAuthenticator extends Adapter { * @return true if the user has been successfully authenticated, false otherwise */ public boolean authenticate(String username, String password, Long domainId, Map requestParameters); + + /** + * @param password + * @return the encoded password + */ + public String encode(String password); } diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index c3e5a8235ce..9efb4ea5a8c 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -47,6 +47,7 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi c.persistDefaultValues(); s_locator = ComponentLocator.getLocator(ManagementServer.Name); ManagementServer ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name); + ms.enableAdminUser("password"); ApiServer.initApiServer(ms.getApiConfig()); } catch (InvalidParameterValueException ipve) { s_logger.error("Exception starting management server ", ipve); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index f1e606e76a1..0def0083f66 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -921,7 +921,18 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } if (password != null) { - user.setPassword(password); + String encodedPassword = null; + for (Enumeration en = _userAuthenticators.enumeration(); en.hasMoreElements();) { + UserAuthenticator authenticator = en.nextElement(); + encodedPassword = authenticator.encode(password); + if (encodedPassword != null) { + break; + } + } + if (encodedPassword == null) { + throw new CloudRuntimeException("Failed to encode password"); + } + user.setPassword(encodedPassword); } if (email != null) { user.setEmail(email); @@ -1670,7 +1681,20 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag if (s_logger.isDebugEnabled()) { s_logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone); } - UserVO user = _userDao.persist(new UserVO(accountId, userName, password, firstName, lastName, email, timezone)); + + String encodedPassword = null; + for (Enumeration en = _userAuthenticators.enumeration(); en.hasMoreElements();) { + UserAuthenticator authenticator = en.nextElement(); + encodedPassword = authenticator.encode(password); + if (encodedPassword != null) { + break; + } + } + if (encodedPassword == null) { + throw new CloudRuntimeException("Failed to encode password"); + } + + UserVO user = _userDao.persist(new UserVO(accountId, userName, encodedPassword, firstName, lastName, email, timezone)); return user; } diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 5e187ed1b82..28b3bb9550f 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -37,8 +37,8 @@ var ERROR_INTERNET_CANNOT_CONNECT = 12029; var ERROR_VMOPS_ACCOUNT_ERROR = 531; // Default password is MD5 hashed. Set the following variable to false to disable this. -var md5Hashed = true; -var md5HashedLogin = true; +var md5Hashed = false; +var md5HashedLogin = false; //page size for API call (e.g."listXXXXXXX&pagesize=N" ) var pageSize = 20; From f65b268a2e3ebab37d54ee9ea23934ab76dc2e1a Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Tue, 30 Oct 2012 14:15:07 +0100 Subject: [PATCH 03/88] Active the testrun by default in maven Exclude the tests that need a database, these should move to the integration test phase or use an in memory database maybe. --- pom.xml | 1 - server/pom.xml | 6 ++++++ utils/pom.xml | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 12b40674905..fe1064cacca 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,6 @@ 1.2 1.0-20081010.060147 4.1 - true diff --git a/server/pom.xml b/server/pom.xml index 29922b62cb5..7f80acc22b5 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -88,6 +88,12 @@ com/cloud/upgrade/* + com/cloud/async/* + com/cloud/cluster/* + com/cloud/snapshot/* + com/cloud/storage/dao/* + com/cloud/vm/dao/* + com/cloud/vpc/* diff --git a/utils/pom.xml b/utils/pom.xml index 47e20eda59c..2e4e74f5411 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -161,6 +161,7 @@ com/cloud/utils/testcase/*TestCase* + com/cloud/utils/db/*Test* From 4572bc0685bb4f81068fd431fd23db024b32f265 Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Tue, 30 Oct 2012 16:31:36 +0100 Subject: [PATCH 04/88] Summary: Sample packaging scripts This is a manual merge of the maven-to-rpm branch. This commits show how an RPM package can be built without waf. The current version only builds the management server rpm and some related rpms like setup. The main missing items are agent rpm and the awsapi rpm. But it should at least show how to use maven and packaging. Several small tweaks are put into the client/pom.xml to make sure that the war has all items required to run as a standalone war. --- client/pom.xml | 79 ++++- ...server-nonssl.xml => server-nonssl.xml.in} | 0 packaging/centos63/cloud-ipallocator.rc | 98 ++++++ packaging/centos63/cloud-management.rc | 107 +++++++ packaging/centos63/cloud-management.sysconfig | 23 ++ packaging/centos63/cloud.spec | 278 ++++++++++++++++++ packaging/centos63/package.sh | 29 ++ packaging/centos63/replace.properties | 61 ++++ 8 files changed, 673 insertions(+), 2 deletions(-) rename client/tomcatconf/{server-nonssl.xml => server-nonssl.xml.in} (100%) create mode 100755 packaging/centos63/cloud-ipallocator.rc create mode 100755 packaging/centos63/cloud-management.rc create mode 100644 packaging/centos63/cloud-management.sysconfig create mode 100644 packaging/centos63/cloud.spec create mode 100644 packaging/centos63/package.sh create mode 100644 packaging/centos63/replace.properties diff --git a/client/pom.xml b/client/pom.xml index 6e13cc7ada9..056e560baff 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -164,7 +164,7 @@ + todir="${basedir}/target/generated-webapp/WEB-INF/classes/scripts"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -213,6 +255,39 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + 2.5.1 + + + copy + package + + copy + + + + + org.jasypt + jasypt + 1.9.0` + false + ${project.build.directory}/pythonlibs + + + org.jasypt + jasypt + 1.8` + false + ${project.build.directory}/pythonlibs + + + + + + diff --git a/client/tomcatconf/server-nonssl.xml b/client/tomcatconf/server-nonssl.xml.in similarity index 100% rename from client/tomcatconf/server-nonssl.xml rename to client/tomcatconf/server-nonssl.xml.in diff --git a/packaging/centos63/cloud-ipallocator.rc b/packaging/centos63/cloud-ipallocator.rc new file mode 100755 index 00000000000..ffeffa342c8 --- /dev/null +++ b/packaging/centos63/cloud-ipallocator.rc @@ -0,0 +1,98 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# chkconfig: 35 99 10 +# description: Cloud Agent + +# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well + +. /etc/rc.d/init.d/functions + +whatami=cloud-external-ipallocator + +# set environment variables + +SHORTNAME="$whatami" +PIDFILE=/var/run/"$whatami".pid +LOCKFILE=/var/lock/subsys/"$SHORTNAME" +LOGFILE=/var/log/cloud/ipallocator/ipallocator.log +PROGNAME="External IPAllocator" + +unset OPTIONS +[ -r /etc/sysconfig/"$SHORTNAME" ] && source /etc/sysconfig/"$SHORTNAME" +DAEMONIZE=/usr/bin/cloud-daemonize +PROG=/usr/bin/cloud-external-ipallocator.py +OPTIONS=8083 + +start() { + echo -n $"Starting $PROGNAME: " + if hostname --fqdn >/dev/null 2>&1 ; then + daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ + -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS + RETVAL=$? + echo + else + failure + echo + echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr + RETVAL=9 + fi + [ $RETVAL = 0 ] && touch ${LOCKFILE} + return $RETVAL +} + +stop() { + echo -n $"Stopping $PROGNAME: " + killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p ${PIDFILE} $SHORTNAME + RETVAL=$? + ;; + restart) + stop + sleep 3 + start + ;; + condrestart) + if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then + stop + sleep 3 + start + fi + ;; + *) + echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/packaging/centos63/cloud-management.rc b/packaging/centos63/cloud-management.rc new file mode 100755 index 00000000000..48c2ab44a62 --- /dev/null +++ b/packaging/centos63/cloud-management.rc @@ -0,0 +1,107 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# cloud-management This shell script takes care of starting and stopping Tomcat +# +# chkconfig: - 80 20 +# +### BEGIN INIT INFO +# Provides: tomcat6 +# Required-Start: $network $syslog +# Required-Stop: $network $syslog +# Default-Start: +# Default-Stop: +# Description: Release implementation for Servlet 2.5 and JSP 2.1 +# Short-Description: start and stop tomcat +### END INIT INFO +# +# - originally written by Henri Gomez, Keith Irwin, and Nicolas Mailhot +# - heavily rewritten by Deepak Bhole and Jason Corley +# + +if [ -r /etc/rc.d/init.d/functions ]; then + . /etc/rc.d/init.d/functions +fi +if [ -r /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions +fi + + +NAME="$(basename $0)" +stop() { + SHUTDOWN_WAIT="30" + count="0" + if [ -f /var/run/cloud-management.pid ]; then + pid=`cat /var/run/cloud-management.pid` + kill $pid &>/dev/null + until [ "$(ps --pid $pid | grep -c $pid)" -eq "0" ] || \ + [ "$count" -gt "$SHUTDOWN_WAIT" ] + do + sleep 1 + let count="${count}+1" + done + if [ "$(ps --pid $pid | grep -c $pid)" -eq "0" ]; then + log_success_msg "Stopping cloud-management:" + else + log_failure_msg "Stopping cloud-management:" + fi + else + echo "Cannot find PID file of Cloud-management" + log_failure_msg "Stopping cloud-management:" + fi +} + +set_ulimit() { + fd_limit=`ulimit -n` + if [ "$fd_limit" != "4096" ]; then + user=`whoami` + if [ $user == "root" ]; then + ulimit -n 4096 + fi + fi +} + +handle_pid_file() { + if [ "$1" -ne 0 ] ; then + echo "The pid file locates at /var/run/cloud-management.pid and lock file at /var/lock/subsys/cloud-management. + Starting cloud-management will take care of them or you can manually clean up." + fi +} + +# See how we were called. +case "$1" in + status) + status ${NAME} + RETVAL=$? + handle_pid_file $RETVAL + ;; + stop) + stop + ;; + restart) + stop + set start + set_ulimit + . /etc/rc.d/init.d/tomcat6 + ;; + *) + set_ulimit + . /etc/rc.d/init.d/tomcat6 +esac + +exit $RETVAL diff --git a/packaging/centos63/cloud-management.sysconfig b/packaging/centos63/cloud-management.sysconfig new file mode 100644 index 00000000000..15df8cdab92 --- /dev/null +++ b/packaging/centos63/cloud-management.sysconfig @@ -0,0 +1,23 @@ +# 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. + +# This file is loaded in /etc/init.d/vmopsmanagement +# ATM we only do two things here: + +dummy=1 ; export TOMCAT_CFG=/etc/cloud/management/tomcat6.conf ; . /etc/cloud/management/tomcat6.conf +#-------------------------- + diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec new file mode 100644 index 00000000000..b0f4046fef5 --- /dev/null +++ b/packaging/centos63/cloud.spec @@ -0,0 +1,278 @@ +# 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. + +%define __os_install_post %{nil} +%global debug_package %{nil} + +# DISABLE the post-percentinstall java repacking and line number stripping +# we need to find a way to just disable the java repacking and line number stripping, but not the autodeps + +Name: cloud +Summary: CloudStack IaaS Platform +#http://fedoraproject.org/wiki/PackageNamingGuidelines#Pre-Release_packages +%if "%{?_prerelease}" != "" +%define _maventag %{_ver}-SNAPSHOT +Release: %{_rel}%{dist} +%else +%define _maventag %{_ver} +Release: %{_rel}%{dist} +%endif +Version: %{_ver} +License: Apache License 2.0 +Vendor: Apache CloudStack +Packager: Apache CloudStack +Group: System Environment/Libraries +# FIXME do groups for every single one of the subpackages +Source0: %{name}-%{_maventag}.tgz +BuildRoot: %{_tmppath}/%{name}-%{_maventag}-%{release}-build + +BuildRequires: java-1.6.0-openjdk-devel +BuildRequires: tomcat6 +BuildRequires: ws-commons-util +BuildRequires: jpackage-utils +BuildRequires: gcc +BuildRequires: glibc-devel +BuildRequires: /usr/bin/mkisofs +BuildRequires: MySQL-python +#BuildRequires: maven => 3.0.0 + +%description +CloudStack is a highly-scalable elastic, open source, +intelligent IaaS cloud implementation. + +%package management-server +Summary: CloudStack management server UI +Requires: tomcat6 +Requires: java >= 1.6.0 +Requires: python +Requires: bash +Requires: bzip2 +Requires: gzip +Requires: unzip +Requires: /sbin/mount.nfs +Requires: openssh-clients +Requires: nfs-utils +Requires: wget +Requires: mysql-connector-java +Requires: ws-commons-util +Requires: jpackage-utils +Requires: sudo +Requires: /sbin/service +Requires: /sbin/chkconfig +Requires: /usr/bin/ssh-keygen +Requires: mkisofs +Requires: MySQL-python +Requires: python-paramiko +Requires: ipmitool +Requires: %{name}-setup = %{_ver} +Group: System Environment/Libraries +%description management-server +The CloudStack management server is the central point of coordination, +management, and intelligence in CloudStack. + +%package setup +Summary: CloudStack database setup scripts +Requires: java >= 1.6.0 +Requires: python +Requires: MySQL-python +Requires: %{name}-python = %{_ver} +Group: System Environment/Libraries +%description setup +The scripts and commands used to setup and configure the database + +%package python +Summary: CloudStack Python library +# FIXME nuke the archdependency +Requires: python +Group: System Environment/Libraries +%description python +The CloudStack Python library contains a few Python modules that the +CloudStack uses. + + +%prep +echo Doing CloudStack build + +%setup -q -n %{name}-%{_maventag} + +%build + +# this fixes the /usr/com bug on centos5 +%define _localstatedir /var +%define _sharedstatedir /var/lib +cp packaging/centos63/replace.properties build/replace.properties +echo VERSION=%{_maventag} >> build/replace.properties +echo PACKAGE=%{name} >> build/replace.properties +mvn package -Dsystemvm + +%install +[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} +mkdir -p ${RPM_BUILD_ROOT}%{_bindir} +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}/setup +mkdir -p ${RPM_BUILD_ROOT}/usr/share/%{name}/management/ +ln -sf /usr/share/tomcat6/bin ${RPM_BUILD_ROOT}/usr/share/%{name}/management/bin +ln -sf /etc/cloud/management ${RPM_BUILD_ROOT}/usr/share/%{name}/management/conf +ln -sf /usr/share/tomcat6/lib ${RPM_BUILD_ROOT}/usr/share/%{name}/management/lib +ln -sf /var/log/cloud/management ${RPM_BUILD_ROOT}/usr/share/%{name}/management/logs +ln -sf /var/cache/cloud/management/temp ${RPM_BUILD_ROOT}/usr/share/%{name}/management/temp +ln -sf /var/cache/cloud/management/work ${RPM_BUILD_ROOT}/usr/share/%{name}/management/work +mkdir -p ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client +mkdir -p ${RPM_BUILD_ROOT}/var/log/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}/var/log/%{name}/agent +mkdir -p ${RPM_BUILD_ROOT}/var/log/%{name}/awsapi +mkdir -p ${RPM_BUILD_ROOT}/var/log/%{name}/ipallocator +mkdir -p ${RPM_BUILD_ROOT}/var/cache/%{name}/management/work +mkdir -p ${RPM_BUILD_ROOT}/var/cache/%{name}/management/temp +mkdir -p ${RPM_BUILD_ROOT}/var/lib/%{name}/mnt +mkdir -p ${RPM_BUILD_ROOT}/var/lib/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}/etc/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}/etc/%{name}/management/Catalina/localhost/client +mkdir -p ${RPM_BUILD_ROOT}/etc/rc.d/init.d +mkdir -p ${RPM_BUILD_ROOT}/etc/sysconfig +mkdir -p ${RPM_BUILD_ROOT}/etc/%{name}/management/Catalina/localhost/client + +install -D client/target/utilities/bin/* ${RPM_BUILD_ROOT}%{_bindir} +install -D console-proxy/dist/systemvm.iso ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/vms/systemvm.iso +install -D console-proxy/dist/systemvm.zip ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/vms/systemvm.zip + +cp -r client/target/utilities/scripts/db/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}/setup +cp -r client/target/cloud-client-ui-4.1.0-SNAPSHOT/* ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client + +for name in db.properties log4j-cloud.xml tomcat6-nonssl.conf tomcat6-ssl.conf server-ssl.xml server-nonssl.xml \ + catalina.policy catalina.properties db-enc.properties classpath.conf tomcat-users.xml web.xml ; do + mv ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/$name \ + ${RPM_BUILD_ROOT}/etc/%{name}/management/$name +done +mv ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/context.xml \ + ${RPM_BUILD_ROOT}/etc/%{name}/management/Catalina/localhost/client + +mkdir -p ${RPM_BUILD_ROOT}/usr/lib/python2.6/site-packages/ +cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}/usr/lib/python2.6/site-packages/ +cp -r cloud-cli/cloudtool ${RPM_BUILD_ROOT}/usr/lib/python2.6/site-packages/ +install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}/usr/lib/python2.6/site-packages/cloud_utils.py +install cloud-cli/cloudapis/cloud.py ${RPM_BUILD_ROOT}/usr/lib/python2.6/site-packages/cloudapis.py +install python/bindir/cloud-external-ipallocator.py ${RPM_BUILD_ROOT}%{_bindir}/ +install -D client/target/pythonlibs/jasypt-1.9.0.jar ${RPM_BUILD_ROOT}%{_javadir}/jasypt-1.9.0.jar +install -D client/target/pythonlibs/jasypt-1.8.jar ${RPM_BUILD_ROOT}%{_javadir}/jasypt-1.8.jar + +install -D packaging/centos63/cloud-ipallocator.rc ${RPM_BUILD_ROOT}/etc/rc.d/init.d/%{name}-ipallocator +install -D packaging/centos63/cloud-management.rc ${RPM_BUILD_ROOT}/etc/rc.d/init.d/%{name}-management +install -D packaging/centos63/cloud-management.sysconfig ${RPM_BUILD_ROOT}/etc/sysconfig/%{name}-management + +chmod 770 ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/Catalina +chmod 770 ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/Catalina/localhost +chmod 770 ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/Catalina/localhost/client +chmod 770 ${RPM_BUILD_ROOT}%{_sharedstatedir}/%{name}/mnt +chmod 770 ${RPM_BUILD_ROOT}%{_sharedstatedir}/%{name}/management +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/work +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/temp +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent +chmod -R ugo+x ${RPM_BUILD_ROOT}/usr/share/%{name}/management/webapps/client/WEB-INF/classes/scripts +%clean + +[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} + + +%preun management-server +/sbin/service %{name}-management stop || true +if [ "$1" == "0" ] ; then + /sbin/chkconfig --del %{name}-management > /dev/null 2>&1 || true + /sbin/service %{name}-management stop > /dev/null 2>&1 || true +fi + +%pre management-server +id %{name} > /dev/null 2>&1 || /usr/sbin/useradd -M -c "CloudStack unprivileged user" \ + -r -s /bin/sh -d %{_sharedstatedir}/%{name}/management %{name}|| true + +# set max file descriptors for cloud user to 4096 +sed -i /"cloud hard nofile"/d /etc/security/limits.conf +sed -i /"cloud soft nofile"/d /etc/security/limits.conf +echo "cloud hard nofile 4096" >> /etc/security/limits.conf +echo "cloud soft nofile 4096" >> /etc/security/limits.conf +rm -rf %{_localstatedir}/cache/%{name} +# user harcoded here, also hardcoded on wscript + +%post management-server +if [ "$1" == "1" ] ; then + /sbin/chkconfig --add %{name}-management > /dev/null 2>&1 || true + /sbin/chkconfig --level 345 %{name}-management on > /dev/null 2>&1 || true +fi + +if [ ! -f %{_datadir}/%{name}/management/webapps/client/WEB-INF/classes/scripts/scripts/vm/hypervisor/xenserver/vhd-util ] ; then + echo Please download vhd-util from http://download.cloud.com.s3.amazonaws.com/tools/vhd-util and put it in + echo %{_datadir}/%{name}/management/webapps/client/WEB-INF/classes/scripts/vm/hypervisor/xenserver/ +fi + +#No default permission as the permission setup is complex +%files management-server +%defattr(-,root,root,-) +%doc LICENSE +%doc NOTICE +%dir %attr(0770,root,%{name}) %{_sysconfdir}/%{name}/management/Catalina +%dir %attr(0770,root,%{name}) %{_sysconfdir}/%{name}/management/Catalina/localhost +%dir %attr(0770,root,%{name}) %{_sysconfdir}/%{name}/management/Catalina/localhost/client +%dir %{_datadir}/%{name}/management +%dir %attr(0770,root,%{name}) %{_sharedstatedir}/%{name}/mnt +%dir %attr(0770,%{name},%{name}) %{_sharedstatedir}/%{name}/management +%dir %attr(0770,root,%{name}) %{_localstatedir}/cache/%{name}/management +%dir %attr(0770,root,%{name}) %{_localstatedir}/cache/%{name}/management/work +%dir %attr(0770,root,%{name}) %{_localstatedir}/cache/%{name}/management/temp +%dir %attr(0770,root,%{name}) %{_localstatedir}/log/%{name}/management +%dir %attr(0770,root,%{name}) %{_localstatedir}/log/%{name}/agent +%config(noreplace) %{_sysconfdir}/sysconfig/%{name}-management +%config(noreplace) %{_sysconfdir}/%{name}/management +%config(noreplace) %attr(0640,root,%{name}) %{_sysconfdir}/%{name}/management/db.properties +%config(noreplace) %{_sysconfdir}/%{name}/management/log4j-%{name}.xml +%config(noreplace) %{_sysconfdir}/%{name}/management/tomcat6-nonssl.conf +%config(noreplace) %{_sysconfdir}/%{name}/management/tomcat6-ssl.conf +%attr(0755,root,root) %{_initrddir}/%{name}-management +%attr(0755,root,root) %{_bindir}/%{name}-setup-management +%attr(0755,root,root) %{_bindir}/%{name}-update-xenserver-licenses +%{_datadir}/%{name}/management/* + +%files setup +%attr(0755,root,root) %{_bindir}/%{name}-setup-databases +%attr(0755,root,root) %{_bindir}/%{name}-migrate-databases +%attr(0755,root,root) %{_bindir}/%{name}-set-guest-password +%attr(0755,root,root) %{_bindir}/%{name}-set-guest-sshkey +%attr(0755,root,root) %{_bindir}/%{name}-sysvmadm +%attr(0755,root,root) %{_bindir}/%{name}-setup-encryption +%dir %{_datadir}/%{name}/setup +%{_datadir}/%{name}/setup/*.sql +%{_datadir}/%{name}/setup/db/*.sql +%{_datadir}/%{name}/setup/*.sh +%{_datadir}/%{name}/setup/server-setup.xml +%{_javadir}/jasypt-1.9.0.jar +%{_javadir}/jasypt-1.8.jar +%doc LICENSE +%doc NOTICE + +%files python +%defattr(0644,root,root,0755) +%{_prefix}/lib*/python*/site-packages/%{name}* +%attr(0755,root,root) %{_bindir}/cloud-external-ipallocator.py +%attr(0755,root,root) %{_initrddir}/cloud-ipallocator +%dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/ipallocator +%doc LICENSE +%doc NOTICE + + +%changelog +* Fri Oct 03 2012 Hugo Trippaers 4.1.0 +- new style spec file + diff --git a/packaging/centos63/package.sh b/packaging/centos63/package.sh new file mode 100644 index 00000000000..f693f90df7b --- /dev/null +++ b/packaging/centos63/package.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +CWD=`pwd` +RPMDIR=$CWD/../../dist/rpmbuild + + + +VERSION=`(cd ../../; mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version) | grep -v '^\['` +if echo $VERSION | grep SNAPSHOT ; then + REALVER=`echo $VERSION | cut -d '-' -f 1` + DEFVER="-D_ver $REALVER" + DEFPRE="-D_prerelease 1" + DEFREL="-D_rel SNAPSHOT" +else + DEFVER="-D_ver $REALVER" + DEFPRE= + DEFREL= +fi + +mkdir -p $RPMDIR/SPECS +mkdir -p $RPMDIR/SOURCES/cloud-$VERSION + + +(cd ../../; tar -c --exclude .git --exclude dist . | tar -C $RPMDIR/SOURCES/cloud-$VERSION -x ) +(cd $RPMDIR/SOURCES/; tar -czf cloud-$VERSION.tgz cloud-$VERSION) + +cp cloud.spec $RPMDIR/SPECS + +(cd $RPMDIR; rpmbuild -ba SPECS/cloud.spec "-D_topdir $RPMDIR" "$DEFVER" "$DEFREL" "$DEFPRE" ) diff --git a/packaging/centos63/replace.properties b/packaging/centos63/replace.properties new file mode 100644 index 00000000000..e6efc76278c --- /dev/null +++ b/packaging/centos63/replace.properties @@ -0,0 +1,61 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +DBUSER=cloud +DBPW=cloud +DBROOTPW= +MSLOG=vmops.log +APISERVERLOG=api.log +DBHOST=localhost +MSMNTDIR=/mnt +COMPONENTS-SPEC=components-premium.xml +AWSAPILOG=awsapi.log +REMOTEHOST=localhost +AGENTCLASSPATH= +AGENTLOG=/var/log/cloud/agent/agent.log +AGENTLOGDIR=/var/log/cloud/agent/ +AGENTSYSCONFDIR=/etc/cloud/agent +APISERVERLOG=/var/log/cloud/management/apilog.log +AWSAPILOG=/var/log/cloud/awsapi/awsapi.log +BINDIR=/usr/bin +COMMONLIBDIR=/usr/share/java +CONFIGUREVARS= +DEPSCLASSPATH= +DOCDIR= +IPALOCATORLOG=/var/log/cloud/management/ipallocator.log +JAVADIR=/usr/share/java +LIBEXECDIR=/usr/libexec +LOCKDIR=/var/lock +MSCLASSPATH= +MSCONF=/etc/cloud/management +MSENVIRON=/usr/share/cloud/management +MSLOG=/var/log/cloud/management/management-server.log +MSLOGDIR=/var/log/cloud/management/ +MSMNTDIR=/var/lib/cloud/mnt +MSUSER=cloud +PIDDIR=/var/run +PLUGINJAVADIR= +PREMIUMJAVADIR= +PYTHONDIR=/usr/lib/python2.6/site-packages/ +SERVERSYSCONFDIR=/etc/sysconfig +SETUPDATADIR=/usr/share/cloud/setup +SYSCONFDIR=/etc/sysconfig +SYSTEMCLASSPATH= +SYSTEMJARS= +USAGECLASSPATH= +USAGELOG=/var/log/cloud/usage +USAGESYSCONFDIR=/etc/sysconfig From 95df86e1e030ab955ac09f145df37f3aef606c05 Mon Sep 17 00:00:00 2001 From: marcus Date: Tue, 30 Oct 2012 22:57:21 -0600 Subject: [PATCH 05/88] Summary: Fix virtual router trying to resolve own host name Detail: Several virtual router configuration commands, such as iptables commands, run slowly due to attempting to do a name lookup on the virtual router's hostname and having to time out. This is seen in the agent logs when a virtual router command is run, as "unable to resolve host r-410-VM" or similar. This can make for very slow router configuration, especially as the number of network rules grows. This change simply sets the router's name to the localhost IP in /etc/hosts BUG-ID : NONE Reviewed-by: Marcus Sorensen Reported-by: Marcus Sorensen Signed-off-by: Marcus Sorensen 1351659441 -0600 --- .../systemvm/debian/config/etc/init.d/cloud-early-config | 9 +++++++++ 1 file changed, 9 insertions(+) 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 7e5815682a8..d4af3a95755 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -814,9 +814,18 @@ change_password() { fi } +set_etc_hosts() { + if [ -f /etc/hosts ]; then + host=`hostname -s`; + grep -q $host /etc/hosts || echo "127.0.0.1 $host" >> /etc/hosts; + fi +} + start() { # Clear /tmp for file lock rm -f /tmp/*.lock + # fix lookups for our hostname (iptables, etc) + set_etc_hosts local hyp=$(hypervisor) [ $? -ne 0 ] && log_it "Failed to detect hypervisor type, bailing out of early init" && exit 10 log_it "Detected that we are running inside $hyp guest" From 761b4f60a10a49dc71c9aafabe4ed35e4694fd56 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Wed, 31 Oct 2012 09:01:41 -0600 Subject: [PATCH 06/88] Summary: Fix virtual router trying to resolve own host name (v2) Detail: Make change in 95df86e1e030ab955ac09f145df37f3aef606c05 be specific to VPC. BUG-ID : NONE Reviewed-by: Marcus Sorensen Reported-by: Marcus Sorensen Signed-off-by: Marcus Sorensen 1351695701 -0600 --- .../debian/config/etc/init.d/cloud-early-config | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config index d4af3a95755..258e71e24a9 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -597,6 +597,11 @@ setup_vpcrouter() { setup_vmware_extra_nics fi + if [ -f /etc/hosts ]; then + host=`hostname -s`; + grep -q $host /etc/hosts || echo "127.0.0.1 $host" >> /etc/hosts; + fi + cat > /etc/network/interfaces << EOF auto lo $1 iface lo inet loopback @@ -814,18 +819,9 @@ change_password() { fi } -set_etc_hosts() { - if [ -f /etc/hosts ]; then - host=`hostname -s`; - grep -q $host /etc/hosts || echo "127.0.0.1 $host" >> /etc/hosts; - fi -} - start() { # Clear /tmp for file lock rm -f /tmp/*.lock - # fix lookups for our hostname (iptables, etc) - set_etc_hosts local hyp=$(hypervisor) [ $? -ne 0 ] && log_it "Failed to detect hypervisor type, bailing out of early init" && exit 10 log_it "Detected that we are running inside $hyp guest" From 1c0e2cf3f5969305346cd8190f30f4813c19c6c1 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Wed, 31 Oct 2012 09:42:04 -0600 Subject: [PATCH 07/88] Summary: Implement Static NAT for KVM VPC Detail: SetVPCStaticNatRules was one command that was left off of the initial VPC implementation. This adds it in. BUG-ID : CLOUDSTACK-426 Reviewed-by: Marcus Sorensen Reported-by: Marcus Sorensen Signed-off-by: Marcus Sorensen 1351698124 -0600 --- .../VirtualRoutingResource.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 7e53f03e431..2dc7950b2b7 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -264,8 +264,35 @@ public class VirtualRoutingResource implements Manager { return new SetPortForwardingRulesAnswer(cmd, results, endResult); } + + protected Answer SetVPCStaticNatRules(SetStaticNatRulesCommand cmd) { + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + String[] results = new String[cmd.getRules().length]; + int i = 0; + boolean endResult = true; + + for (StaticNatRuleTO rule : cmd.getRules()) { + String args = rule.revoked() ? " -D" : " -A"; + args += " -l " + rule.getSrcIp(); + args += " -r " + rule.getDstIp(); + + String result = routerProxy("vpc_staticnat.sh", routerIp, args); + + if(result == null) { + results[i++] = null; + } else { + results[i++] = "Failed"; + endResult = false; + } + } + return new SetStaticNatRulesAnswer(cmd, results, endResult); + + } private Answer execute(SetStaticNatRulesCommand cmd) { + if ( cmd.getVpcId() != null ) { + return SetVPCStaticNatRules(cmd); + } String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); String[] results = new String[cmd.getRules().length]; int i = 0; From d72d3ee22bf9cb0b4381b7370b291c9f3856247f Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 22:58:06 +0530 Subject: [PATCH 08/88] marvin: check and use logger Checks and only then calls logger Signed-off-by: Rohit Yadav --- tools/marvin/marvin/cloudstackConnection.py | 24 ++++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py index d70c192406e..98c021437f7 100644 --- a/tools/marvin/marvin/cloudstackConnection.py +++ b/tools/marvin/marvin/cloudstackConnection.py @@ -69,16 +69,21 @@ class cloudConnection(object): try: self.connection = urllib2.urlopen("http://%s:%d/client/api?%s"%(self.mgtSvr, self.port, requestUrl)) - self.logging.debug("sending GET request: %s"%requestUrl) + if self.logging is not None: + self.logging.debug("sending GET request: %s"%requestUrl) response = self.connection.read() - self.logging.info("got response: %s"%response) + if self.logging is not None: + self.logging.info("got response: %s"%response) except IOError, e: if hasattr(e, 'reason'): - self.logging.critical("failed to reach %s because of %s"%(self.mgtSvr, e.reason)) + if self.logging is not None: + self.logging.critical("failed to reach %s because of %s"%(self.mgtSvr, e.reason)) elif hasattr(e, 'code'): - self.logging.critical("server returned %d error code"%e.code) + if self.logging is not None: + self.logging.critical("server returned %d error code"%e.code) except httplib.HTTPException, h: - self.logging.debug("encountered http Exception %s"%h.args) + if self.logging is not None: + self.logging.debug("encountered http Exception %s"%h.args) if self.retries > 0: self.retries = self.retries - 1 self.make_request_with_auth(command, requests) @@ -95,9 +100,11 @@ class cloudConnection(object): requestUrl = "&".join(["=".join([request[0], urllib.quote_plus(str(request[1]))]) for request in requests]) self.connection = urllib2.urlopen("http://%s:%d/client/api?%s"%(self.mgtSvr, self.port, requestUrl)) - self.logging.debug("sending GET request without auth: %s"%requestUrl) + if self.logging is not None: + self.logging.debug("sending GET request without auth: %s"%requestUrl) response = self.connection.read() - self.logging.info("got response: %s"%response) + if self.logging is not None: + self.logging.info("got response: %s"%response) return response def pollAsyncJob(self, jobId, response): @@ -114,7 +121,8 @@ class cloudConnection(object): return asyncResonse time.sleep(5) - self.logging.debug("job: %s still processing, will timeout in %ds"%(jobId, timeout)) + if self.logging is not None: + self.logging.debug("job: %s still processing, will timeout in %ds"%(jobId, timeout)) timeout = timeout - 5 raise cloudstackException.cloudstackAPIException("asyncquery", "Async job timeout %s"%jobId) From ff9e60929cf56f7a8f2290b64211060895439123 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 23:01:43 +0530 Subject: [PATCH 09/88] gitignore: ignore pythonic eggs and marvin in cli Signed-off-by: Rohit Yadav --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 1a645d9fac8..82464765226 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ dist/ cloud-*.tar.bz2 *.log *.pyc +*.egginfo/ build.number api.log.*.gz cloud.log.*.* @@ -45,6 +46,7 @@ deps/awsapi-lib/ git-remote-https.exe.stackdump *.swp tools/devcloud/devcloudbox/.vagrant +tools/cli/cloudmonkey/marvin/ *.jar *.war *.mar From 2ceaa3911e792dbeb6c40dfb70961008a01f7e3c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 23:02:30 +0530 Subject: [PATCH 10/88] cli: cloudmonkey the command line interface cloudmonkey ----------- Apache CloudStack's very own monkey powered command line interface based on Marvin. The neglected robot and monkey should rule the world! Features: - it's a shell and also a terminal tool - scalable to find and run old and new APIs - intuitive grammar and verbs - autocompletion (functional hack) - shell execution using ! or shell - cfg support: user defined variables, like prompt, ruler, host, port etc. - history - colors - dynamic API loading and rule generation - leverages Marvin to get latest autogenerated APIs - emacs like shortcuts on prompt - uses apiKey and secretKey to interact with mgmt server - logs all client commands - PEP-8 compliant code TODOs: - Reverse search - Fix input and output processing Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/__init__.py | 26 +++ tools/cli/cloudmonkey/cloudmonkey.py | 323 +++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 tools/cli/cloudmonkey/__init__.py create mode 100644 tools/cli/cloudmonkey/cloudmonkey.py diff --git a/tools/cli/cloudmonkey/__init__.py b/tools/cli/cloudmonkey/__init__.py new file mode 100644 index 00000000000..e66b2b9e6b0 --- /dev/null +++ b/tools/cli/cloudmonkey/__init__.py @@ -0,0 +1,26 @@ +# 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. + +# Use following rules for versioning: +# .. +# Example: For CloudStack 4.1.x, CLI version should be 0.1.4 +__version__ = "0.0.4" + +try: + from cloudmonkey import * +except ImportError, e: + print e diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py new file mode 100644 index 00000000000..9147d053ac1 --- /dev/null +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# 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. + +try: + import atexit + import cmd + import clint + import codecs + import logging + import os + import pdb + import readline + import rlcompleter + import sys + import types + + from clint.textui import colored + from ConfigParser import ConfigParser, SafeConfigParser + + from marvin.cloudstackConnection import cloudConnection + from marvin.cloudstackException import cloudstackAPIException + from marvin.cloudstackAPI import * + from marvin import cloudstackAPI +except ImportError, e: + print "Import error in %s : %s" % (__name__, e) + import sys + sys.exit() + +log_fmt = '%(asctime)s - %(filename)s:%(lineno)s - [%(levelname)s] %(message)s' +logger = logging.getLogger(__name__) +completions = cloudstackAPI.__all__ + + +class CloudStackShell(cmd.Cmd): + intro = "☁ Apache CloudStack CLI. Type help or ? to list commands.\n" + ruler = "-" + config_file = os.path.expanduser('~/.cloudmonkey_config') + + def __init__(self): + self.config_fields = {'host': 'localhost', 'port': '8080', + 'apiKey': '', 'secretKey': '', + 'prompt': '🙉 cloudmonkey> ', 'color': 'true', + 'log_file': + os.path.expanduser('~/.cloudmonkey_log'), + 'history_file': + os.path.expanduser('~/.cloudmonkey_history')} + if os.path.exists(self.config_file): + config = self.read_config() + else: + for key in self.config_fields.keys(): + setattr(self, key, self.config_fields[key]) + config = self.write_config() + print "Set your api and secret keys using the set command!" + + for key in self.config_fields.keys(): + setattr(self, key, config.get('CLI', key)) + + self.prompt += " " # Cosmetic fix for prompt + logging.basicConfig(filename=self.log_file, + level=logging.DEBUG, format=log_fmt) + self.logger = logging.getLogger(self.__class__.__name__) + + cmd.Cmd.__init__(self) + # Update config if config_file does not exist + if not os.path.exists(self.config_file): + config = self.write_config() + + # Fix autocompletion issue + if sys.platform == "darwin": + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab: complete") + + # Enable history support + try: + if os.path.exists(self.history_file): + readline.read_history_file(self.history_file) + atexit.register(readline.write_history_file, self.history_file) + except IOError: + print("Error: history support") + + def read_config(self): + config = ConfigParser() + try: + with open(self.config_file, 'r') as cfg: + config.readfp(cfg) + for section in config.sections(): + for option in config.options(section): + logger.debug("[%s] %s=%s" % (section, option, + config.get(section, option))) + except IOError, e: + self.print_shell("Error: config_file not found", e) + return config + + def write_config(self): + config = ConfigParser() + config.add_section('CLI') + for key in self.config_fields.keys(): + config.set('CLI', key, getattr(self, key)) + with open(self.config_file, 'w') as cfg: + config.write(cfg) + return config + + def emptyline(self): + pass + + def print_shell(self, *args): + try: + for arg in args: + if isinstance(type(args), types.NoneType): + continue + if self.color == 'true': + if str(arg).count(self.ruler) == len(str(arg)): + print colored.green(arg), + elif 'type' in arg: + print colored.green(arg), + elif 'state' in arg: + print colored.yellow(arg), + elif 'id =' in arg: + print colored.cyan(arg), + elif 'name =' in arg: + print colored.magenta(arg), + elif ':' in arg: + print colored.blue(arg), + elif 'Error' in arg: + print colored.red(arg), + else: + print arg, + else: + print arg, + print + except Exception, e: + print colored.red("Error: "), e + + # FIXME: Fix result processing and printing + def print_result(self, result, response, api_mod): + def print_result_as_list(): + if result is None: + return + for node in result: + print_result_as_instance(node) + + def print_result_as_instance(node): + for attribute in dir(response): + if "__" not in attribute: + attribute_value = getattr(node, attribute) + if isinstance(attribute_value, list): + self.print_shell("\n%s:" % attribute) + try: + self.print_result(attribute_value, + getattr(api_mod, attribute)(), + api_mod) + except AttributeError, e: + pass + elif attribute_value is not None: + self.print_shell("%s = %s" % + (attribute, attribute_value)) + self.print_shell(self.ruler * 80) + + if result is None: + return + + if type(result) is types.InstanceType: + print_result_as_instance(result) + elif isinstance(result, list): + print_result_as_list() + elif isinstance(result, str): + print result + elif isinstance(type(result), types.NoneType): + print_result_as_instance(result) + elif not (str(result) is None): + self.print_shell(result) + + def do_quit(self, s): + """ + Quit Apache CloudStack CLI + """ + self.print_shell("Bye!") + return True + + def do_shell(self, args): + """ + Execute shell commands using shell or ! + Example: !ls or shell ls + """ + os.system(args) + + def make_request(self, command, requests={}): + conn = cloudConnection(self.host, port=int(self.port), + apiKey=self.apiKey, securityKey=self.secretKey, + logging=logging.getLogger("cloudConnection")) + try: + response = conn.make_request(command, requests) + except cloudstackAPIException, e: + self.print_shell("API Error", e) + return None + return response + + def default(self, args): + args = args.split(" ") + api_name = args[0] + + try: + api_cmd_str = "%sCmd" % api_name + api_rsp_str = "%sResponse" % api_name + api_mod = __import__("marvin.cloudstackAPI.%s" % api_name, + globals(), locals(), [api_cmd_str], -1) + api_cmd = getattr(api_mod, api_cmd_str) + api_rsp = getattr(api_mod, api_rsp_str) + except ImportError, e: + self.print_shell("Error: API %s not found!" % e) + return + + command = api_cmd() + response = api_rsp() + #FIXME: Parsing logic + args_dict = dict(map(lambda x: x.split("="), + args[1:])[x] for x in range(len(args) - 1)) + + for attribute in dir(command): + if attribute in args_dict: + setattr(command, attribute, args_dict[attribute]) + + result = self.make_request(command, response) + try: + self.print_result(result, response, api_mod) + except Exception as e: + self.print_shell("🙈 Error on parsing and printing", e) + + def completedefault(self, text, line, begidx, endidx): + mline = line.partition(" ")[2] + offs = len(mline) - len(text) + return [s[offs:] for s in completions if s.startswith(mline)] + + def do_api(self, args): + """ + Make raw api calls. Syntax: api =. Example: + api listAccount listall=true + """ + if len(args) > 0: + return self.default(args) + else: + self.print_shell("Please use a valid syntax") + + def complete_api(self, text, line, begidx, endidx): + return self.completedefault(text, line, begidx, endidx) + + def do_set(self, args): + """ + Set config for CloudStack CLI. Available options are: + host, port, apiKey, secretKey, log_file, history_file + """ + args = args.split(' ') + if len(args) == 2: + key, value = args + # Note: keys and fields should have same names + setattr(self, key, value) + self.write_config() + else: + self.print_shell("Please use the syntax: set valid-key value") + + def complete_set(self, text, line, begidx, endidx): + mline = line.partition(" ")[2] + offs = len(mline) - len(text) + return [s[offs:] for s in + ['host', 'port', 'apiKey', 'secretKey', 'prompt', 'color', + 'log_file', 'history_file'] if s.startswith(mline)] + + +def main(): + grammar = ['list', 'create', 'delete', 'update', 'disable', 'enable', + 'add', 'remove'] + self = CloudStackShell + for rule in grammar: + setattr(self, 'completions_' + rule, map(lambda x: x.replace(rule, ''), + filter(lambda x: rule in x, + completions))) + + def add_grammar(rule): + def grammar_closure(self, args): + self.default(rule + args) + return grammar_closure + + grammar_handler = add_grammar(rule) + grammar_handler.__doc__ = "%ss resources" % rule.capitalize() + grammar_handler.__name__ = 'do_' + rule + setattr(self, grammar_handler.__name__, grammar_handler) + + def add_completer(rule): + def completer_closure(self, text, line, begidx, endidx): + mline = line.partition(" ")[2] + offs = len(mline) - len(text) + return [s[offs:] for s in getattr(self, 'completions_' + rule) + if s.startswith(mline)] + return completer_closure + + completion_handler = add_completer(rule) + completion_handler.__name__ = 'complete_' + rule + setattr(self, completion_handler.__name__, completion_handler) + + if len(sys.argv) > 1: + CloudStackShell().onecmd(' '.join(sys.argv[1:])) + else: + CloudStackShell().cmdloop() + +if __name__ == "__main__": + main() From 039b938ecc552aca8f782bb0bda2abb6f8b3b6be Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 23:10:55 +0530 Subject: [PATCH 11/88] maven: pom.xml for tools/cli copies marvin to cli/cloudmonkey/ Signed-off-by: Rohit Yadav --- tools/cli/pom.xml | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tools/cli/pom.xml diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml new file mode 100644 index 00000000000..e7d435bacc9 --- /dev/null +++ b/tools/cli/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + cloud-cli + Apache CloudStack Developer Tools: cloudmonkey cli + pom + + org.apache.cloudstack + cloudstack + 4.1.0-SNAPSHOT + ../../pom.xml + + + + install + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + compile + compile + + exec + + + ${basedir} + cp + + -rv + ${basedir}/../marvin/marvin + ${basedir}/cloudmonkey + + + + + package + compile + + exec + + + ${basedir} + python + + setup.py + sdist + + + + + + + + From b38cf801ac1edf9257c919ebe29ff9115dcf0c0f Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 23:11:35 +0530 Subject: [PATCH 12/88] cli: setup.py for cloudmonkey Version are named in reflection of CloudStack. .. So, for CloudStack 4.1.0, version for cli should be 0.1.4 To create distribution: python setup.py sdist python setup.py install Using pip: pip install dist/cloudmonkey* Or pretty soon, from cheese shop: pip install cloudmonkey Signed-off-by: Rohit Yadav --- tools/cli/setup.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tools/cli/setup.py diff --git a/tools/cli/setup.py b/tools/cli/setup.py new file mode 100644 index 00000000000..fdbdb2ba0d3 --- /dev/null +++ b/tools/cli/setup.py @@ -0,0 +1,59 @@ +# 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. + +try: + from setuptools import setup, find_packages +except ImportError: + from distribute_setup import use_setuptools + use_setuptools() + from setuptools import setup, find_packages + +from cloudmonkey import __version__ + +name = 'cloudmonkey' +version = __version__ + +setup( + name = name, + version = version, + author = "The Apache CloudStack Team", + author_email = "cloudstack-dev@incubator.apache.org", + maintainer = "Rohit Yadav", + maintainer_email = "bhaisaab@apache.org", + url = "http://incubator.apache.org/cloudstack", + description = "Command Line Interface for Apache CloudStack", + license = 'ASL 2.0', + packages=find_packages(), + install_requires=['clint'], + include_package_data = True, + zip_safe = False, + classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Software Development :: Testing", + "Topic :: Software Development :: Interpreters", + "Topic :: Utilities", + ], + entry_points=""" + [console_scripts] + cloudmonkey = cloudmonkey:main + """, +) From 23659684e8fcf1a9f848ab44b9ccf76a286f8047 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 31 Oct 2012 23:14:54 +0530 Subject: [PATCH 13/88] maven: include tools/cli in top level pom For developer profile include tools/cli in top level pom Signed-off-by: Rohit Yadav --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index fe1064cacca..4b563b3b39e 100644 --- a/pom.xml +++ b/pom.xml @@ -341,9 +341,10 @@ developer developer - tools/apidoc - tools/devcloud - tools/marvin + tools/apidoc + tools/devcloud + tools/marvin + tools/cli From 000cc7204239e4c90dfcdae479a38a9683936dd8 Mon Sep 17 00:00:00 2001 From: Chip Childers Date: Mon, 29 Oct 2012 11:02:36 -0400 Subject: [PATCH 14/88] CLOUDSTACK-422: XSL files missing license header Added license headers to the apidoc XSL files that were fooling RAT. Signed-off-by: Chip Childers --- tools/apidoc/generateadmincommands.xsl | 18 ++++++++++++++++++ tools/apidoc/generatecommand.xsl | 18 ++++++++++++++++++ tools/apidoc/generatedomainadmincommands.xsl | 18 ++++++++++++++++++ tools/apidoc/generatetoc_footer.xsl | 18 ++++++++++++++++++ tools/apidoc/generateusercommands.xsl | 18 ++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/tools/apidoc/generateadmincommands.xsl b/tools/apidoc/generateadmincommands.xsl index 5f576c0f887..bb5dc51774d 100644 --- a/tools/apidoc/generateadmincommands.xsl +++ b/tools/apidoc/generateadmincommands.xsl @@ -1,4 +1,22 @@ + diff --git a/tools/apidoc/generatecommand.xsl b/tools/apidoc/generatecommand.xsl index 0d2e8622711..b665cf36f7d 100644 --- a/tools/apidoc/generatecommand.xsl +++ b/tools/apidoc/generatecommand.xsl @@ -1,4 +1,22 @@ + diff --git a/tools/apidoc/generatedomainadmincommands.xsl b/tools/apidoc/generatedomainadmincommands.xsl index 7d60ef2b91f..de67a056f86 100644 --- a/tools/apidoc/generatedomainadmincommands.xsl +++ b/tools/apidoc/generatedomainadmincommands.xsl @@ -1,4 +1,22 @@ + diff --git a/tools/apidoc/generatetoc_footer.xsl b/tools/apidoc/generatetoc_footer.xsl index 5bc2cf46659..cf6dbc4c7b3 100644 --- a/tools/apidoc/generatetoc_footer.xsl +++ b/tools/apidoc/generatetoc_footer.xsl @@ -1,3 +1,21 @@ + diff --git a/tools/apidoc/generateusercommands.xsl b/tools/apidoc/generateusercommands.xsl index ca0eb1f6eaf..fe8ac91d890 100644 --- a/tools/apidoc/generateusercommands.xsl +++ b/tools/apidoc/generateusercommands.xsl @@ -1,4 +1,22 @@ + From 713418c0aaafbe297929f275e646198d5a29b0c5 Mon Sep 17 00:00:00 2001 From: Chip Childers Date: Wed, 31 Oct 2012 14:29:38 -0400 Subject: [PATCH 15/88] Fixed license headers in 2 files Signed-off-by: Chip Childers --- packaging/centos63/package.sh | 16 ++++++++++++++++ .../server/auth/test/AuthenticatorTest.java | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packaging/centos63/package.sh b/packaging/centos63/package.sh index f693f90df7b..83c0e5eda4c 100644 --- a/packaging/centos63/package.sh +++ b/packaging/centos63/package.sh @@ -1,4 +1,20 @@ #!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. CWD=`pwd` RPMDIR=$CWD/../../dist/rpmbuild diff --git a/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java b/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java index cf990248f39..4e23d14fe43 100644 --- a/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java +++ b/plugins/user-authenticators/sha256salted/test/src/com/cloud/server/auth/test/AuthenticatorTest.java @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package src.com.cloud.server.auth.test; import static org.junit.Assert.*; From bfbf634c0fd4fc2eadcaf15f39cec63f588fe7c4 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 29 Oct 2012 17:03:15 -0700 Subject: [PATCH 16/88] CS-16573: cloudstack UI - user page - listView - encode data passed to API call. --- ui/scripts/accounts.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index a74389d505f..0ba3e3ffa7d 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -787,24 +787,19 @@ firstname: { label: 'label.first.name' }, lastname: { label: 'label.last.name' } }, - dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } - + dataProvider: function(args) { var accountObj = args.context.accounts[0]; + if(isAdmin() || isDomainAdmin()) { + var data = { + domainid: accountObj.domainid, + account: accountObj.name + }; + listViewDataProvider(args, data); + $.ajax({ - url: createURL("listUsers&domainid=" + accountObj.domainid + "&account=" + todb(accountObj.name) + "&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", + url: createURL('listUsers'), + data: data, success: function(json) { args.response.success({ actionFilter: userActionfilter, From 8692f68603dd45d6f1c2a470db94365dfd5262d7 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 15:13:44 -0700 Subject: [PATCH 17/88] CS-16573: cloudstack UI - create user - encode data passed to API call. --- ui/scripts/accounts.js | 52 +++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index 0ba3e3ffa7d..907546279dc 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -888,29 +888,39 @@ action: function(args) { var accountObj = args.context.accounts[0]; - var array1 = []; - array1.push("&username=" + todb(args.data.username)); - + var data = { + username: args.data.username + }; + var password = args.data.password; - if (md5Hashed) - password = $.md5(password); - else - password = todb(password); - array1.push("&password=" + password); - - array1.push("&email=" + todb(args.data.email)); - array1.push("&firstname=" + todb(args.data.firstname)); - array1.push("&lastname=" + todb(args.data.lastname)); - if(args.data.timezone != null && args.data.timezone.length > 0) - array1.push("&timezone=" + todb(args.data.timezone)); - - array1.push("&domainid=" + accountObj.domainid); - array1.push("&account=" + todb(accountObj.name)); - array1.push("&accounttype=" + accountObj.accounttype); - + if (md5Hashed) { + password = $.md5(password); + } + $.extend(data, { + password: password + }); + + $.extend(data, { + email: args.data.email, + firstname: args.data.firstname, + lastname: args.data.lastname + }); + + if(args.data.timezone != null && args.data.timezone.length > 0) { + $.extend(data, { + timezone: args.data.timezone + }); + } + + $.extend(data, { + domainid: accountObj.domainid, + account: accountObj.name, + accounttype: accountObj.accounttype + }); + $.ajax({ - url: createURL("createUser" + array1.join("")), - dataType: "json", + url: createURL('createUser'), + data: data, success: function(json) { var item = json.createuserresponse.user; args.response.success({data: item}); From 703c5b44f21128df4b3990c9fdfa2e9bb0d06f93 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 15:45:59 -0700 Subject: [PATCH 18/88] cloudstack UI - user page - update user, change password, generate key, disable user, enable user, delete user - encode data passed to API call. --- ui/scripts/accounts.js | 62 ++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index 907546279dc..324c5f56988 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -947,15 +947,17 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&username=" + todb(args.data.username)); - array1.push("&email=" + todb(args.data.email)); - array1.push("&firstname=" + todb(args.data.firstname)); - array1.push("&lastname=" + todb(args.data.lastname)); - array1.push("&timezone=" + todb(args.data.timezone)); + var data = { + id: args.context.users[0].id, + username: args.data.username, + email: args.data.email, + firstname: args.data.firstname, + lastname: args.data.lastname, + timezone: args.data.timezone + }; $.ajax({ - url: createURL("updateUser&id=" + args.context.users[0].id + array1.join("")), - dataType: "json", + url: createURL('updateUser'), + data: data, success: function(json) { var item = json.updateuserresponse.user; args.response.success({data:item}); @@ -998,11 +1000,15 @@ var password = args.data.newPassword; if (md5Hashed) password = $.md5(password); - else - password = todb(password); + + var data = { + id: args.context.users[0].id, + password: password + }; + $.ajax({ - url: createURL("updateUser&id=" + args.context.users[0].id + "&password=" + password), - dataType: "json", + url: createURL('updateUser'), + data: data, async: true, success: function(json) { args.response.success({data: json.updateuserresponse.user}); @@ -1027,10 +1033,12 @@ } }, action: function(args) { + var data = { + id: args.context.users[0].id + }; $.ajax({ - url: createURL("registerUserKeys&id=" + args.context.users[0].id), - dataType: "json", - async: true, + url: createURL('registerUserKeys'), + data: data, success: function(json) { args.response.success({data: json.registeruserkeysresponse.userkeys}); } @@ -1054,10 +1062,12 @@ } }, action: function(args) { + var data = { + id: args.context.users[0].id + }; $.ajax({ - url: createURL("disableUser&id=" + args.context.users[0].id), - dataType: "json", - async: true, + url: createURL('disableUser'), + data: data, success: function(json) { var jid = json.disableuserresponse.jobid; args.response.success( @@ -1091,10 +1101,12 @@ } }, action: function(args) { + var data = { + id: args.context.users[0].id + }; $.ajax({ - url: createURL("enableUser&id=" + args.context.users[0].id), - dataType: "json", - async: true, + url: createURL('enableUser'), + data: data, success: function(json) { args.response.success({data: json.enableuserresponse.user}); }, @@ -1121,10 +1133,12 @@ } }, action: function(args) { + var data = { + id: args.context.users[0].id + }; $.ajax({ - url: createURL("deleteUser&id=" + args.context.users[0].id), - dataType: "json", - async: true, + url: createURL('deleteUser'), + data: data, success: function(json) { args.response.success(); } From ae79a1ebbd8cd4ce1fdada299c0f54c6b4c64a60 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 15:54:39 -0700 Subject: [PATCH 19/88] cloudstack UI - domain page - create domain - encode data passed to API call. --- ui/scripts/domains.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js index e6633397925..991e37d7324 100644 --- a/ui/scripts/domains.js +++ b/ui/scripts/domains.js @@ -193,16 +193,20 @@ label: 'label.add.domain', action: function(args) { - var array1 = []; - array1.push("&parentdomainid=" + args.context.domains[0].id); - array1.push("&name=" + todb(args.data.name)); - if(args.data.networkdomain != null && args.data.networkdomain.length > 0) - array1.push("&networkdomain=" + todb(args.data.networkdomain)); + var data = { + parentdomainid: args.context.domains[0].id, + name: args.data.name + }; + + if(args.data.networkdomain != null && args.data.networkdomain.length > 0) { + $.extend(data, { + networkdomain: args.data.networkdomain + }); + } $.ajax({ - url: createURL("createDomain" + array1.join("")), - dataType: "json", - async: false, + url: createURL('createDomain'), + data: data, success: function(json) { var item = json.createdomainresponse.domain; args.response.success({data: item}); From b2656b5ce2204117ec9c8bb741bc4231bf6c61c8 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 16:10:23 -0700 Subject: [PATCH 20/88] CS-16573: cloudstack UI - global settings page, Hypervisor Capabilities page - encode data passed to API call. --- ui/scripts/globalSettings.js | 47 +++++++++++++++--------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/ui/scripts/globalSettings.js b/ui/scripts/globalSettings.js index 0ae73ca9c5f..e65a00b290c 100644 --- a/ui/scripts/globalSettings.js +++ b/ui/scripts/globalSettings.js @@ -30,16 +30,14 @@ actions: { edit: { label: 'label.change.value', - action: function(args) { - var name = args.data.jsonObj.name; - var value = args.data.value; - + action: function(args) { + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; $.ajax({ - url: createURL( - 'updateConfiguration&name=' + name + '&value=' + value - ), - dataType: 'json', - async: true, + url: createURL('updateConfiguration'), + data: data, success: function(json) { var item = json.updateconfigurationresponse.configuration; if(item.category == "Usage") @@ -95,22 +93,12 @@ maxguestslimit: { label: 'label.max.guest.limit' } }, dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } - + var data = {}; + listViewDataProvider(args, data); + $.ajax({ - url: createURL("listHypervisorCapabilities&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", - async: true, + url: createURL('listHypervisorCapabilities'), + data: data, success: function(json) { var items = json.listhypervisorcapabilitiesresponse.hypervisorCapabilities; args.response.success({data:items}); @@ -127,11 +115,14 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&maxguestslimit=" + todb(args.data.maxguestslimit)); + var data = { + id: args.context.hypervisorCapabilities[0].id, + maxguestslimit: args.data.maxguestslimit + }; + $.ajax({ - url: createURL("updateHypervisorCapabilities&id=" + args.context.hypervisorCapabilities[0].id + array1.join("")), - dataType: "json", + url: createURL('updateHypervisorCapabilities'), + data: data, success: function(json) { var item = json.updatehypervisorcapabilitiesresponse['null']; args.response.success({data: item}); From ab4f7079362eea02a8e681e751684033e92c947c Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 16:23:57 -0700 Subject: [PATCH 21/88] CS-16573: cloudstack UI - compute offering page - encode data passed to API call. --- ui/scripts/configuration.js | 118 +++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 6b41cb995a6..a4ba018d5f9 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -161,34 +161,51 @@ }, action: function(args) { - var array1 = []; - array1.push("&name=" + args.data.name); - array1.push("&displaytext=" + todb(args.data.description)); - array1.push("&storageType=" + todb(args.data.storageType)); - array1.push("&cpuNumber=" + args.data.cpuNumber); - array1.push("&cpuSpeed="+ args.data.cpuSpeed); - array1.push("&memory=" + args.data.memory); + var data = { + issystem: false, + name: args.data.name, + displaytext: args.data.description, + storageType: args.data.storageType, + cpuNumber: args.data.cpuNumber, + cpuSpeed: args.data.cpuSpeed, + memory: args.data.memory + }; + + if(args.data.networkRate != null && args.data.networkRate.length > 0) { + $.extend(data, { + networkrate: args.data.networkRate + }); + } - if(args.data.networkRate != null && args.data.networkRate.length > 0) - array1.push("&networkrate=" + args.data.networkRate); - - array1.push("&offerha=" + (args.data.offerHA == "on")); - - if(args.data.storageTags != null && args.data.storageTags.length > 0) - array1.push("&tags=" + todb(args.data.storageTags)); - - if(args.data.hostTags != null && args.data.hostTags.length > 0) - array1.push("&hosttags=" + todb(args.data.hostTags)); - - array1.push("&limitcpuuse=" + (args.data.cpuCap == "on")); - - if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") - array1.push("&domainid=" + args.data.domainId); + $.extend(data, { + offerha: (args.data.offerHA == "on") + }); + + if(args.data.storageTags != null && args.data.storageTags.length > 0) { + $.extend(data, { + tags: args.data.storageTags + }); + } + + if(args.data.hostTags != null && args.data.hostTags.length > 0) { + $.extend(data, { + hosttags: args.data.hostTags + }); + } + + $.extend(data, { + limitcpuuse: (args.data.cpuCap == "on") + }); + + if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") { + $.extend(data, { + domainid: args.data.domainId + }); + } $.ajax({ - url: createURL("createServiceOffering&issystem=false"+array1.join("")), - dataType: "json", - async: true, + url: createURL('createServiceOffering'), + data: data, success: function(json) { var item = json.createserviceofferingresponse.serviceoffering; args.response.success({data: item}); @@ -208,22 +225,16 @@ }, dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } + var data = {}; + listViewDataProvider(args, data); + + $.extend(data, { + issystem: false + }); $.ajax({ - url: createURL("listServiceOfferings&issystem=false&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", - async: true, + url: createURL('listServiceOfferings'), + data: data, success: function(json) { var items = json.listserviceofferingsresponse.serviceoffering; args.response.success({ @@ -243,12 +254,14 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&name=" + todb(args.data.name)); - array1.push("&displaytext=" + todb(args.data.displaytext)); + var data = { + id: args.context.serviceOfferings[0].id, + name: args.data.name, + displaytext: args.data.displaytext + }; $.ajax({ - url: createURL("updateServiceOffering&id=" + args.context.serviceOfferings[0].id + array1.join("")), - dataType: "json", + url: createURL('updateServiceOffering'), + data: data, success: function(json) { var item = json.updateserviceofferingresponse.serviceoffering; args.response.success({data: item}); @@ -271,9 +284,12 @@ } }, action: function(args) { + var data = { + id: args.context.serviceOfferings[0].id + }; $.ajax({ - url: createURL("deleteServiceOffering&id=" + args.context.serviceOfferings[0].id), - dataType: "json", + url: createURL('deleteServiceOffering'), + data: data, async: true, success: function(json) { args.response.success(); @@ -340,10 +356,14 @@ } ], - dataProvider: function(args) { + dataProvider: function(args) { + var data = { + issystem: false, + id: args.context.serviceOfferings[0].id + }; $.ajax({ - url: createURL("listServiceOfferings&issystem=false&id=" + args.context.serviceOfferings[0].id), - dataType: "json", + url: createURL('listServiceOfferings'), + data: data, async: true, success: function(json) { var item = json.listserviceofferingsresponse.serviceoffering[0]; From 489b60e3d528de1bd63ae6d3d2e17bef9248bf0f Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 31 Oct 2012 11:45:56 -0700 Subject: [PATCH 22/88] CS-16573: cloudstack UI - system offering page, disk offering page - encode data passed to API call. --- ui/scripts/configuration.js | 206 ++++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 91 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index a4ba018d5f9..8abf4255276 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -528,35 +528,52 @@ }, action: function(args) { - var array1 = []; - array1.push("&name=" + args.data.name); - array1.push("&displaytext=" + todb(args.data.description)); - array1.push("&systemvmtype=" + todb(args.data.systemvmtype)); - array1.push("&storageType=" + todb(args.data.storageType)); - array1.push("&cpuNumber=" + args.data.cpuNumber); - array1.push("&cpuSpeed="+ args.data.cpuSpeed); - array1.push("&memory=" + args.data.memory); + var data = { + issystem: true, + name: args.data.name, + displaytext: args.data.description, + systemvmtype: args.data.systemvmtype, + storageType: args.data.storageType, + cpuNumber: args.data.cpuNumber, + cpuSpeed: args.data.cpuSpeed, + memory: args.data.memory + }; - if(args.data.networkRate != null && args.data.networkRate.length > 0) - array1.push("&networkrate=" + args.data.networkRate); + if(args.data.networkRate != null && args.data.networkRate.length > 0) { + $.extend(data, { + networkrate: args.data.networkRate + }); + } - array1.push("&offerha=" + (args.data.offerHA == "on")); + $.extend(data, { + offerha: (args.data.offerHA == "on") + }); + + if(args.data.storageTags != null && args.data.storageTags.length > 0) { + $.extend(data, { + tags: args.data.storageTags + }); + } - if(args.data.storageTags != null && args.data.storageTags.length > 0) - array1.push("&tags=" + todb(args.data.storageTags)); + if(args.data.hostTags != null && args.data.hostTags.length > 0) { + $.extend(data, { + hosttags: args.data.hostTags + }); + } - if(args.data.hostTags != null && args.data.hostTags.length > 0) - array1.push("&hosttags=" + todb(args.data.hostTags)); - - array1.push("&limitcpuuse=" + (args.data.cpuCap == "on")); - - if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") - array1.push("&domainid=" + args.data.domainId); + $.extend(data, { + limitcpuuse: (args.data.cpuCap == "on") + }); + + if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") { + $.extend(data, { + domainid: args.data.domainId + }); + } $.ajax({ - url: createURL("createServiceOffering&issystem=true"+array1.join("")), - dataType: "json", - async: true, + url: createURL('createServiceOffering'), + data: data, success: function(json) { var item = json.createserviceofferingresponse.serviceoffering; args.response.success({data: item}); @@ -576,22 +593,16 @@ }, dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } - + var data = {}; + listViewDataProvider(args, data); + + $.extend(data, { + issystem: true + }); + $.ajax({ - url: createURL("listServiceOfferings&issystem=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", - async: true, + url: createURL('listServiceOfferings'), + data: data, success: function(json) { var items = json.listserviceofferingsresponse.serviceoffering; args.response.success({data:items}); @@ -608,12 +619,14 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&name=" + todb(args.data.name)); - array1.push("&displaytext=" + todb(args.data.displaytext)); + var data = { + id: args.context.systemServiceOfferings[0].id, + name: args.data.name, + displaytext: args.data.displaytext + }; $.ajax({ - url: createURL("updateServiceOffering&id=" + args.context.systemServiceOfferings[0].id + array1.join("")), - dataType: "json", + url: createURL('updateServiceOffering'), + data: data, success: function(json) { var item = json.updateserviceofferingresponse.serviceoffering; args.response.success({data: item}); @@ -636,10 +649,12 @@ } }, action: function(args) { + var data = { + id: args.context.systemServiceOfferings[0].id + }; $.ajax({ - url: createURL("deleteServiceOffering&id=" + args.context.systemServiceOfferings[0].id), - dataType: "json", - async: true, + url: createURL('deleteServiceOffering'), + data: data, success: function(json) { args.response.success(); }, @@ -723,11 +738,14 @@ } ], - dataProvider: function(args) { + dataProvider: function(args) { + var data = { + issystem: true, + id: args.context.systemServiceOfferings[0].id + }; $.ajax({ - url: createURL("listServiceOfferings&issystem=true&id=" + args.context.systemServiceOfferings[0].id), - dataType: "json", - async: true, + url: createURL('listServiceOfferings'), + data: data, success: function(json) { var item = json.listserviceofferingsresponse.serviceoffering[0]; args.response.success({ @@ -770,22 +788,12 @@ reorder: cloudStack.api.actions.sort('updateDiskOffering', 'diskOfferings'), dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } - + var data = {}; + listViewDataProvider(args, data); + $.ajax({ - url: createURL("listDiskOfferings&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", - async: true, + url: createURL('listDiskOfferings'), + data: data, success: function(json) { var items = json.listdiskofferingsresponse.diskoffering; args.response.success({data:items}); @@ -880,25 +888,35 @@ }, action: function(args) { - var array1 = []; - array1.push("&name=" + args.data.name); - array1.push("&displaytext=" + todb(args.data.description)); + var data = { + isMirrored: false, + name: args.data.name, + displaytext: args.data.description, + storageType: args.data.storageType, + customized: (args.data.isCustomized=="on") + }; + + if(args.$form.find('.form-item[rel=disksize]').css("display") != "none") { + $.extend(data, { + disksize: args.data.disksize + }); + } - array1.push("&storageType=" + todb(args.data.storageType)); - array1.push("&customized=" + (args.data.isCustomized=="on")); - if(args.$form.find('.form-item[rel=disksize]').css("display") != "none") - array1.push("&disksize=" + args.data.disksize); + if(args.data.tags != null && args.data.tags.length > 0) { + $.extend(data, { + tags: args.data.tags + }); + } - if(args.data.tags != null && args.data.tags.length > 0) - array1.push("&tags=" + todb(args.data.tags)); - - if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") - array1.push("&domainid=" + args.data.domainId); + if(args.$form.find('.form-item[rel=domainId]').css("display") != "none") { + $.extend(data, { + domainid: args.data.domainId + }); + } $.ajax({ - url: createURL("createDiskOffering&isMirrored=false" + array1.join("")), - dataType: "json", - async: true, + url: createURL('createDiskOffering'), + data: data, success: function(json) { var item = json.creatediskofferingresponse.diskoffering; args.response.success({data: item}); @@ -923,12 +941,14 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&name=" + todb(args.data.name)); - array1.push("&displaytext=" + todb(args.data.displaytext)); + var data = { + id: args.context.diskOfferings[0].id, + name: args.data.name, + displaytext: args.data.displaytext + }; $.ajax({ - url: createURL("updateDiskOffering&id=" + args.context.diskOfferings[0].id + array1.join("")), - dataType: "json", + url: createURL('updateDiskOffering'), + data: data, success: function(json) { var item = json.updatediskofferingresponse.diskoffering; args.response.success({data: item}); @@ -951,10 +971,12 @@ } }, action: function(args) { + var data = { + id: args.context.diskOfferings[0].id + }; $.ajax({ - url: createURL("deleteDiskOffering&id=" + args.context.diskOfferings[0].id), - dataType: "json", - async: true, + url: createURL('deleteDiskOffering'), + data: data, success: function(json) { args.response.success(); }, @@ -1010,10 +1032,12 @@ ], dataProvider: function(args) { + var data = { + id: args.context.diskOfferings[0].id + }; $.ajax({ - url: createURL("listDiskOfferings&id=" + args.context.diskOfferings[0].id), - dataType: "json", - async: true, + url: createURL('listDiskOfferings'), + data: data, success: function(json) { var item = json.listdiskofferingsresponse.diskoffering[0]; args.response.success({ From e7fad4199488899a3c1914fa00b63bd43ffaf2d1 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 31 Oct 2012 11:53:59 -0700 Subject: [PATCH 23/88] CS-16573: cloudstack UI - update network offering - encode data passed to API call. --- ui/scripts/configuration.js | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 8abf4255276..dd6b691eee2 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1072,26 +1072,12 @@ }, dataProvider: function(args) { - var array1 = []; - if(args.filterBy != null) { - if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch(args.filterBy.search.by) { - case "name": - if(args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; - } - } - } + var data = {}; + listViewDataProvider(args, data); $.ajax({ - url: createURL('listNetworkOfferings' + array1.join("")), - data: { - page: args.page, - pagesize: pageSize - }, - dataType: "json", - async: true, + url: createURL('listNetworkOfferings'), + data: data, success: function(json) { var items = json.listnetworkofferingsresponse.networkoffering; @@ -1787,13 +1773,16 @@ edit: { label: 'label.edit', action: function(args) { - var array1 = []; - array1.push("&name=" + todb(args.data.name)); - array1.push("&displaytext=" + todb(args.data.displaytext)); - array1.push("&availability=" + args.data.availability); + var data = { + id: args.context.networkOfferings[0].id, + name: args.data.name, + displaytext: args.data.displaytext, + availability: args.data.availability + }; + $.ajax({ - url: createURL("updateNetworkOffering&id=" + args.context.networkOfferings[0].id + array1.join("")), - dataType: "json", + url: createURL('updateNetworkOffering'), + data: data, success: function(json) { //if availability is being updated from Required to Optional if(args.context.networkOfferings[0].availability == "Required" && args.data.availability == "Optional") From f39cd839996d04e74952cee9a3e01a354f296261 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Wed, 31 Oct 2012 17:53:56 -0500 Subject: [PATCH 24/88] Minor doc cleanup. --- docs/en-US/configure-package-repository.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en-US/configure-package-repository.xml b/docs/en-US/configure-package-repository.xml index 7687fa392ff..81df446225b 100644 --- a/docs/en-US/configure-package-repository.xml +++ b/docs/en-US/configure-package-repository.xml @@ -42,7 +42,7 @@
DEB package repository - You can add a DEB package repository to your apt sources with the following commands. Please note that currently only packages for Ubuntu 12.04 LTS (precise) are being build. + You can add a DEB package repository to your apt sources with the following commands. Please note that only packages for Ubuntu 12.04 LTS (precise) are being built at this time. Use your preferred editor and open (or create) /etc/apt/sources.list.d/cloudstack. Add the community provided repository to the file: deb http://cloudstack.apt-get.eu/ubuntu precise 4.0 We now have to add the public key to the trusted keys. @@ -53,6 +53,7 @@
RPM package repository + We expect that CloudStack will have a Yum repository with packages for supported RPM-based systems on or near the 4.0.0-incubating release. If you're using an RPM-based system, you'll want to add the Yum repository so that you can install CloudStack with Yum. Yum repository information is found under /etc/yum.repos.d. You'll see several .repo files in this directory, each one denoting a specific repository. To add the CloudStack repository, visit the downloads page for the repository information. It will look something like this: From 8dd1991e6d216cabab385c6c9711d7b03d17f4ac Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 31 Oct 2012 12:16:09 -0700 Subject: [PATCH 25/88] CS-16662: cloudstack UI - IP Address page - VPN tab - pass domainId/account parameter to removeVpnUser API. --- ui/scripts/network.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 08307010482..ef6eaa043d8 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -3072,10 +3072,9 @@ $.ajax({ url: createURL('removeVpnUser'), data: { - username: args.context.multiRule[0].username, - id: args.context.multiRule[0].domainid, - account: args.context.ipAddresses[0].account, - domainid: args.context.ipAddresses[0].domainid + domainid: args.context.multiRule[0].domainid, + account: args.context.multiRule[0].account, + username: args.context.multiRule[0].username }, dataType: 'json', async: true, From 51a5377d05c557e20ed1768e1a8d9ac2fe60cfec Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 1 Nov 2012 14:12:08 +0530 Subject: [PATCH 26/88] cli: add more verbs to grammar Adds additional verbs to grammar of cli Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 9147d053ac1..ba15001f282 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -284,8 +284,14 @@ class CloudStackShell(cmd.Cmd): def main(): - grammar = ['list', 'create', 'delete', 'update', 'disable', 'enable', - 'add', 'remove'] + grammar = ['create', 'list', 'delete', 'update', + 'enable', 'disable', 'add', 'remove', 'attach', 'detach', + 'assign', 'authorize', 'change', 'register', + 'start', 'restart', 'reboot', 'stop', 'reconnect', + 'cancel', 'destroy', 'revoke', + 'copy', 'extract', 'migrate', 'restore', + 'get', 'prepare', 'deploy', 'upload'] + self = CloudStackShell for rule in grammar: setattr(self, 'completions_' + rule, map(lambda x: x.replace(rule, ''), From db6c8f0176e9c7f51d4dd22f6e8a85cf50f4e084 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 1 Nov 2012 14:12:49 -0500 Subject: [PATCH 27/88] CLOUDSTACK-439: Fixed typos and changed direct attached to shared network IPs --- .../src/com/cloud/alert/AlertManagerImpl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/src/com/cloud/alert/AlertManagerImpl.java b/server/src/com/cloud/alert/AlertManagerImpl.java index 9c0cbb322fb..3909f13ea94 100755 --- a/server/src/com/cloud/alert/AlertManagerImpl.java +++ b/server/src/com/cloud/alert/AlertManagerImpl.java @@ -560,35 +560,35 @@ public class AlertManagerImpl implements AlertManager { //Cluster Level case CapacityVO.CAPACITY_TYPE_MEMORY: - msgSubject = "System Alert: Low Available Memory in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Low Available Memory in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); msgContent = "System memory is low, total: " + totalStr + " MB, used: " + usedStr + " MB (" + pctStr + "%)"; alertType = ALERT_TYPE_MEMORY; break; case CapacityVO.CAPACITY_TYPE_CPU: - msgSubject = "System Alert: Low Unallocated CPU in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Low Unallocated CPU in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = _dfWhole.format(totalCapacity); usedStr = _dfWhole.format(usedCapacity); msgContent = "Unallocated CPU is low, total: " + totalStr + " Mhz, used: " + usedStr + " Mhz (" + pctStr + "%)"; alertType = ALERT_TYPE_CPU; break; case CapacityVO.CAPACITY_TYPE_STORAGE: - msgSubject = "System Alert: Low Available Storage in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Low Available Storage in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); msgContent = "Available storage space is low, total: " + totalStr + " MB, used: " + usedStr + " MB (" + pctStr + "%)"; alertType = ALERT_TYPE_STORAGE; break; case CapacityVO.CAPACITY_TYPE_STORAGE_ALLOCATED: - msgSubject = "System Alert: Remaining unallocated Storage is low in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Remaining unallocated Storage is low in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); msgContent = "Unallocated storage space is low, total: " + totalStr + " MB, allocated: " + usedStr + " MB (" + pctStr + "%)"; alertType = ALERT_TYPE_STORAGE_ALLOCATED; break; case CapacityVO.CAPACITY_TYPE_LOCAL_STORAGE: - msgSubject = "System Alert: Remaining unallocated Local Storage is low in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Remaining unallocated Local Storage is low in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); msgContent = "Unallocated storage space is low, total: " + totalStr + " MB, allocated: " + usedStr + " MB (" + pctStr + "%)"; @@ -597,7 +597,7 @@ public class AlertManagerImpl implements AlertManager { //Pod Level case CapacityVO.CAPACITY_TYPE_PRIVATE_IP: - msgSubject = "System Alert: Number of unallocated private IPs is low in pod " +pod.getName()+ " of availablity zone " + dc.getName(); + msgSubject = "System Alert: Number of unallocated private IPs is low in pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = Double.toString(totalCapacity); usedStr = Double.toString(usedCapacity); msgContent = "Number of unallocated private IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; @@ -606,28 +606,28 @@ public class AlertManagerImpl implements AlertManager { //Zone Level case CapacityVO.CAPACITY_TYPE_SECONDARY_STORAGE: - msgSubject = "System Alert: Low Available Secondary Storage in availablity zone " + dc.getName(); + msgSubject = "System Alert: Low Available Secondary Storage in availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); msgContent = "Available secondary storage space is low, total: " + totalStr + " MB, used: " + usedStr + " MB (" + pctStr + "%)"; alertType = ALERT_TYPE_SECONDARY_STORAGE; break; case CapacityVO.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP: - msgSubject = "System Alert: Number of unallocated virtual network public IPs is low in availablity zone " + dc.getName(); + msgSubject = "System Alert: Number of unallocated virtual network public IPs is low in availability zone " + dc.getName(); totalStr = Double.toString(totalCapacity); usedStr = Double.toString(usedCapacity); msgContent = "Number of unallocated public IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; alertType = ALERT_TYPE_VIRTUAL_NETWORK_PUBLIC_IP; break; case CapacityVO.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP: - msgSubject = "System Alert: Number of unallocated direct attached public IPs is low in availablity zone " + dc.getName(); + msgSubject = "System Alert: Number of unallocated shared network IPs is low in availability zone " + dc.getName(); totalStr = Double.toString(totalCapacity); usedStr = Double.toString(usedCapacity); - msgContent = "Number of unallocated direct attached public IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; + msgContent = "Number of unallocated shared network IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; alertType = ALERT_TYPE_DIRECT_ATTACHED_PUBLIC_IP; break; case CapacityVO.CAPACITY_TYPE_VLAN: - msgSubject = "System Alert: Number of unallocated VLANs is low in availablity zone " + dc.getName(); + msgSubject = "System Alert: Number of unallocated VLANs is low in availability zone " + dc.getName(); totalStr = Double.toString(totalCapacity); usedStr = Double.toString(usedCapacity); msgContent = "Number of unallocated VLANs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; From 53072af8f79dd42809877c300cf18644d3d0fdae Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Thu, 1 Nov 2012 12:17:09 -0700 Subject: [PATCH 28/88] CS-16295: Fix select project search box Adds search functionality to 'select project' dialog's input box, which was not sending value to the listProjects API --- ui/scripts/projects.js | 13 +++++++++---- ui/scripts/ui-custom/projects.js | 7 +++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ui/scripts/projects.js b/ui/scripts/projects.js index 34c57c6aae7..8310ecf0e4c 100644 --- a/ui/scripts/projects.js +++ b/ui/scripts/projects.js @@ -569,13 +569,18 @@ // Project listing data provider dataProvider: function(args) { var user = args.context.users[0]; + var data = { + accountId: user.userid, + listAll: true + }; + + if (args.projectName) { + data.keyword = args.projectName; + } $.ajax({ url: createURL('listProjects', { ignoreProject: true }), - data: { - accountId: user.userid, - listAll: true - }, + data: data, dataType: 'json', async: true, success: function(data) { diff --git a/ui/scripts/ui-custom/projects.js b/ui/scripts/ui-custom/projects.js index 221d834d29f..117c4337726 100644 --- a/ui/scripts/ui-custom/projects.js +++ b/ui/scripts/ui-custom/projects.js @@ -543,8 +543,9 @@ var $cancel = $('
').addClass('button cancel').html(_l('label.cancel')); // Get project data - var loadData = function(complete) { + var loadData = function(complete, options) { cloudStack.projects.dataProvider({ + projectName: options ? options.projectName : null, context: cloudStack.context, response: { success: function(args) { @@ -585,7 +586,9 @@ // Search form $searchForm.submit(function() { $list.find('li').remove(); - loadData(); + loadData(null, { + projectName: $(this).find('input[type=text]').val() + }); return false; }); From ed26f69868a56c85b3bf95e864aaaa6371daca27 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 2 Nov 2012 01:04:49 +0530 Subject: [PATCH 29/88] CS-16630:UI freeze when accessing the Ip Addresses tab , when we are in the configuration of one of the ip addresses --- ui/scripts/ui/widgets/cloudBrowser.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/scripts/ui/widgets/cloudBrowser.js b/ui/scripts/ui/widgets/cloudBrowser.js index 47fbd0a08b5..9a56bb353e9 100644 --- a/ui/scripts/ui/widgets/cloudBrowser.js +++ b/ui/scripts/ui/widgets/cloudBrowser.js @@ -349,6 +349,11 @@ 'cloudBrowser', { 'breadcrumb': function($target, $browser, data) { + + if ($ ('#browser').hasClass('panel-highlight')) { + return false; + } + $browser.cloudBrowser('selectPanel', { panel: data.panel }); } } From 33a7f9ad9c91444bdb8ee48c24cbe3f776065c60 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Thu, 1 Nov 2012 15:31:45 -0700 Subject: [PATCH 30/88] Multi-edit table: add scrollbars for longer values Add horizontal scrollbar to fix longer values (such as CIDR lists) causing table to overflow. --- ui/css/cloudstack3.css | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 93578037168..75b1d004be7 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7266,8 +7266,9 @@ div.panel.ui-dialog div.list-view div.fixed-header { border-radius: 5px; border-radius: 5px 5px 5px 5px; width: 74px; + text-indent: 0px; text-align: center; - padding: 6px 9px 4px 0px; + padding: 6px 0px 4px; background: url(../images/bg-gradients.png) repeat-x 0px -220px; /*+placement:shift 4px 0px;*/ position: relative; @@ -7389,10 +7390,18 @@ div.panel.ui-dialog div.list-view div.fixed-header { border-left: none; border-right: 1px solid #CFC9C9; height: 15px; - overflow: hidden; + overflow: auto; padding-right: 0; } +.multi-edit .data .data-body .data-item > table tbody tr td span { + overflow-x: auto; + overflow-y: hidden; + width: 70px; + display: block; + float: left; +} + .multi-edit .data .data-body .data-item table tbody tr td.blank { } @@ -7437,6 +7446,7 @@ div.panel.ui-dialog div.list-view div.fixed-header { .multi-edit .data .data-body .data-item tr td .expand { width: 14px; height: 15px; + display: block; cursor: pointer; background: #FFFFFF url(../images/sprites.png) -541px -499px; border: 1px solid #D0D0D0; From bb38462b4f016bac389c64d960c9145d0993f3d0 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 1 Nov 2012 17:39:03 -0500 Subject: [PATCH 31/88] CLOUDSTACK-250 minor fix in documentation about maintenance mode. --- docs/en-US/vcenter-maintenance-mode.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/vcenter-maintenance-mode.xml b/docs/en-US/vcenter-maintenance-mode.xml index a07300eee58..fb896b2b166 100644 --- a/docs/en-US/vcenter-maintenance-mode.xml +++ b/docs/en-US/vcenter-maintenance-mode.xml @@ -35,7 +35,7 @@ First use vCenter to exit the vCenter maintenance mode. This makes the host ready for &PRODUCT; to reactivate it. Then use &PRODUCT;'s administrator UI to cancel the &PRODUCT; maintenance mode - When the host comes back online, the VMs that were migrated off of it are migrated back to it and new VMs can be added. + When the host comes back online, the VMs that were migrated off of it may be migrated back to it manually and new VMs can be added.
From 5ddb6f251cde71ec16ab5195406ff6185547af23 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 1 Nov 2012 17:44:08 -0500 Subject: [PATCH 32/88] CLOUDSTACK-392: Install Guide - fixed issue with minor correction needed in docs. --- docs/en-US/log-in-root-admin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/log-in-root-admin.xml b/docs/en-US/log-in-root-admin.xml index c0a340a4ea9..bf499d6fe95 100644 --- a/docs/en-US/log-in-root-admin.xml +++ b/docs/en-US/log-in-root-admin.xml @@ -28,7 +28,7 @@ Open your favorite Web browser and go to this URL. Substitute the IP address of your own Management Server: http://<management-server-ip-address>:8080/client - On a fresh Management Server installation, a guided tour splash screen appears. On later visits, you’ll see a login screen where you can enter a user ID and password and proceed to your Dashboard. + After logging into a fresh Management Server installation, a guided tour splash screen appears. On later visits, you’ll be taken directly into the Dashboard. If you see the first-time splash screen, choose one of the following. From dee9264ddcc226231494e18eea49f46c11660eb9 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 1 Nov 2012 17:49:03 -0500 Subject: [PATCH 33/88] CLOUDSTACK-385: Section 3.2.3 (MD5): confusing first sentence. Fixed this, though the entire section could probably do with a re-write. I think the existing instructions are overly complex. --- docs/en-US/verifying-source.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en-US/verifying-source.xml b/docs/en-US/verifying-source.xml index 05b54ecb6a9..f8bd102379d 100644 --- a/docs/en-US/verifying-source.xml +++ b/docs/en-US/verifying-source.xml @@ -54,9 +54,9 @@
MD5 - In addition to the cryptographic signature, the &PRODUCT; provides a number - of cryptographic hashes to aid in assurance of validity of the downloaded - release. You can verify this hash by executing the following command: + In addition to the cryptographic signature, &PRODUCT; has an MD5 checksum + that you can use to verify the download matches the release. + You can verify this hash by executing the following command: $ gpg --print-md MD5 apache-cloudstack-4.0.0-incubating-src.tar.bz2 | diff - apache-cloudstack-4.0.0-incubating-src.tar.bz2.md5 From 7ffcd9e8bfb853f406282512d9bff10e5d907431 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 1 Nov 2012 18:12:33 -0500 Subject: [PATCH 34/88] CLOUDSTACK-291 Additional comment on install.sh upgrade path: Noted that users need to agree to changes suggested by Yum or APT. --- docs/en-US/Release_Notes.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 849dc5af911..418d7ca2089 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -174,6 +174,7 @@ # apt-get update # apt-get upgrade cloud-* + You will, of course, have to agree to the changes suggested by Yum or APT. If the upgrade output includes a message similar to the following, then some custom content was found in your old components.xml, and you need to merge the two @@ -619,6 +620,7 @@ # apt-get update # apt-get upgrade cloud-* + You will, of course, have to agree to the changes suggested by Yum or APT. If you have made changes to your existing copy of the file components.xml in your From af7c8c42a565b8935ec5d61aa3bc3f2d6c202c42 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 2 Nov 2012 11:08:32 +0530 Subject: [PATCH 35/88] CS-12436:Static NAT Enabled box: Button with VM label expands beyond the parent box due to length of the name --- ui/css/cloudstack3.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 93578037168..5fdc2af3b6e 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -8025,7 +8025,7 @@ div.panel.ui-dialog div.list-view div.fixed-header { max-width: 98px; max-height: 21px; padding: 7px; - font-size: 14px; + font-size: 10px; position: absolute; overflow: hidden; color: #485563; From 7bd7884f57cf6ee2dd88f7c1779f27397fba3f50 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 18 Oct 2012 18:45:00 -0700 Subject: [PATCH 36/88] CS-16499: releaseNic - lock the nic row in the DB before checking its state. Hold the lock till the nics attributes + nicCount is updated Conflicts: server/src/com/cloud/network/NetworkManagerImpl.java Conflicts: server/src/com/cloud/network/NetworkManagerImpl.java --- .../com/cloud/network/NetworkManagerImpl.java | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 7506985ab99..4f72a808e36 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2094,7 +2094,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag List nics = _nicDao.listByVmId(vmProfile.getId()); // we have to implement default nics first - to ensure that default network elements start up first in multiple - // nics case)(need for setting DNS on Dhcp to domR's Ip4 address) + //nics case + // (need for setting DNS on Dhcp to domR's Ip4 address) Collections.sort(nics, new Comparator() { @Override @@ -2193,6 +2194,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override + @DB public void release(VirtualMachineProfile vmProfile, boolean forced) throws ConcurrentOperationException, ResourceUnavailableException { List nics = _nicDao.listByVmId(vmProfile.getId()); @@ -2200,6 +2202,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag releaseNic(vmProfile, nic); } } + + @Override public void releaseNic(VirtualMachineProfile vmProfile, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException { @@ -2208,11 +2212,21 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } - protected void releaseNic(VirtualMachineProfile vmProfile, NicVO nic) + protected void releaseNic(VirtualMachineProfile vmProfile, NicVO nicVO) throws ConcurrentOperationException, ResourceUnavailableException { - NetworkVO network = _networksDao.findById(nic.getNetworkId()); - if (nic.getState() == Nic.State.Reserved || nic.getState() == Nic.State.Reserving) { - Nic.State originalState = nic.getState(); + //lock the nic + Transaction txn = Transaction.currentTxn(); + txn.start(); + + NicVO nic = _nicDao.lockRow(nicVO.getId(), true); + if (nic == null) { + throw new ConcurrentOperationException("Unable to acquire lock on nic " + nic); + } + + Nic.State originalState = nic.getState(); + NetworkVO network = _networksDao.findById(nicVO.getNetworkId()); + + if (originalState == Nic.State.Reserved || originalState == Nic.State.Reserving) { if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) { NetworkGuru guru = _networkGurus.get(network.getGuruName()); nic.setState(Nic.State.Releasing); @@ -2228,6 +2242,9 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag _nicDao.update(nic.getId(), nic); } } + //commit the transaction before proceeding releasing nic profile on the network elements + txn.commit(); + // Perform release on network elements for (NetworkElement element : _networkElements) { if (s_logger.isDebugEnabled()) { @@ -2241,7 +2258,11 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } else { nic.setState(Nic.State.Allocated); updateNic(nic, network.getId(), -1); + txn.commit(); } + } else { + //commiting the empty transaction here as we have to release the lock we've held + txn.commit(); } } From 597bc8c66c56eacb4fd01deaf6045d57f89d0685 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 18 Oct 2012 19:03:59 -0700 Subject: [PATCH 37/88] CS-16499: forgot to add @DB to releaseNic() call --- server/src/com/cloud/network/NetworkManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 4f72a808e36..012b00fa7af 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2205,13 +2205,14 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override + @DB public void releaseNic(VirtualMachineProfile vmProfile, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException { NicVO nicVO = _nicDao.findById(nic.getId()); releaseNic(vmProfile, nicVO); } - + @DB protected void releaseNic(VirtualMachineProfile vmProfile, NicVO nicVO) throws ConcurrentOperationException, ResourceUnavailableException { //lock the nic From 705130e42c01cb510f3482112e6a8231f714092b Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Fri, 19 Oct 2012 16:29:15 -0700 Subject: [PATCH 38/88] CS-16499: removed useless txn.commit statement. Used to call it to ensure that the lock() on nic object will get removed even in case when we don't do the DB update. But our Transaction code does it automatic --- server/src/com/cloud/network/NetworkManagerImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 012b00fa7af..ab1ee78ad96 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2261,9 +2261,6 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag updateNic(nic, network.getId(), -1); txn.commit(); } - } else { - //commiting the empty transaction here as we have to release the lock we've held - txn.commit(); } } From 36babbfcac0f2d3cab4c8b8eb3c1b2e9a58936ce Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 30 Aug 2012 17:43:10 -0700 Subject: [PATCH 39/88] CS-16213: agent LB - don't process Disconnect event from other management server in cluster, if the recipient is the future owner of the host. The disconnect will be handled during the rebalance host connection process itself. Reviewed-by: Kelven Yang --- .../manager/ClusteredAgentManagerImpl.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index 65ff8f0de6a..df763f38f7a 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -305,6 +305,16 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } AgentAttache attache = findAttache(hostId); if (attache != null) { + if (_clusterMgr.isAgentRebalanceEnabled()) { + //don't process disconnect if the host is being rebalanced + HostTransferMapVO transferVO = _hostTransferDao.findById(hostId); + if (transferVO != null) { + if (transferVO.getFutureOwner() == _nodeId && transferVO.getState() == HostTransferState.TransferStarted) { + s_logger.debug("Not processing disconnect event as the host is being connected to " + _nodeId); + return true; + } + } + } handleDisconnect(attache, Event.AgentDisconnected, false, false); } @@ -936,9 +946,24 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust HostVO host = _hostDao.findById(hostId); try { if (s_logger.isDebugEnabled()) { - s_logger.debug("Loading directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process"); + s_logger.debug("Disconnecting host " + host.getId() + "(" + host.getName() + " as a part of rebalance process without notification"); } - result = loadDirectlyConnectedHost(host, true); + + AgentAttache attache = findAttache(hostId); + if (attache != null) { + result = handleDisconnect(attache, Event.AgentDisconnected, false, false); + } + + if (result) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Loading directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process"); + } + result = loadDirectlyConnectedHost(host, true); + } else { + s_logger.warn("Failed to disconnect " + host.getId() + "(" + host.getName() + + " as a part of rebalance process without notification"); + } + } catch (Exception ex) { s_logger.warn("Failed to load directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process due to:", ex); result = false; From 62607c9a75337d75f471d97402256d00e406429e Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Fri, 31 Aug 2012 14:22:38 -0700 Subject: [PATCH 40/88] HandleDisconnect - don't update the DB when the disconnect event is happening as a part of MS Cluster notification Reviewed-by: Frank Zhang --- .../cloud/agent/manager/AgentManagerImpl.java | 23 +++++++++++-------- .../manager/ClusteredAgentManagerImpl.java | 10 ++++---- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index 62e8acb0642..892e4055cfe 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -608,19 +608,19 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { ConnectionException ce = (ConnectionException)e; if (ce.isSetupError()) { s_logger.warn("Monitor " + monitor.second().getClass().getSimpleName() + " says there is an error in the connect process for " + hostId + " due to " + e.getMessage()); - handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected); + handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); throw ce; } else { s_logger.info("Monitor " + monitor.second().getClass().getSimpleName() + " says not to continue the connect process for " + hostId + " due to " + e.getMessage()); - handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested); + handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true); return attache; } } else if (e instanceof HypervisorVersionChangedException) { - handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested); + handleDisconnectWithoutInvestigation(attache, Event.ShutdownRequested, true); throw new CloudRuntimeException("Unable to connect " + attache.getId(), e); } else { s_logger.error("Monitor " + monitor.second().getClass().getSimpleName() + " says there is an error in the connect process for " + hostId + " due to " + e.getMessage(), e); - handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected); + handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); throw new CloudRuntimeException("Unable to connect " + attache.getId(), e); } } @@ -634,7 +634,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { // this is tricky part for secondary storage // make it as disconnected, wait for secondary storage VM to be up // return the attache instead of null, even it is disconnectede - handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected); + handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); } agentStatusTransitTo(host, Event.Ready, _nodeId); @@ -836,7 +836,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { return _name; } - protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event) { + protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event, boolean transitState) { long hostId = attache.getId(); s_logger.info("Host " + hostId + " is disconnecting with event " + event); @@ -871,8 +871,11 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { s_logger.debug("Deregistering link for " + hostId + " with state " + nextStatus); } + //remove the attache removeAgent(attache, nextStatus); - if (host != null) { + + //update the DB + if (host != null && transitState) { disconnectAgent(host, event, _nodeId); } @@ -942,7 +945,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } - handleDisconnectWithoutInvestigation(attache, event); + handleDisconnectWithoutInvestigation(attache, event, true); host = _hostDao.findById(host.getId()); if (host.getStatus() == Status.Alert || host.getStatus() == Status.Down) { _haMgr.scheduleRestartForVmsOnHost(host, true); @@ -968,7 +971,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (_investigate == true) { handleDisconnectWithInvestigation(_attache, _event); } else { - handleDisconnectWithoutInvestigation(_attache, _event); + handleDisconnectWithoutInvestigation(_attache, _event, true); } } catch (final Exception e) { s_logger.error("Exception caught while handling disconnect: ", e); @@ -1060,7 +1063,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { AgentAttache attache = null; attache = findAttache(hostId); if (attache != null) { - handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected); + handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); } return true; } else if (event == Event.ShutdownRequested) { diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index df763f38f7a..78143fd7021 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -270,7 +270,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } @Override - protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event) { + protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event, boolean transitState) { return handleDisconnect(attache, event, false, true); } @@ -282,7 +282,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust protected boolean handleDisconnect(AgentAttache agent, Status.Event event, boolean investigate, boolean broadcast) { boolean res; if (!investigate) { - res = super.handleDisconnectWithoutInvestigation(agent, event); + res = super.handleDisconnectWithoutInvestigation(agent, event, true); } else { res = super.handleDisconnectWithInvestigation(agent, event); } @@ -315,7 +315,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } } - handleDisconnect(attache, Event.AgentDisconnected, false, false); + return super.handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, false); } return true; @@ -1027,7 +1027,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust try { s_logger.debug("Management server " + _nodeId + " failed to rebalance agent " + hostId); _hostTransferDao.completeAgentTransfer(hostId); - handleDisconnectWithoutInvestigation(findAttache(hostId), Event.RebalanceFailed); + handleDisconnectWithoutInvestigation(findAttache(hostId), Event.RebalanceFailed, true); } catch (Exception ex) { s_logger.warn("Failed to reconnect host id=" + hostId + " as a part of failed rebalance task cleanup"); } @@ -1044,7 +1044,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust synchronized (_agents) { ClusteredDirectAgentAttache attache = (ClusteredDirectAgentAttache)_agents.get(hostId); if (attache != null && attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) { - handleDisconnectWithoutInvestigation(attache, Event.StartAgentRebalance); + handleDisconnectWithoutInvestigation(attache, Event.StartAgentRebalance, true); ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)createAttache(hostId); if (forwardAttache == null) { s_logger.warn("Unable to create a forward attache for the host " + hostId + " as a part of rebalance process"); From 3948d7d7c55581eadb4917cd317234c294f61ff3 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 23 Oct 2012 10:47:44 -0700 Subject: [PATCH 41/88] CS-16213: skip agent rebalancing when the management server doesn't own the host any more --- .../src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index 78143fd7021..93c3912df85 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -863,7 +863,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust continue; } - if (transferMap.getInitialOwner() != _nodeId || attache.forForward()) { + if (transferMap.getInitialOwner() != _nodeId || attache == null || attache.forForward()) { s_logger.debug("Management server " + _nodeId + " doesn't own host id=" + hostId + " any more, skipping rebalance for the host"); iterator.remove(); _hostTransferDao.completeAgentTransfer(hostId); From a5077968db6d32108d3332523014e1fd3646e451 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Wed, 24 Oct 2012 09:54:38 -0700 Subject: [PATCH 42/88] CS-16592: process handleConnectedAgent in a separate thread pool --- .../cloud/agent/manager/AgentManagerImpl.java | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index 892e4055cfe..d7edd45fcd1 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -219,6 +219,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { protected AgentMonitor _monitor = null; protected ExecutorService _executor; + protected ThreadPoolExecutor _connectExecutor; protected StateMachine2 _statusStateMachine = Status.getStateMachine(); @@ -274,7 +275,12 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { registerForHostEvents(_monitor, true, true, false); _executor = new ThreadPoolExecutor(threads, threads, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("AgentTaskPool")); - + + _connectExecutor = new ThreadPoolExecutor(100, 500, 60l, TimeUnit.SECONDS, + new LinkedBlockingQueue(), new NamedThreadFactory("AgentConnectTaskPool")); + //allow core threads to time out even when there are no items in the queue + _connectExecutor.allowCoreThreadTimeOut(true); + _connection = new NioServer("AgentManager", _port, workers + 10, this); s_logger.info("Listening on " + _port + " with " + workers + " workers"); @@ -828,6 +834,8 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } } + + _connectExecutor.shutdownNow(); return true; } @@ -1206,6 +1214,35 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } } + + protected class HandleAgentConnectTask implements Runnable { + Link _link; + Command[] _cmds; + Request _request; + + HandleAgentConnectTask(Link link, final Command[] cmds, final Request request) { + _link = link; + _cmds = cmds; + _request = request; + } + + @Override + public void run() { + _request.logD("Processing the first command "); + StartupCommand[] startups = new StartupCommand[_cmds.length]; + for (int i = 0; i < _cmds.length; i++) { + startups[i] = (StartupCommand) _cmds[i]; + } + AgentAttache attache = handleConnectedAgent(_link, startups, _request); + if (attache == null) { + s_logger.warn("Unable to create attache for agent: " + _request); + } + } + } + + protected void connectAgent(Link link, final Command[] cmds, final Request request) { + _connectExecutor.execute(new HandleAgentConnectTask(link, cmds, request)); + } public class AgentHandler extends Task { public AgentHandler(Task.Type type, Link link, byte[] data) { @@ -1218,21 +1255,13 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { Command cmd = cmds[0]; boolean logD = true; - Response response = null; if (attache == null) { - request.logD("Processing the first command "); if (!(cmd instanceof StartupCommand)) { s_logger.warn("Throwing away a request because it came through as the first command on a connect: " + request); - return; - } - - StartupCommand[] startups = new StartupCommand[cmds.length]; - for (int i = 0; i < cmds.length; i++) { - startups[i] = (StartupCommand) cmds[i]; - } - attache = handleConnectedAgent(link, startups, request); - if (attache == null) { - s_logger.warn("Unable to create attache for agent: " + request); + } else { + //submit the task for execution + request.logD("Scheduling the first command "); + connectAgent(link, cmds, request); } return; } @@ -1331,7 +1360,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { answers[i] = answer; } - response = new Response(request, answers, _nodeId, attache.getId()); + Response response = new Response(request, answers, _nodeId, attache.getId()); if (s_logger.isDebugEnabled()) { if (logD) { s_logger.debug("SeqA " + attache.getId() + "-" + response.getSequence() + ": Sending " + response); From efb5f85aad9a7c6f7f7866eda520fac9b7b2c2be Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Wed, 24 Oct 2012 10:43:44 -0700 Subject: [PATCH 43/88] CS-16594: when request is cancelled by the remote peer, in addition to cancelling the request, unblock the agent queue if the request is the current request --- .../com/cloud/agent/manager/ClusteredAgentManagerImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index 93c3912df85..ace25a4604e 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -581,6 +581,12 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust logD(data, "Cancel request received"); } agent.cancel(cancel.getSequence()); + final Long current = agent._currentSequence; + //if the request is the current request, always have to trigger sending next request in sequence, + //otherwise the agent queue will be blocked + if (req.executeInSequence() && (current != null && current == Request.getSequence(data))) { + agent.sendNext(Request.getSequence(data)); + } return; } From 04a3c4ffdcc37962d714056d48230d5083e3093c Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 30 Oct 2012 10:02:14 -0700 Subject: [PATCH 44/88] Removed unneeded if statement from VpcVirtualNetworkApplianceManagerImpl --- .../network/router/VpcVirtualNetworkApplianceManagerImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index 3139998c5c3..1fd710dc23d 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -301,9 +301,7 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian result = result && _itMgr.removeVmFromNetwork(router, network, null); if (result) { - if (result) { - _routerDao.removeRouterFromGuestNetwork(router.getId(), network.getId()); - } + _routerDao.removeRouterFromGuestNetwork(router.getId(), network.getId()); } return result; } From fe41325e969b08126f963f7877d13d3534089414 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 30 Oct 2012 10:23:16 -0700 Subject: [PATCH 45/88] ApiResponseSerializer - replaced all occurrences of string concatenation with StringBuffer.append Conflicts: server/src/com/cloud/api/response/ApiResponseSerializer.java --- .../api/response/ApiResponseSerializer.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 1429d14ed58..bdbf685731e 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -33,8 +33,8 @@ import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseGsonHelper; import com.cloud.api.ApiServer; import com.cloud.api.BaseCmd; -import com.cloud.utils.IdentityProxy; import com.cloud.api.ResponseObject; +import com.cloud.utils.IdentityProxy; import com.cloud.utils.encoding.URLEncoder; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.uuididentity.dao.IdentityDao; @@ -70,7 +70,7 @@ public class ApiResponseSerializer { StringBuilder sb = new StringBuilder(); - sb.append("{ \"" + result.getResponseName() + "\" : "); + sb.append("{ \"").append(result.getResponseName()).append("\" : "); if (result instanceof ListResponse) { List responses = ((ListResponse) result).getResponses(); if ((responses != null) && !responses.isEmpty()) { @@ -80,19 +80,20 @@ public class ApiResponseSerializer { jsonStr = unescape(jsonStr); if (count != null && count != 0) { - sb.append("{ \"" + ApiConstants.COUNT + "\":" + count + " ,\"" + responses.get(0).getObjectName() + "\" : [ " + jsonStr); + sb.append("{ \"").append(ApiConstants.COUNT).append("\":").append(count).append(" ,\""). + append(responses.get(0).getObjectName()).append("\" : [ ").append(jsonStr); } for (int i = 1; i < ((ListResponse) result).getResponses().size(); i++) { jsonStr = gson.toJson(responses.get(i)); jsonStr = unescape(jsonStr); - sb.append(", " + jsonStr); + sb.append(", ").append(jsonStr); } sb.append(" ] }"); } else { sb.append("{ }"); } } else if (result instanceof SuccessResponse) { - sb.append("{ \"success\" : \"" + ((SuccessResponse) result).getSuccess() + "\"} "); + sb.append("{ \"success\" : \"").append(((SuccessResponse) result).getSuccess()).append("\"} "); } else if (result instanceof ExceptionResponse) { String jsonErrorText = gson.toJson((ExceptionResponse) result); jsonErrorText = unescape(jsonErrorText); @@ -104,7 +105,7 @@ public class ApiResponseSerializer { if (result instanceof AsyncJobResponse || result instanceof CreateCmdResponse) { sb.append(jsonStr); } else { - sb.append(" { \"" + result.getObjectName() + "\" : " + jsonStr + " } "); + sb.append(" { \"").append(result.getObjectName()).append("\" : ").append(jsonStr).append(" } "); } } else { sb.append("{ }"); @@ -119,13 +120,14 @@ public class ApiResponseSerializer { private static String toXMLSerializedString(ResponseObject result) { StringBuilder sb = new StringBuilder(); sb.append(""); - sb.append("<" + result.getResponseName() + " cloud-stack-version=\"" + ApiDBUtils.getVersion() + "\">"); + sb.append("<").append(result.getResponseName()).append(" cloud-stack-version=\"").append(ApiDBUtils.getVersion()).append("\">"); if (result instanceof ListResponse) { Integer count = ((ListResponse) result).getCount(); if (count != null && count != 0) { - sb.append("<" + ApiConstants.COUNT + ">" + ((ListResponse) result).getCount() + ""); + sb.append("<").append(ApiConstants.COUNT).append(">").append(((ListResponse) result).getCount()). + append(""); } List responses = ((ListResponse) result).getResponses(); if ((responses != null) && !responses.isEmpty()) { @@ -141,17 +143,17 @@ public class ApiResponseSerializer { } } - sb.append(""); + sb.append(""); return sb.toString(); } private static void serializeResponseObjXML(StringBuilder sb, ResponseObject obj) { if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) { - sb.append("<" + obj.getObjectName() + ">"); + sb.append("<").append(obj.getObjectName()).append(">"); } serializeResponseObjFieldsXML(sb, obj); if (!(obj instanceof SuccessResponse) && !(obj instanceof ExceptionResponse)) { - sb.append(""); + sb.append(""); } } @@ -221,24 +223,24 @@ public class ApiResponseSerializer { if(id != null && !id.isEmpty()) { // If this is the first IdentityProxy field encountered, put in a uuidList tag. if (!usedUuidList) { - sb.append("<" + serializedName.value() + ">"); + sb.append("<").append(serializedName.value()).append(">"); usedUuidList = true; } - sb.append("<" + "uuid" + ">" + id + ""); + sb.append("").append(id).append(""); } // Append the new idFieldName property also. String idFieldName = idProxy.getidFieldName(); if (idFieldName != null) { - sb.append("<" + "uuidProperty" + ">" + idFieldName + ""); + sb.append("").append(idFieldName).append(""); } } } if (usedUuidList) { // close the uuidList. - sb.append(""); + sb.append(""); } } else if (fieldValue instanceof Date) { - sb.append("<" + serializedName.value() + ">" + BaseCmd.getDateString((Date) fieldValue) + ""); + sb.append("<").append(">").append(BaseCmd.getDateString((Date) fieldValue)).append(""); } else if (fieldValue instanceof IdentityProxy) { IdentityProxy idProxy = (IdentityProxy)fieldValue; String id = (idProxy.getValue() != null ? String.valueOf(idProxy.getValue()) : ""); @@ -251,14 +253,14 @@ public class ApiResponseSerializer { } } if(id != null && !id.isEmpty()) - sb.append("<" + serializedName.value() + ">" + id + ""); + sb.append("<").append(serializedName.value()).append(">").append(id).append(""); } else { String resultString = escapeSpecialXmlChars(fieldValue.toString()); if (!(obj instanceof ExceptionResponse)) { resultString = encodeParam(resultString); } - sb.append("<" + serializedName.value() + ">" + resultString + ""); + sb.append("<").append(serializedName.value()).append(">").append(resultString).append(""); } } } From 013102c028e5763d28419ab50bc2da9a273c78f9 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Wed, 31 Oct 2012 16:43:51 -0700 Subject: [PATCH 46/88] CS-16611: when expunge Async job, expunge corresponding sync queue items Conflicts: server/src/com/cloud/async/AsyncJobManagerImpl.java server/src/com/cloud/async/dao/SyncQueueItemDao.java --- api/src/com/cloud/async/SyncQueueItem.java | 2 + .../com/cloud/async/AsyncJobManagerImpl.java | 123 ++++++++++-------- .../src/com/cloud/async/SyncQueueManager.java | 2 + .../com/cloud/async/SyncQueueManagerImpl.java | 27 ++-- .../com/cloud/async/dao/SyncQueueItemDao.java | 3 +- .../cloud/async/dao/SyncQueueItemDaoImpl.java | 23 ++++ 6 files changed, 116 insertions(+), 64 deletions(-) diff --git a/api/src/com/cloud/async/SyncQueueItem.java b/api/src/com/cloud/async/SyncQueueItem.java index f299481eb11..9f9c379a742 100644 --- a/api/src/com/cloud/async/SyncQueueItem.java +++ b/api/src/com/cloud/async/SyncQueueItem.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.async; + public interface SyncQueueItem { + public final String AsyncJobContentType = "AsyncJob"; String getContentType(); diff --git a/server/src/com/cloud/async/AsyncJobManagerImpl.java b/server/src/com/cloud/async/AsyncJobManagerImpl.java index 6cf95feb977..5fb5105857c 100644 --- a/server/src/com/cloud/async/AsyncJobManagerImpl.java +++ b/server/src/com/cloud/async/AsyncJobManagerImpl.java @@ -270,7 +270,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe Random random = new Random(); for(int i = 0; i < 5; i++) { - queue = _queueMgr.queue(syncObjType, syncObjId, "AsyncJob", job.getId(), queueSizeLimit); + queue = _queueMgr.queue(syncObjType, syncObjId, SyncQueueItem.AsyncJobContentType, job.getId(), queueSizeLimit); if(queue != null) { break; } @@ -598,60 +598,73 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe return new Runnable() { @Override public void run() { - GlobalLock scanLock = GlobalLock.getInternLock("AsyncJobManagerGC"); - try { - if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { - try { - reallyRun(); - } finally { - scanLock.unlock(); - } - } - } finally { - scanLock.releaseRef(); - } - } - - private void reallyRun() { - try { - s_logger.trace("Begin cleanup expired async-jobs"); - - Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - _jobExpireSeconds*1000); - - // limit to 100 jobs per turn, this gives cleanup throughput as 600 jobs per minute - // hopefully this will be fast enough to balance potential growth of job table - List l = _jobDao.getExpiredJobs(cutTime, 100); - if(l != null && l.size() > 0) { - for(AsyncJobVO job : l) { - _jobDao.expunge(job.getId()); - } - } - - // forcely cancel blocking queue items if they've been staying there for too long - List blockItems = _queueMgr.getBlockedQueueItems(_jobCancelThresholdSeconds*1000, false); - if(blockItems != null && blockItems.size() > 0) { - for(SyncQueueItemVO item : blockItems) { - if(item.getContentType().equalsIgnoreCase("AsyncJob")) { - completeAsyncJob(item.getContentId(), AsyncJobResult.STATUS_FAILED, 0, getResetResultResponse("Job is cancelled as it has been blocking others for too long")); + GlobalLock scanLock = GlobalLock.getInternLock("AsyncJobManagerGC"); + try { + if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { + try { + reallyRun(); + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + } + + public void reallyRun() { + try { + s_logger.trace("Begin cleanup expired async-jobs"); + + Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - _jobExpireSeconds*1000); + + // limit to 100 jobs per turn, this gives cleanup throughput as 600 jobs per minute + // hopefully this will be fast enough to balance potential growth of job table + List l = _jobDao.getExpiredJobs(cutTime, 100); + if(l != null && l.size() > 0) { + for(AsyncJobVO job : l) { + expungeAsyncJob(job); + } + } + + // forcefully cancel blocking queue items if they've been staying there for too long + List blockItems = _queueMgr.getBlockedQueueItems(_jobCancelThresholdSeconds*1000, false); + if(blockItems != null && blockItems.size() > 0) { + for(SyncQueueItemVO item : blockItems) { + if(item.getContentType().equalsIgnoreCase(SyncQueueItem.AsyncJobContentType)) { + completeAsyncJob(item.getContentId(), AsyncJobResult.STATUS_FAILED, 0, + getResetResultResponse("Job is cancelled as it has been blocking others for too long")); } - - // purge the item and resume queue processing - _queueMgr.purgeItem(item.getId()); - } - } - - s_logger.trace("End cleanup expired async-jobs"); - } catch(Throwable e) { - s_logger.error("Unexpected exception when trying to execute queue item, ", e); - } finally { - StackMaid.current().exitCleanup(); - } - } - }; - } - - private long getMsid() { - if(_clusterMgr != null) { + + // purge the item and resume queue processing + _queueMgr.purgeItem(item.getId()); + } + } + + s_logger.trace("End cleanup expired async-jobs"); + } catch(Throwable e) { + s_logger.error("Unexpected exception when trying to execute queue item, ", e); + } finally { + StackMaid.current().exitCleanup(); + } + } + + + }; + } + + @DB + protected void expungeAsyncJob(AsyncJobVO job) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + _jobDao.expunge(job.getId()); + //purge corresponding sync queue item + _queueMgr.purgeAsyncJobQueueItemId(job.getId()); + txn.commit(); + } + + private long getMsid() { + if(_clusterMgr != null) { return _clusterMgr.getManagementNodeId(); } @@ -666,7 +679,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe } String contentType = item.getContentType(); - if(contentType != null && contentType.equals("AsyncJob")) { + if(contentType != null && contentType.equalsIgnoreCase(SyncQueueItem.AsyncJobContentType)) { Long jobId = item.getContentId(); if(jobId != null) { s_logger.warn("Mark job as failed as its correspoding queue-item has been discarded. job id: " + jobId); diff --git a/server/src/com/cloud/async/SyncQueueManager.java b/server/src/com/cloud/async/SyncQueueManager.java index b605f1b8670..a7032daaa47 100644 --- a/server/src/com/cloud/async/SyncQueueManager.java +++ b/server/src/com/cloud/async/SyncQueueManager.java @@ -30,4 +30,6 @@ public interface SyncQueueManager extends Manager { public List getActiveQueueItems(Long msid, boolean exclusive); public List getBlockedQueueItems(long thresholdMs, boolean exclusive); + + void purgeAsyncJobQueueItemId(long asyncJobId); } diff --git a/server/src/com/cloud/async/SyncQueueManagerImpl.java b/server/src/com/cloud/async/SyncQueueManagerImpl.java index c3f49557b00..4d1506523f6 100644 --- a/server/src/com/cloud/async/SyncQueueManagerImpl.java +++ b/server/src/com/cloud/async/SyncQueueManagerImpl.java @@ -185,13 +185,16 @@ public class SyncQueueManagerImpl implements SyncQueueManager { if(itemVO != null) { SyncQueueVO queueVO = _syncQueueDao.lockRow(itemVO.getQueueId(), true); - _syncQueueItemDao.expunge(itemVO.getId()); - - queueVO.setLastUpdated(DateUtil.currentGMTTime()); - //decrement the count - assert (queueVO.getQueueSize() > 0) : "Count reduce happens when it's already <= 0!"; - queueVO.setQueueSize(queueVO.getQueueSize() - 1); - _syncQueueDao.update(queueVO.getId(), queueVO); + _syncQueueItemDao.expunge(itemVO.getId()); + + //if item is active, reset queue information + if (itemVO.getLastProcessMsid() != null) { + queueVO.setLastUpdated(DateUtil.currentGMTTime()); + //decrement the count + assert (queueVO.getQueueSize() > 0) : "Count reduce happens when it's already <= 0!"; + queueVO.setQueueSize(queueVO.getQueueSize() - 1); + _syncQueueDao.update(queueVO.getId(), queueVO); + } } txt.commit(); } catch(Exception e) { @@ -273,5 +276,13 @@ public class SyncQueueManagerImpl implements SyncQueueManager { private boolean queueReadyToProcess(SyncQueueVO queueVO) { return queueVO.getQueueSize() < queueVO.getQueueSizeLimit(); + } + + @Override + public void purgeAsyncJobQueueItemId(long asyncJobId) { + Long itemId = _syncQueueItemDao.getQueueItemIdByContentIdAndType(asyncJobId, SyncQueueItem.AsyncJobContentType); + if (itemId != null) { + purgeItem(itemId); + } } -} \ No newline at end of file +} diff --git a/server/src/com/cloud/async/dao/SyncQueueItemDao.java b/server/src/com/cloud/async/dao/SyncQueueItemDao.java index cd9df2f4682..6b9da8b63ea 100644 --- a/server/src/com/cloud/async/dao/SyncQueueItemDao.java +++ b/server/src/com/cloud/async/dao/SyncQueueItemDao.java @@ -26,4 +26,5 @@ public interface SyncQueueItemDao extends GenericDao { public List getNextQueueItems(int maxItems); public List getActiveQueueItems(Long msid, boolean exclusive); public List getBlockedQueueItems(long thresholdMs, boolean exclusive); -} + public Long getQueueItemIdByContentIdAndType(long contentId, String contentType); +} diff --git a/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java b/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java index ce212981d50..5e757563bff 100644 --- a/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java +++ b/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java @@ -33,13 +33,25 @@ import com.cloud.async.SyncQueueItemVO; import com.cloud.utils.DateUtil; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; @Local(value = { SyncQueueItemDao.class }) public class SyncQueueItemDaoImpl extends GenericDaoBase implements SyncQueueItemDao { private static final Logger s_logger = Logger.getLogger(SyncQueueItemDaoImpl.class); + final GenericSearchBuilder queueIdSearch; + + protected SyncQueueItemDaoImpl() { + super(); + queueIdSearch = createSearchBuilder(Long.class); + queueIdSearch.and("contentId", queueIdSearch.entity().getContentId(), Op.EQ); + queueIdSearch.and("contentType", queueIdSearch.entity().getContentType(), Op.EQ); + queueIdSearch.selectField(queueIdSearch.entity().getId()); + queueIdSearch.done(); + } @Override @@ -132,4 +144,15 @@ public class SyncQueueItemDaoImpl extends GenericDaoBase return lockRows(sc, null, true); return listBy(sc, null); } + + + @Override + public Long getQueueItemIdByContentIdAndType(long contentId, String contentType) { + SearchCriteria sc = queueIdSearch.create(); + sc.setParameters("contentId", contentId); + sc.setParameters("contentType", contentType); + List id = customSearch(sc, null); + + return id.size() == 0 ? null : id.get(0); + } } From 1f458983afb9a19752e84f148a05f3a1c161ae91 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 1 Nov 2012 15:22:15 -0700 Subject: [PATCH 47/88] CS-16310: don't process Disconnect when: * attache is not forForward() * and the Disconnect came through cluster notification event The fix will prevent delayed AgentDisconnect cluster notification processing. --- .../agent/manager/ClusteredAgentManagerImpl.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index ace25a4604e..1b6dc44a1a3 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -305,16 +305,26 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } AgentAttache attache = findAttache(hostId); if (attache != null) { + //don't process disconnect if the host is being rebalanced if (_clusterMgr.isAgentRebalanceEnabled()) { - //don't process disconnect if the host is being rebalanced HostTransferMapVO transferVO = _hostTransferDao.findById(hostId); if (transferVO != null) { if (transferVO.getFutureOwner() == _nodeId && transferVO.getState() == HostTransferState.TransferStarted) { - s_logger.debug("Not processing disconnect event as the host is being connected to " + _nodeId); + s_logger.debug("Not processing " + Event.AgentDisconnected + " event for the host id=" + + hostId +" as the host is being connected to " + _nodeId); return true; } } } + + //don't process disconnect if the disconnect came for the host via delayed cluster notification, + //but the host has already reconnected to the current management server + if (!attache.forForward()) { + s_logger.debug("Not processing " + Event.AgentDisconnected + " event for the host id=" + + hostId +" as the host is directly connected to the current management server " + _nodeId); + return true; + } + return super.handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, false); } From 83200abc1861aa4ba4b8d23d370f641c2b074f67 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 30 Oct 2012 15:13:44 -0700 Subject: [PATCH 48/88] CS-16573: cloudstack UI - create user - encode data passed to API call. --- ui/scripts/instanceWizard.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index b35369db9d7..1d37f78c79f 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -467,9 +467,17 @@ //create new network starts here if(args.data["new-network"] == "create-new-network") { var isCreateNetworkSuccessful = true; + + var data = { + networkOfferingId: args.data["new-network-networkofferingid"], + name: args.data["new-network-name"], + displayText: args.data["new-network-name"], + zoneId: selectedZoneObj.id + }; + $.ajax({ - url: createURL("createNetwork&networkOfferingId="+args.data["new-network-networkofferingid"]+"&name="+todb(args.data["new-network-name"])+"&displayText="+todb(args.data["new-network-name"])+"&zoneId="+selectedZoneObj.id), - dataType: "json", + url: createURL('createNetwork'), + data: data, async: false, success: function(json) { newNetwork = json.createnetworkresponse.network; From 0d7ed63e20a0d9a1ecb06a43d64557dc65c5a5ec Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 1 Nov 2012 13:41:35 -0700 Subject: [PATCH 49/88] CS-16573: cloudstack UI - VM Wizard - makeSelects() - sanitize output value. --- ui/scripts/ui-custom/instanceWizard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 6a9a0570d2d..e9fda2e2662 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -137,8 +137,8 @@ ) .append( $('
').addClass('select-desc') - .append($('
').addClass('name').html(this[fields.name])) - .append($('
').addClass('desc').html(this[fields.desc])) + .append($('
').addClass('name').html(_s(this[fields.name]))) + .append($('
').addClass('desc').html(_s(this[fields.desc]))) ) .data('json-obj', this); From cfdcc7ed158f25bc812292837170483a933a87e5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 2 Nov 2012 10:19:41 -0700 Subject: [PATCH 50/88] cloudstack UI - infrastructure page - bring resource count back. --- ui/scripts/system.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 4eadf097b36..627b64f3d8b 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -336,12 +336,7 @@ data: data }); }; - - // re: CS-16413 -- Disable API calls - return args.response.success({ - data: {} - }); - + dataFns.zoneCount({}); } }, From e8a7a823891255aa4586ea9417e2dd942f2b6b8b Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 2 Nov 2012 11:12:17 -0700 Subject: [PATCH 51/88] cloudstack UI - infrastructure page - resource count - hide resource count whose API hasn't been fixed yet (i.e. zones count, hosts count, secondary storage count) and show resource count whose API has been fixed (i.e. pods count, clusters count, primary storage count, system VMs count, virtual routers count). --- ui/scripts/system.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 627b64f3d8b..d27abcccf80 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -207,10 +207,19 @@ $.ajax({ url: createURL('listClusters'), success: function(json) { - dataFns.hostCount($.extend(data, { + /* + dataFns.hostCount($.extend(data, { clusterCount: json.listclustersresponse.count ? json.listclustersresponse.count : 0 })); + */ + + //uncomment the 4 lines above and remove the following 4 lines after "count" in listHosts API is fixed. + dataFns.primaryStorageCount($.extend(data, { + clusterCount: json.listclustersresponse.count ? + json.listclustersresponse.count : 0 + })); + } }); }, @@ -234,10 +243,19 @@ $.ajax({ url: createURL('listStoragePools'), success: function(json) { - dataFns.secondaryStorageCount($.extend(data, { + /* + dataFns.secondaryStorageCount($.extend(data, { primaryStorageCount: json.liststoragepoolsresponse.count ? json.liststoragepoolsresponse.count : 0 })); + */ + + //uncomment the 4 lines above and remove the following 4 lines after "count" in listHosts API is fixed. + dataFns.systemVmCount($.extend(data, { + primaryStorageCount: json.liststoragepoolsresponse.count ? + json.liststoragepoolsresponse.count : 0 + })); + } }); }, @@ -337,7 +355,9 @@ }); }; - dataFns.zoneCount({}); + //dataFns.zoneCount({}); + dataFns.podCount({}); //uncomment the line above and remove this line after "count" in listZones API is fixed. + } }, From dfffa805bce1210d4ff58b874c1a08d3a5daa2f7 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 2 Nov 2012 12:37:35 -0700 Subject: [PATCH 52/88] Multi-edit: fix alignment of table rows and header --- ui/css/cloudstack3.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 5465dbf1580..2676c192e8b 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7181,6 +7181,10 @@ div.panel.ui-dialog div.list-view div.fixed-header { border-top: none; } +.multi-edit table th { + min-width: 120px; +} + .detail-group .multi-edit table td { border-left: 1px solid #CDCCCC; } @@ -7397,7 +7401,7 @@ div.panel.ui-dialog div.list-view div.fixed-header { .multi-edit .data .data-body .data-item > table tbody tr td span { overflow-x: auto; overflow-y: hidden; - width: 70px; + max-width: 88px; display: block; float: left; } @@ -7410,6 +7414,7 @@ div.panel.ui-dialog div.list-view div.fixed-header { } .multi-edit .data .data-body .data-item table tbody tr td.name span { + width: 53px; color: #4C5D78; font-weight: bold; } From 04c5d89f80fe20e8d7d110a07fe9cb5be3abb89f Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Sat, 3 Nov 2012 13:01:32 +0530 Subject: [PATCH 53/88] CS-16681:Inconsistent behavior in custom disk offering selection page in Add VM instance wizard - Fixed by adding a Jquery bind event --- ui/scripts/ui-custom/instanceWizard.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index e9fda2e2662..a591d178e16 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -876,6 +876,12 @@ } }); + $wizard.find('div.data-disk-offering div.custom-size input[type=text]').bind('change',function() { + var old = $wizard.find('div.data-disk-offering div.custom-size input[type=text]').val(); + $wizard.find('div.data-disk-offering span.custom-disk-size').html(_s(old)); + }); + + return $wizard.dialog({ title: _l('label.vm.add'), width: 800, From 863306260abb11753b4f5e60b3880a25be013768 Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Sat, 3 Nov 2012 17:40:58 +0530 Subject: [PATCH 54/88] fix for CLOUDSTACk-290:Reviewed:By-Jessica Tomechak --- docs/en-US/Release_Notes.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 418d7ca2089..17f9079cc01 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -32,7 +32,7 @@ Anyone contributing to CloudStack should be on this mailing list. You can also report bugs in CloudStack using the Apache Defect Tracking - System + System. To posts to the lists, you'll need to be subscribed. See the CloudStack Web site for instructions. @@ -92,11 +92,11 @@ Name - systemvm-vmware-3.0.5 + systemvm-vmware-3.0.0 Description - systemvm-vmware-3.0.5 + systemvm-vmware-3.0.0 URL @@ -560,8 +560,8 @@ VMware - Name: systemvm-vmware-3.0.5 - Description: systemvm-vmware-3.0.5 + Name: systemvm-vmware-3.0.0 + Description: systemvm-vmware-3.0.0 URL: http://download.cloud.com/templates/burbank/burbank-systemvm-08012012.ova Zone: Choose the zone where this hypervisor is used From d8fbb0dd132f176e13c0bab4a715d2f6480ce62b Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 3 Nov 2012 18:39:53 +0530 Subject: [PATCH 55/88] maven: enable jvm heap size for forked surefire test processes Any forked process that runs from maven surefire plugin gets it own separate heap which may cause build issues. Patch fixes the issue by explicity args to the surefire plugin for cloud-server. Signed-off-by: Rohit Yadav --- server/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/server/pom.xml b/server/pom.xml index 7f80acc22b5..cd4fbbe309a 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -86,6 +86,7 @@ org.apache.maven.plugins maven-surefire-plugin + -Xmx1024m com/cloud/upgrade/* com/cloud/async/* From 2bbbdbf58b40b6a5dbd9b010db89e5c934a69433 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sun, 4 Nov 2012 01:57:05 +0530 Subject: [PATCH 56/88] developer: Move custom sql commands to one prefill schema file The sql-maven plugin does not allow multiple sqlCommands to executed, only the last one in the gets executed, so moving out all the developer related schema to one file. Adds: - ROOT domain - system and admin accounts - system and admin users - custom configurations Signed-off-by: Rohit Yadav --- developer/developer-prefill.sql | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 developer/developer-prefill.sql diff --git a/developer/developer-prefill.sql b/developer/developer-prefill.sql new file mode 100644 index 00000000000..cba5382f3e2 --- /dev/null +++ b/developer/developer-prefill.sql @@ -0,0 +1,47 @@ +-- 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. + +-- Add a default ROOT domain +INSERT INTO `cloud`.`domain` (id, name, parent, path, owner) VALUES + (1, 'ROOT', NULL, '/', 2); + +-- Add system and admin accounts +INSERT INTO `cloud`.`account` (id, account_name, type, domain_id, state) VALUES + (1, 'system', 1, 1, 'enabled'); + +INSERT INTO `cloud`.`account` (id, account_name, type, domain_id, state) VALUES + (2, 'admin', 1, 1, 'enabled'); + +-- Add system user +INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, + lastname, email, state, created) VALUES (1, 'system', RAND(), + '1', 'system', 'cloud', NULL, 'enabled', NOW()); + +-- Add system user with encrypted password=password +INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, + lastname, email, state, created) VALUES (2, 'admin', '5f4dcc3b5aa765d61d8327deb882cf99', + '2', 'Admin', 'User', 'admin@mailprovider.com', 'enabled', NOW()); + +-- Add configurations +INSERT INTO `cloud`.`configuration` (category, instance, component, name, value) + VALUES ('Hidden', 'DEFAULT', 'management-server', 'init', 'false'); + +INSERT INTO `cloud`.`configuration` (category, instance, component, name, value) + VALUES ('Advanced', 'DEFAULT', 'management-server', + 'integration.api.port', '8096'); + +commit; From f940c566abda1a31c7364c975b28868251ef03ca Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sun, 4 Nov 2012 01:58:50 +0530 Subject: [PATCH 57/88] developer: Add developer-prefill.sql to be executed by developer profile Removes all other custom sqlCommand configs, have the developer profile setup from developer-prefill.sql file. Signed-off-by: Rohit Yadav --- developer/pom.xml | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/developer/pom.xml b/developer/pom.xml index bba914be752..61cb1108d3a 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -303,32 +303,16 @@ - prefill-schema + prefill-developer-schema process-test-resources execute - INSERT INTO `cloud`.`domain` (id, name, - parent, path, owner) VALUES (1, 'ROOT', NULL, '/', - 2) - - - - prefill-configuration - process-test-resources - - execute - - - INSERT INTO `cloud`.`configuration` - (category, instance, component, name, value) VALUES - ('Hidden', 'DEFAULT', 'management-server', 'init', - 'false') - INSERT INTO `cloud`.`configuration` - (category, instance, component, name, value) VALUES - ('Advanced', 'DEFAULT', 'management-server', 'integration.api.port', - '8096') + true + + ${basedir}/developer-prefill.sql + From fb08e8b7e4a66171999865c5d111df27f94bd553 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 5 Nov 2012 10:21:43 +0100 Subject: [PATCH 58/88] docs: Add RPM repository The RPM packages are now also hosted on cloudstack.apt-get.eu By adding this to the docs it makes life easier for people when installing. Also replaced CloudStack by &PRODUCT; a couple of times --- docs/en-US/configure-package-repository.xml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/en-US/configure-package-repository.xml b/docs/en-US/configure-package-repository.xml index 81df446225b..268387db942 100644 --- a/docs/en-US/configure-package-repository.xml +++ b/docs/en-US/configure-package-repository.xml @@ -53,21 +53,17 @@
RPM package repository - We expect that CloudStack will have a Yum repository with packages for supported RPM-based systems on or near the 4.0.0-incubating release. - If you're using an RPM-based system, you'll want to add the Yum repository so that you can install CloudStack with Yum. + There is a RPM package repository for &PRODUCT; so you can easily install on RHEL based platforms. + If you're using an RPM-based system, you'll want to add the Yum repository so that you can install &PRODUCT; with Yum. Yum repository information is found under /etc/yum.repos.d. You'll see several .repo files in this directory, each one denoting a specific repository. - To add the CloudStack repository, visit the downloads page for the repository information. It will look something like this: + To add the &PRODUCT; repository, create /etc/yum.repos.d/cloudstack.repo and insert the following information. [cloudstack] name=cloudstack -baseurl=http://server.url/downloads/rpm/stable/ +baseurl=http://cloudstack.apt-get.eu/rhel/4.0/ enabled=1 -gpgcheck=1 +gpgcheck=0 - Next you'll want to add the GPG key: - -$ rpm --import http://server.url/downloads/RPM-GPG-KEY.txt - Now you should be able to install CloudStack using Yum.
From 4cb9e6d7d430efa41cec772e4d84ea57c50696df Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 5 Nov 2012 10:23:09 +0100 Subject: [PATCH 59/88] docs: Change filename of Deb repository file These files should end with .list, otherwise Apt won't pick them up. --- docs/en-US/configure-package-repository.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/configure-package-repository.xml b/docs/en-US/configure-package-repository.xml index 268387db942..9ef2307b105 100644 --- a/docs/en-US/configure-package-repository.xml +++ b/docs/en-US/configure-package-repository.xml @@ -43,7 +43,7 @@
DEB package repository You can add a DEB package repository to your apt sources with the following commands. Please note that only packages for Ubuntu 12.04 LTS (precise) are being built at this time. - Use your preferred editor and open (or create) /etc/apt/sources.list.d/cloudstack. Add the community provided repository to the file: + Use your preferred editor and open (or create) /etc/apt/sources.list.d/cloudstack.list. Add the community provided repository to the file: deb http://cloudstack.apt-get.eu/ubuntu precise 4.0 We now have to add the public key to the trusted keys. $ wget -O - http://cloudstack.apt-get.eu/release.asc|apt-key add - From 4da02c097176e3ccc41e6112f67e1edad8e528b9 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 5 Nov 2012 10:28:13 +0100 Subject: [PATCH 60/88] deb: Have the agent depend on the System ISO This is needed for deploying System VMs on a KVM hypervisor Without this ISO we can't deploy this System VMs --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 44736d74511..380b2e4a78d 100644 --- a/debian/control +++ b/debian/control @@ -90,7 +90,7 @@ Description: CloudStack agent libraries Package: cloud-agent Architecture: any -Depends: openjdk-6-jre, cloud-utils (= ${source:Version}), cloud-core (= ${source:Version}), cloud-agent-deps (= ${source:Version}), cloud-python (= ${source:Version}), cloud-agent-libs (= ${source:Version}), cloud-scripts (= ${source:Version}), libvirt0, sysvinit-utils, chkconfig, qemu-kvm, libvirt-bin, uuid-runtime, rsync, grep, iproute, ebtables, vlan, liblog4j1.2-java (>= 1.2.16), libjna-java, wget, jsvc, lsb-base (>= 3.2) +Depends: openjdk-6-jre, cloud-utils (= ${source:Version}), cloud-core (= ${source:Version}), cloud-agent-deps (= ${source:Version}), cloud-python (= ${source:Version}), cloud-agent-libs (= ${source:Version}), cloud-scripts (= ${source:Version}), cloud-system-iso (= ${source:Version}), libvirt0, sysvinit-utils, chkconfig, qemu-kvm, libvirt-bin, uuid-runtime, rsync, grep, iproute, ebtables, vlan, liblog4j1.2-java (>= 1.2.16), libjna-java, wget, jsvc, lsb-base (>= 3.2) Description: CloudStack agent The CloudStack agent is in charge of managing shared computing resources in a CloudStack powered cloud. Install this package if this computer From 46ad8d6f4e829ca9586989c0acd1f53ee0cddbf3 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 18:12:36 +0530 Subject: [PATCH 61/88] cli: remind user to set host, port too Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index ba15001f282..4b282f57790 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -66,7 +66,7 @@ class CloudStackShell(cmd.Cmd): for key in self.config_fields.keys(): setattr(self, key, self.config_fields[key]) config = self.write_config() - print "Set your api and secret keys using the set command!" + print("Set your api and secret keys, host, port etc. using the set command!") for key in self.config_fields.keys(): setattr(self, key, config.get('CLI', key)) From 49b4786de6dada0939e4a26b2cfe0fbae06d8511 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 18:13:35 +0530 Subject: [PATCH 62/88] cli: don't search rule in apis, filter apis that start with that rule Search verb in apis takes more time than filtering out apis that start with that verb from the grammar. Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 4b282f57790..1cc166c1d65 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -295,7 +295,7 @@ def main(): self = CloudStackShell for rule in grammar: setattr(self, 'completions_' + rule, map(lambda x: x.replace(rule, ''), - filter(lambda x: rule in x, + filter(lambda x: x.startswith(rule), completions))) def add_grammar(rule): From fe7b28ea0bcb23e63ed8e36e613517cf21e8742a Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Mon, 5 Nov 2012 15:18:24 +0100 Subject: [PATCH 63/88] Summary: Fixed typo in the documentation, reported by Roeland Kuipers --- docs/en-US/plugin-niciranvp-provider.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/plugin-niciranvp-provider.xml b/docs/en-US/plugin-niciranvp-provider.xml index d81db99d9c0..80fb2273238 100644 --- a/docs/en-US/plugin-niciranvp-provider.xml +++ b/docs/en-US/plugin-niciranvp-provider.xml @@ -27,7 +27,7 @@ addNetworkServiceProvider - name = "NiciraNVP" + name = "NiciraNvp" physicalnetworkid = <the uuid of the physical network> From 5611a8eda212d0d564c4f0fe0da9ed7f6cf3f196 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:36:07 +0530 Subject: [PATCH 64/88] cli: add logic to complete api parameters and use caching to optimize runtime Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 89 ++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 1cc166c1d65..c5191268638 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -52,6 +52,9 @@ class CloudStackShell(cmd.Cmd): ruler = "-" config_file = os.path.expanduser('~/.cloudmonkey_config') + # datastructure {'list': {'users': ['listUsers', [params], docstring]}} + cache_verbs = {} + def __init__(self): self.config_fields = {'host': 'localhost', 'port': '8080', 'apiKey': '', 'secretKey': '', @@ -212,6 +215,15 @@ class CloudStackShell(cmd.Cmd): return None return response + def get_api_module(self, api_name, api_class_strs=[]): + try: + api_mod = __import__("marvin.cloudstackAPI.%s" % api_name, + globals(), locals(), api_class_strs, -1) + except ImportError, e: + self.print_shell("Error: API %s not found!" % e) + return None + return api_mod + def default(self, args): args = args.split(" ") api_name = args[0] @@ -219,11 +231,10 @@ class CloudStackShell(cmd.Cmd): try: api_cmd_str = "%sCmd" % api_name api_rsp_str = "%sResponse" % api_name - api_mod = __import__("marvin.cloudstackAPI.%s" % api_name, - globals(), locals(), [api_cmd_str], -1) + api_mod = self.get_api_module(api_name, [api_cmd_str, api_rsp_str]) api_cmd = getattr(api_mod, api_cmd_str) api_rsp = getattr(api_mod, api_rsp_str) - except ImportError, e: + except AttributeError, e: self.print_shell("Error: API %s not found!" % e) return @@ -243,10 +254,43 @@ class CloudStackShell(cmd.Cmd): except Exception as e: self.print_shell("🙈 Error on parsing and printing", e) + def cache_verb_miss(self, verb): + completions_found = filter(lambda x: x.startswith(verb), completions) + self.cache_verbs[verb] = {} + for api_name in completions_found: + try: + api_cmd_str = "%sCmd" % api_name + api_mod = self.get_api_module(api_name, [api_cmd_str]) + api_cmd = getattr(api_mod, api_cmd_str) + doc = api_mod.__doc__ + except AttributeError, e: + self.print_shell("Error: API attribute %s not found!" % e) + params = filter(lambda x: '__' not in x, dir(api_cmd())) + api_name_lower = api_name.replace(verb, '').lower() + self.cache_verbs[verb][api_name_lower] = [api_name, params, doc] + def completedefault(self, text, line, begidx, endidx): - mline = line.partition(" ")[2] - offs = len(mline) - len(text) - return [s[offs:] for s in completions if s.startswith(mline)] + partitions = line.partition(" ") + verb = partitions[0] + rline = partitions[2].partition(" ") + subject = rline[0] + separator = rline[1] + params = rline[2] + + autocompletions = [] + search_string = "" + + if not verb in self.cache_verbs: + self.cache_verb_miss(verb) + + if separator != " ": # Complete verb subjects + autocompletions = self.cache_verbs[verb].keys() + search_string = subject + else: # Complete subject params + autocompletions = self.cache_verbs[verb][subject][1] + search_string = text + + return [s for s in autocompletions if s.startswith(search_string)] def do_api(self, args): """ @@ -259,7 +303,9 @@ class CloudStackShell(cmd.Cmd): self.print_shell("Please use a valid syntax") def complete_api(self, text, line, begidx, endidx): - return self.completedefault(text, line, begidx, endidx) + mline = line.partition(" ")[2] + offs = len(mline) - len(text) + return [s[offs:] for s in completions if s.startswith(mline)] def do_set(self, args): """ @@ -284,6 +330,7 @@ class CloudStackShell(cmd.Cmd): def main(): + # Add verbs in grammar grammar = ['create', 'list', 'delete', 'update', 'enable', 'disable', 'add', 'remove', 'attach', 'detach', 'assign', 'authorize', 'change', 'register', @@ -294,13 +341,19 @@ def main(): self = CloudStackShell for rule in grammar: - setattr(self, 'completions_' + rule, map(lambda x: x.replace(rule, ''), - filter(lambda x: x.startswith(rule), - completions))) - def add_grammar(rule): def grammar_closure(self, args): - self.default(rule + args) + if not rule in self.cache_verbs: + self.cache_verb_miss(rule) + try: + res = self.cache_verbs[rule][args.partition(" ")[0]] + except KeyError, e: + self.print_shell("Error: no such command on %s" % rule) + return + if '--help' in args: + self.print_shell(res[2]) + return + self.default(res[0]) return grammar_closure grammar_handler = add_grammar(rule) @@ -308,18 +361,6 @@ def main(): grammar_handler.__name__ = 'do_' + rule setattr(self, grammar_handler.__name__, grammar_handler) - def add_completer(rule): - def completer_closure(self, text, line, begidx, endidx): - mline = line.partition(" ")[2] - offs = len(mline) - len(text) - return [s[offs:] for s in getattr(self, 'completions_' + rule) - if s.startswith(mline)] - return completer_closure - - completion_handler = add_completer(rule) - completion_handler.__name__ = 'complete_' + rule - setattr(self, completion_handler.__name__, completion_handler) - if len(sys.argv) > 1: CloudStackShell().onecmd(' '.join(sys.argv[1:])) else: From 8c12f49dbe559b77f03f2d985fe5913c6ef58138 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:38:16 +0530 Subject: [PATCH 65/88] cli: fix intro message Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index c5191268638..70e7987426c 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -69,7 +69,8 @@ class CloudStackShell(cmd.Cmd): for key in self.config_fields.keys(): setattr(self, key, self.config_fields[key]) config = self.write_config() - print("Set your api and secret keys, host, port etc. using the set command!") + print("Set your apiKey, secretKey, host, port, prompt, color, " + "log_file, history_file using the set command!") for key in self.config_fields.keys(): setattr(self, key, config.get('CLI', key)) From 94649491beece6c25eb3329a1aff8dbb7b25be7e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:38:41 +0530 Subject: [PATCH 66/88] cli: fix color parsing Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 70e7987426c..4086b2099b1 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -140,10 +140,10 @@ class CloudStackShell(cmd.Cmd): print colored.cyan(arg), elif 'name =' in arg: print colored.magenta(arg), - elif ':' in arg: - print colored.blue(arg), elif 'Error' in arg: print colored.red(arg), + elif ':' in arg: + print colored.blue(arg), else: print arg, else: From 98d341d9325377b0beaf8dafb3ad4092bed3f383 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:56:39 +0530 Subject: [PATCH 67/88] cli: add grammar to class Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 4086b2099b1..ebb03e7802b 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -51,6 +51,7 @@ class CloudStackShell(cmd.Cmd): intro = "☁ Apache CloudStack CLI. Type help or ? to list commands.\n" ruler = "-" config_file = os.path.expanduser('~/.cloudmonkey_config') + grammar = [] # datastructure {'list': {'users': ['listUsers', [params], docstring]}} cache_verbs = {} @@ -124,6 +125,9 @@ class CloudStackShell(cmd.Cmd): def emptyline(self): pass + def set_grammar(self, grammar): + self.grammar = grammar + def print_shell(self, *args): try: for arg in args: @@ -362,10 +366,12 @@ def main(): grammar_handler.__name__ = 'do_' + rule setattr(self, grammar_handler.__name__, grammar_handler) + shell = CloudStackShell() + shell.set_grammar(grammar) if len(sys.argv) > 1: - CloudStackShell().onecmd(' '.join(sys.argv[1:])) + shell.onecmd(' '.join(sys.argv[1:])) else: - CloudStackShell().cmdloop() + shell.cmdloop() if __name__ == "__main__": main() From c444bb3811a146107fd1c47dcf190bfad5fac589 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:57:11 +0530 Subject: [PATCH 68/88] cli: don't get the required list as params Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index ebb03e7802b..05ea8869589 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -270,7 +270,8 @@ class CloudStackShell(cmd.Cmd): doc = api_mod.__doc__ except AttributeError, e: self.print_shell("Error: API attribute %s not found!" % e) - params = filter(lambda x: '__' not in x, dir(api_cmd())) + params = filter(lambda x: '__' not in x and 'required' not in x, + dir(api_cmd())) api_name_lower = api_name.replace(verb, '').lower() self.cache_verbs[verb][api_name_lower] = [api_name, params, doc] From 64907c1da4318f37ebd71770713a358f24406790 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:57:59 +0530 Subject: [PATCH 69/88] cli: fix parsing args Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 05ea8869589..3e09cb701ae 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -283,10 +283,13 @@ class CloudStackShell(cmd.Cmd): separator = rline[1] params = rline[2] + if verb not in self.grammar: + return [] + autocompletions = [] search_string = "" - if not verb in self.cache_verbs: + if verb not in self.cache_verbs: self.cache_verb_miss(verb) if separator != " ": # Complete verb subjects @@ -352,14 +355,15 @@ def main(): if not rule in self.cache_verbs: self.cache_verb_miss(rule) try: - res = self.cache_verbs[rule][args.partition(" ")[0]] + args_partition = args.partition(" ") + res = self.cache_verbs[rule][args_partition[0]] except KeyError, e: self.print_shell("Error: no such command on %s" % rule) return if '--help' in args: self.print_shell(res[2]) return - self.default(res[0]) + self.default(res[0] + " " + args_partition[2]) return grammar_closure grammar_handler = add_grammar(rule) From c10eeb60362ccc902105fa275b5d20b95d3108df Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 5 Nov 2012 21:58:16 +0530 Subject: [PATCH 70/88] cli: strip args else this fails on parsing Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cloudmonkey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py index 3e09cb701ae..6e08adf918e 100644 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ b/tools/cli/cloudmonkey/cloudmonkey.py @@ -230,7 +230,7 @@ class CloudStackShell(cmd.Cmd): return api_mod def default(self, args): - args = args.split(" ") + args = args.strip().split(" ") api_name = args[0] try: From b101dc7279f7c31ebf846f70a4c13ecf1ee3d6fd Mon Sep 17 00:00:00 2001 From: Edison Xu Date: Mon, 29 Oct 2012 15:29:36 -0700 Subject: [PATCH 71/88] KVM agent connet: * send StartupAnswer right after StartupCommand is recieved * if post processor going wrong, send out readycommand with error message to agent, then agent will exit --- agent/src/com/cloud/agent/Agent.java | 23 ++- api/src/com/cloud/agent/api/ReadyCommand.java | 9 ++ .../cloud/agent/manager/AgentManagerImpl.java | 138 +++++++----------- 3 files changed, 85 insertions(+), 85 deletions(-) diff --git a/agent/src/com/cloud/agent/Agent.java b/agent/src/com/cloud/agent/Agent.java index 4bfd0765161..84b0db62684 100755 --- a/agent/src/com/cloud/agent/Agent.java +++ b/agent/src/com/cloud/agent/Agent.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; - import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; @@ -48,6 +47,7 @@ import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.ModifySshKeysCommand; import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.ShutdownCommand; import com.cloud.agent.api.StartupAnswer; import com.cloud.agent.api.StartupCommand; @@ -491,6 +491,10 @@ public class Agent implements HandlerFactory, IAgentControl { cancelTasks(); _reconnectAllowed = false; answer = new Answer(cmd, true, null); + } else if (cmd instanceof ReadyCommand && ((ReadyCommand)cmd).getDetails() != null) { + s_logger.debug("Not ready to connect to mgt server: " + ((ReadyCommand)cmd).getDetails()); + System.exit(1); + return; } else if (cmd instanceof MaintainCommand) { s_logger.debug("Received maintainCommand" ); cancelTasks(); @@ -513,6 +517,9 @@ public class Agent implements HandlerFactory, IAgentControl { } } else { + if (cmd instanceof ReadyCommand) { + processReadyCommand((ReadyCommand)cmd); + } _inProgress.incrementAndGet(); try { answer = _resource.executeRequest(cmd); @@ -576,6 +583,19 @@ public class Agent implements HandlerFactory, IAgentControl { setLastPingResponseTime(); } } + + + public void processReadyCommand(Command cmd) { + + final ReadyCommand ready = (ReadyCommand) cmd; + + s_logger.info("Proccess agent ready command, agent id = " + ready.getHostId()); + if (ready.getHostId() != null) { + setId(ready.getHostId()); + } + s_logger.info("Ready command is processed: agent id = " + getId()); + + } public void processOtherTask(Task task) { final Object obj = task.get(); @@ -601,6 +621,7 @@ public class Agent implements HandlerFactory, IAgentControl { } catch (final ClosedChannelException e) { s_logger.warn("Unable to send request: " + request.toString()); } + } else if (obj instanceof Request) { final Request req = (Request) obj; final Command command = req.getCommand(); diff --git a/api/src/com/cloud/agent/api/ReadyCommand.java b/api/src/com/cloud/agent/api/ReadyCommand.java index b2502964c91..0c91a2555fa 100644 --- a/api/src/com/cloud/agent/api/ReadyCommand.java +++ b/api/src/com/cloud/agent/api/ReadyCommand.java @@ -23,12 +23,18 @@ public class ReadyCommand extends Command { } private Long dcId; + private Long hostId; public ReadyCommand(Long dcId) { super(); this.dcId = dcId; } + public ReadyCommand(Long dcId, Long hostId) { + this(dcId); + this.hostId = hostId; + } + public void setDetails(String details) { _details = details; } @@ -46,4 +52,7 @@ public class ReadyCommand extends Command { return true; } + public Long getHostId() { + return hostId; + } } diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index d7edd45fcd1..8141fdbe3e0 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -634,7 +634,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } Long dcId = host.getDataCenterId(); - ReadyCommand ready = new ReadyCommand(dcId); + ReadyCommand ready = new ReadyCommand(dcId, host.getId()); Answer answer = easySend(hostId, ready); if (answer == null || !answer.getResult()) { // this is tricky part for secondary storage @@ -1096,91 +1096,37 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { return attache; } - //TODO: handle mycloud specific private AgentAttache handleConnectedAgent(final Link link, final StartupCommand[] startup, Request request) { AgentAttache attache = null; - StartupAnswer[] answers = new StartupAnswer[startup.length]; - try { - HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup); + ReadyCommand ready = null; + try { + HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup); if (host != null) { + ready = new ReadyCommand(host.getDataCenterId(), host.getId()); attache = createAttacheForConnect(host, link); + attache = notifyMonitorsOfConnection(attache, startup, false); } - Command cmd; - for (int i = 0; i < startup.length; i++) { - cmd = startup[i]; - if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { - answers[i] = new StartupAnswer(startup[i], attache.getId(), getPingInterval()); - break; - } - } - }catch (ConnectionException e) { - Command cmd; - for (int i = 0; i < startup.length; i++) { - cmd = startup[i]; - if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { - answers[i] = new StartupAnswer(startup[i], e.toString()); - break; - } - } - } catch (IllegalArgumentException e) { - Command cmd; - for (int i = 0; i < startup.length; i++) { - cmd = startup[i]; - if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { - answers[i] = new StartupAnswer(startup[i], e.toString()); - break; - } - } - } catch (CloudRuntimeException e) { - Command cmd; - for (int i = 0; i < startup.length; i++) { - cmd = startup[i]; - if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { - answers[i] = new StartupAnswer(startup[i], e.toString()); - break; - } - } - } - - Response response = null; - if (attache != null) { - response = new Response(request, answers[0], _nodeId, attache.getId()); - } else { - response = new Response(request, answers[0], _nodeId, -1); + } catch (Exception e) { + s_logger.debug("Failed to handle host connection: " + e.toString()); + ready = new ReadyCommand(null); + ready.setDetails(e.toString()); + } finally { + if (ready == null) { + ready = new ReadyCommand(null); + } } try { - link.send(response.toBytes()); - } catch (ClosedChannelException e) { - s_logger.debug("Failed to send startupanswer: " + e.toString()); - return null; - } - if (attache == null) { - return null; - } - - try { - attache = notifyMonitorsOfConnection(attache, startup, false); - return attache; - } catch (ConnectionException e) { - ReadyCommand ready = new ReadyCommand(null); - ready.setDetails(e.toString()); - try { + if (attache == null) { + final Request readyRequest = new Request(-1, -1, ready, false); + link.send(readyRequest.getBytes()); + } else { easySend(attache.getId(), ready); - } catch (Exception e1) { - s_logger.debug("Failed to send readycommand, due to " + e.toString()); } - return null; - } catch (CloudRuntimeException e) { - ReadyCommand ready = new ReadyCommand(null); - ready.setDetails(e.toString()); - try { - easySend(attache.getId(), ready); - } catch (Exception e1) { - s_logger.debug("Failed to send readycommand, due to " + e.toString()); - } - return null; + } catch (Exception e) { + s_logger.debug("Failed to send ready command:" + e.toString()); } + return attache; } protected class SimulateStartTask implements Runnable { @@ -1233,6 +1179,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { for (int i = 0; i < _cmds.length; i++) { startups[i] = (StartupCommand) _cmds[i]; } + AgentAttache attache = handleConnectedAgent(_link, startups, _request); if (attache == null) { s_logger.warn("Unable to create attache for agent: " + _request); @@ -1241,6 +1188,23 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } protected void connectAgent(Link link, final Command[] cmds, final Request request) { + //send startupanswer to agent in the very beginning, so agent can move on without waiting for the answer for an undetermined time, if we put this logic into another thread pool. + StartupAnswer[] answers = new StartupAnswer[cmds.length]; + Command cmd; + for (int i = 0; i < cmds.length; i++) { + cmd = cmds[i]; + if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { + answers[i] = new StartupAnswer((StartupCommand)cmds[i], 0, getPingInterval()); + break; + } + } + Response response = null; + response = new Response(request, answers[0], _nodeId, -1); + try { + link.send(response.toBytes()); + } catch (ClosedChannelException e) { + s_logger.debug("Failed to send startupanswer: " + e.toString()); + } _connectExecutor.execute(new HandleAgentConnectTask(link, cmds, request)); } @@ -1327,17 +1291,23 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (cmd instanceof PingRoutingCommand) { boolean gatewayAccessible = ((PingRoutingCommand) cmd).isGatewayAccessible(); HostVO host = _hostDao.findById(Long.valueOf(cmdHostId)); - if (!gatewayAccessible) { - // alert that host lost connection to - // gateway (cannot ping the default route) - DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); - HostPodVO podVO = _podDao.findById(host.getPodId()); - String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); + + if (host != null) { + if (!gatewayAccessible) { + // alert that host lost connection to + // gateway (cannot ping the default route) + DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); + HostPodVO podVO = _podDao.findById(host.getPodId()); + String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId(), "Host lost connection to gateway, " + hostDesc, "Host [" + hostDesc - + "] lost connection to gateway (default route) and is possibly having network connection issues."); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId(), "Host lost connection to gateway, " + hostDesc, "Host [" + hostDesc + + "] lost connection to gateway (default route) and is possibly having network connection issues."); + } else { + _alertMgr.clearAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId()); + } } else { - _alertMgr.clearAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId()); + s_logger.debug("Not processing " + PingRoutingCommand.class.getSimpleName() + + " for agent id=" + cmdHostId + "; can't find the host in the DB"); } } answer = new PingAnswer((PingCommand) cmd); From 29dda3f41a2670ddd3de34527aece204c764cf0a Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 2 Nov 2012 14:08:47 -0700 Subject: [PATCH 72/88] CS-16599: cloudstack UI - guest network page - fix a bug that networkdomain didn't show in a shared network. --- ui/scripts/network.js | 3 ++- ui/scripts/sharedFunctions.js | 23 +++++++++++++++++++++++ ui/scripts/system.js | 22 ---------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index ef6eaa043d8..a149bfa82a0 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -956,7 +956,8 @@ dataType: "json", async: true, success: function(json) { - var jsonObj = json.listnetworksresponse.network[0]; + var jsonObj = json.listnetworksresponse.network[0]; + addExtraPropertiesToGuestNetworkObject(jsonObj); args.response.success( { actionFilter: cloudStack.actionFilter.guestNetwork, diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 28b3bb9550f..b6b3ef817f6 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -454,6 +454,29 @@ function listViewDataProvider(args, data) { }); } +//used by infrastruct page and network page +var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { + jsonObj.networkdomaintext = jsonObj.networkdomain; + jsonObj.networkofferingidText = jsonObj.networkofferingid; + + if(jsonObj.acltype == "Domain") { + if(jsonObj.domainid == rootAccountId) + jsonObj.scope = "All"; + else + jsonObj.scope = "Domain (" + jsonObj.domain + ")"; + } + else if (jsonObj.acltype == "Account"){ + if(jsonObj.project != null) + jsonObj.scope = "Account (" + jsonObj.domain + ", " + jsonObj.project + ")"; + else + jsonObj.scope = "Account (" + jsonObj.domain + ", " + jsonObj.account + ")"; + } + + if(jsonObj.vlan == null && jsonObj.broadcasturi != null) { + jsonObj.vlan = jsonObj.broadcasturi.replace("vlan://", ""); + } +} + //find service object in network object function ipFindNetworkServiceByName(pName, networkObj) { if(networkObj == null) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index d27abcccf80..7d3f1b9f3c9 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10406,28 +10406,6 @@ } } - var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { - jsonObj.networkdomaintext = jsonObj.networkdomain; - jsonObj.networkofferingidText = jsonObj.networkofferingid; - - if(jsonObj.acltype == "Domain") { - if(jsonObj.domainid == rootAccountId) - jsonObj.scope = "All"; - else - jsonObj.scope = "Domain (" + jsonObj.domain + ")"; - } - else if (jsonObj.acltype == "Account"){ - if(jsonObj.project != null) - jsonObj.scope = "Account (" + jsonObj.domain + ", " + jsonObj.project + ")"; - else - jsonObj.scope = "Account (" + jsonObj.domain + ", " + jsonObj.account + ")"; - } - - if(jsonObj.vlan == null && jsonObj.broadcasturi != null) { - jsonObj.vlan = jsonObj.broadcasturi.replace("vlan://", ""); - } - } - var addExtraPropertiesToRouterInstanceObject = function(jsonObj) { if(jsonObj.isredundantrouter == true) jsonObj["redundantRouterState"] = jsonObj.redundantstate; From 9e729fa166ffb8abc6b0666883306d33c71aa9c0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 11:18:05 -0800 Subject: [PATCH 73/88] Zone wizard UI: fix multi-edit table width --- ui/css/cloudstack3.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 2676c192e8b..a85a392df6e 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -6816,6 +6816,10 @@ label.error { width: 98%; } +.multi-wizard.zone-wizard .multi-edit table th { + max-width: 97px; +} + .multi-wizard.zone-wizard .multi-edit .data { width: 102%; float: left; From 716c35645b85ff0b756885f563ad220c5300c92d Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 11:16:57 -0800 Subject: [PATCH 74/88] CSS: fix small formatting issue --- ui/css/cloudstack3.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index a85a392df6e..f8bf872a4a8 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -9544,7 +9544,8 @@ div.panel.ui-dialog div.list-view div.fixed-header { .tooltip-box { width: 15%; height: auto; - display: inline-block, padding: 4px; + display: inline-block; + padding: 4px; background: #FFFFFF; border: 1px solid #BEB8B8; padding: 10px; From 476e771c1c8f81b87a77883922bc1b35dea213fc Mon Sep 17 00:00:00 2001 From: Dave Cahill Date: Mon, 5 Nov 2012 10:51:02 +0900 Subject: [PATCH 75/88] SSH key changes --- developer/developer-prefill.sql | 5 +++++ server/src/com/cloud/server/ConfigurationServerImpl.java | 1 + 2 files changed, 6 insertions(+) diff --git a/developer/developer-prefill.sql b/developer/developer-prefill.sql index cba5382f3e2..8e215ba77d9 100644 --- a/developer/developer-prefill.sql +++ b/developer/developer-prefill.sql @@ -44,4 +44,9 @@ INSERT INTO `cloud`.`configuration` (category, instance, component, name, value) VALUES ('Advanced', 'DEFAULT', 'management-server', 'integration.api.port', '8096'); +-- Add developer configuration entry; allows management server to be run as a user other than "cloud" +INSERT INTO `cloud`.`configuration` (category, instance, component, name, value) + VALUES ('Advanced', 'DEFAULT', 'management-server', + 'developer', 'true'); + commit; diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 904e8c59f6e..420b1722e0f 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -570,6 +570,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { String username = System.getProperty("user.name"); Boolean devel = Boolean.valueOf(_configDao.getValue("developer")); if (!username.equalsIgnoreCase("cloud") && !devel) { + s_logger.warn("Systemvm keypairs could not be set. Management server should be run as cloud user, or in development mode."); return; } String already = _configDao.getValue("ssh.privatekey"); From 514a7e31d4e3a14f1bc82ee51d609e0e33e6785b Mon Sep 17 00:00:00 2001 From: Jessica Tomechak Date: Mon, 5 Nov 2012 12:46:59 -0800 Subject: [PATCH 76/88] Docs. CLOUDSTACK-134. Copying vhd-util is required only when XenServer is used. --- .../management-server-install-client.xml | 5 ++-- .../management-server-install-multi-node.xml | 19 ++++++++------- .../management-server-install-overview.xml | 24 +++++++++---------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/docs/en-US/management-server-install-client.xml b/docs/en-US/management-server-install-client.xml index 7d219acc623..7e81ec735fc 100644 --- a/docs/en-US/management-server-install-client.xml +++ b/docs/en-US/management-server-install-client.xml @@ -33,8 +33,9 @@ packages will depend on everything you need to run the Management server.
Downloading vhd-util + This procedure is required only for installations where XenServer is installed on the hypervisor hosts. Before setting up the Management Server, download vhd-util from vhd-util + url="http://download.cloud.com.s3.amazonaws.com/tools/vhd-util">vhd-util. If the Management Server is RHEL or CentOS, copy vhd-util to /usr/lib64/cloud/common/scripts/vm/hypervisor/xenserver. If the Management Server is Ubuntu, copy vhd-util to @@ -49,4 +50,4 @@ Install on Ubuntu apt-get install cloud-client
-
+ \ No newline at end of file diff --git a/docs/en-US/management-server-install-multi-node.xml b/docs/en-US/management-server-install-multi-node.xml index 1764fa90092..e61f6230ff0 100644 --- a/docs/en-US/management-server-install-multi-node.xml +++ b/docs/en-US/management-server-install-multi-node.xml @@ -32,6 +32,7 @@ linkend="sect-source-buildrpm"/> or as appropriate.
+ This step is required only for installations where XenServer is installed on the hypervisor hosts. Download vhd-util from vhd-util If the Management Server is RHEL or CentOS, copy vhd-util to @@ -41,21 +42,23 @@ linkend="sect-source-buildrpm"/> or as Ensure that necessary services are started and set to start on boot. - # service rpcbind start -# service nfs start -# chkconfig nfs on -# chkconfig rpcbind on + # service rpcbind start +# service nfs start +# chkconfig nfs on +# chkconfig rpcbind on + - Configure the database client. Note the absence of the --deploy-as argument in this + Configure the database client. Note the absence of the --deploy-as argument in this case. (For more details about the arguments to this command, see .) - # cloud-setup-databases cloud:dbpassword@dbhost -e encryption_type -m management_server_key -k database_key + # cloud-setup-databases cloud:dbpassword@dbhost -e encryption_type -m management_server_key -k database_key + Configure the OS and start the Management Server: - # cloud-setup-management + # cloud-setup-management The Management Server on this node should now be running. @@ -66,4 +69,4 @@ linkend="sect-source-buildrpm"/> or as Load Balancing.
- + \ No newline at end of file diff --git a/docs/en-US/management-server-install-overview.xml b/docs/en-US/management-server-install-overview.xml index ba4c8f667ff..5f46b0099bd 100644 --- a/docs/en-US/management-server-install-overview.xml +++ b/docs/en-US/management-server-install-overview.xml @@ -23,23 +23,21 @@ -->
- Management Server Installation Overview - This section describes installing the Management Server. There are two slightly different installation flows, depending on how many Management Server nodes will be in your cloud: - - A single Management Server node, with MySQL on the same node. - Multiple Management Server nodes, with MySQL on a node separate from the Management Servers. - - In either case, each machine must meet the system requirements described in System Requirements. - For the sake of security, be sure the public Internet can not access port 8096 or port 8250 on the Management Server. - - The procedure for installing the Management Server is: - - + Management Server Installation Overview + This section describes installing the Management Server. There are two slightly different installation flows, depending on how many Management Server nodes will be in your cloud: + + A single Management Server node, with MySQL on the same node. + Multiple Management Server nodes, with MySQL on a node separate from the Management Servers. + + In either case, each machine must meet the system requirements described in System Requirements. + For the sake of security, be sure the public Internet can not access port 8096 or port 8250 on the Management Server. + The procedure for installing the Management Server is: + Prepare the Operating System - Download and install vhd-util. + (XenServer only) Download and install vhd-util. Install the First Management Server Install and Configure the MySQL database From c2ccc52c4d078e67ca12c7896de2d034adb8ad85 Mon Sep 17 00:00:00 2001 From: Jessica Tomechak Date: Mon, 5 Nov 2012 12:47:37 -0800 Subject: [PATCH 77/88] Docs. Remove inaccurate CloudStack software version number from table heading. --- docs/en-US/using-netscaler-load-balancers.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/using-netscaler-load-balancers.xml b/docs/en-US/using-netscaler-load-balancers.xml index 7febb8248a3..c2044de527b 100644 --- a/docs/en-US/using-netscaler-load-balancers.xml +++ b/docs/en-US/using-netscaler-load-balancers.xml @@ -34,7 +34,7 @@ NetScaler ADC Type Description of Capabilities - &PRODUCT; 3.0.3 Supported Features + &PRODUCT; Supported Features From becd611c0766bdbee2d99a5a030b42f8b54dc56d Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 5 Nov 2012 15:38:09 -0800 Subject: [PATCH 78/88] cloudstack UI - Infrastructure page - (1) restore hosts count on UI since listHosts API has been fixed. (2) specifying pagesize as 1 because we don't need any embedded objects to be returned for count display. --- ui/scripts/system.js | 52 ++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7d3f1b9f3c9..ee269641c25 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -194,6 +194,10 @@ podCount: function(data) { $.ajax({ url: createURL('listPods'), + data: { + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. + }, success: function(json) { dataFns.clusterCount($.extend(data, { podCount: json.listpodsresponse.count ? @@ -206,20 +210,24 @@ clusterCount: function(data) { $.ajax({ url: createURL('listClusters'), - success: function(json) { - /* + data: { + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. + }, + success: function(json) { dataFns.hostCount($.extend(data, { clusterCount: json.listclustersresponse.count ? json.listclustersresponse.count : 0 })); - */ + + //comment the 4 lines above and uncomment the following 4 lines if listHosts API still responds slowly. - //uncomment the 4 lines above and remove the following 4 lines after "count" in listHosts API is fixed. + /* dataFns.primaryStorageCount($.extend(data, { clusterCount: json.listclustersresponse.count ? json.listclustersresponse.count : 0 })); - + */ } }); }, @@ -228,7 +236,9 @@ $.ajax({ url: createURL('listHosts'), data: { - type: 'routing' + type: 'routing', + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. }, success: function(json) { dataFns.primaryStorageCount($.extend(data, { @@ -242,20 +252,24 @@ primaryStorageCount: function(data) { $.ajax({ url: createURL('listStoragePools'), - success: function(json) { - /* + data: { + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. + }, + success: function(json) { dataFns.secondaryStorageCount($.extend(data, { primaryStorageCount: json.liststoragepoolsresponse.count ? json.liststoragepoolsresponse.count : 0 })); - */ + + //comment the 4 lines above and uncomment the following 4 lines if listHosts API still responds slowly. - //uncomment the 4 lines above and remove the following 4 lines after "count" in listHosts API is fixed. + /* dataFns.systemVmCount($.extend(data, { primaryStorageCount: json.liststoragepoolsresponse.count ? json.liststoragepoolsresponse.count : 0 })); - + */ } }); }, @@ -264,7 +278,9 @@ $.ajax({ url: createURL('listHosts'), data: { - type: 'SecondaryStorage' + type: 'SecondaryStorage', + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. }, success: function(json) { dataFns.systemVmCount($.extend(data, { @@ -278,6 +294,10 @@ systemVmCount: function(data) { $.ajax({ url: createURL('listSystemVms'), + data: { + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. + }, success: function(json) { dataFns.virtualRouterCount($.extend(data, { systemVmCount: json.listsystemvmsresponse.count ? @@ -291,14 +311,18 @@ $.ajax({ url: createURL('listRouters'), data: { - projectid: -1 + projectid: -1, + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. }, success: function(json) { var total1 = json.listroutersresponse.count ? json.listroutersresponse.count : 0; $.ajax({ url: createURL('listRouters'), data: { - listAll: true + listAll: true, + page: 1, + pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. }, success: function(json) { var total2 = json.listroutersresponse.count ? json.listroutersresponse.count : 0; From c91bde7d4bdef6f8c02a94adbe7dc70aa5d83e34 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:12:18 -0800 Subject: [PATCH 79/88] Fix capitalization of 'Site-to-site VPN' --- .../classes/resources/messages.properties | 2 +- ui/scripts/network.js | 86 +++++++++---------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index ce76a84c9b3..71f8194ee0f 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -30,6 +30,7 @@ label.broadcast.uri=Broadcast URI #modified labels (begin) ***************************************************************************************** +label.site.to.site.VPN=Site-to-site VPN message.zoneWizard.enable.local.storage=WARNING: If you enable local storage for this zone, you must do the following, depending on where you would like your system VMs to launch:

1. If system VMs need to be launched in primary storage, primary storage needs to be added to the zone after creation. You must also start the zone in a disabled state.

2. If system VMs need to be launched in local storage, system.vm.use.local.storage needs to be set to true before you enable the zone.


Would you like to continue? #modified labels (end) ******************************************************************************************* @@ -61,7 +62,6 @@ label.CIDR.of.destination.network=CIDR of destination network label.add.route=Add route label.add.static.route=Add static route label.remove.static.route=Remove static route -label.site.to.site.VPN=site-to-site VPN label.add.VPN.gateway=Add VPN Gateway message.add.VPN.gateway=Please confirm that you want to add a VPN Gateway label.VPN.gateway=VPN Gateway diff --git a/ui/scripts/network.js b/ui/scripts/network.js index a149bfa82a0..be362ff84f3 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -4030,52 +4030,50 @@ }); } }, - - router: { - title: 'VPC Router Details', - fields:[ - { - name: {label:'label.name'} - }, - { - id:{ label:'label.id'}, - zonename: { label: 'label.zone'}, - dns1: {label: 'label.dns'}, - gateway: {label:'label.gateway'}, - publicip: {label: 'label.public.ip'}, - guestipaddress:{ label: 'label.guest.ip'}, - linklocalip: {label: 'label.linklocal.ip'}, - state: { label:'label.state'}, - serviceofferingname: {label:'label.service.offering'}, - isredundantrouter:{ - label: 'label.redundant.router', - converter: function(booleanValue) { - if (booleanValue == true) { - return "Yes"; - } - return "No"; - } - }, - account: {label:'label.account'}, - domain: {label: 'label.domain'} + router: { + title: 'VPC Router Details', + fields:[ + { + name: {label:'label.name'} + }, + { + id:{ label:'label.id'}, + zonename: { label: 'label.zone'}, + dns1: {label: 'label.dns'}, + gateway: {label:'label.gateway'}, + publicip: {label: 'label.public.ip'}, + guestipaddress:{ label: 'label.guest.ip'}, + linklocalip: {label: 'label.linklocal.ip'}, + state: { label:'label.state'}, + serviceofferingname: {label:'label.service.offering'}, + isredundantrouter:{ + label: 'label.redundant.router', + converter: function(booleanValue) { + if (booleanValue == true) { + return "Yes"; } - ], - dataProvider: function(args) { - $.ajax ({ - url:createURL("listRouters&listAll=true&vpcid=" +args.context.vpc[0].id), - dataType: "json", - async: true, - success:function(json) { - var item = json.listroutersresponse.router[0]; - args.response.success ({ - data:item - }) - } - - }); - } - + return "No"; + } + }, + account: {label:'label.account'}, + domain: {label: 'label.domain'} + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL("listRouters&listAll=true&vpcid=" +args.context.vpc[0].id), + dataType: "json", + async: true, + success: function(json) { + var item = json.listroutersresponse.router[0]; + + args.response.success({ + data:item + }); } + }); + } + } } } } From a7138d96659bdf15a036ea9d8899da3413067440 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 14:52:19 -0800 Subject: [PATCH 80/88] CS-16732: Show virtual router actions on VPC detail view --- ui/scripts/network.js | 1 + ui/scripts/system.js | 2 +- ui/scripts/vpc.js | 21 ++++++++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index be362ff84f3..dae4dfb23b5 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -4068,6 +4068,7 @@ var item = json.listroutersresponse.router[0]; args.response.success({ + actionFilter: cloudStack.sections.system.routerActionFilter, data:item }); } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index ee269641c25..5a84bf8027b 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -10353,7 +10353,7 @@ return allowedActions; } - var routerActionfilter = function(args) { + var routerActionfilter = cloudStack.sections.system.routerActionFilter = function(args) { var jsonObj = args.context.item; var allowedActions = []; diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index a79006eab3f..6ce7e8b2369 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -251,8 +251,27 @@ routerDetailView: function() { return { title: 'VPC router details', + updateContext: function(args) { + var router; + + $.ajax({ + url: createURL("listRouters&listAll=true&vpcid=" +args.context.vpc[0].id), + dataType: "json", + async: false, + success: function(json) { + router = json.listroutersresponse.router[0]; + } + }); + + return { + routers: [router] + }; + }, + actions: cloudStack.sections.system.subsections.virtualRouters + .listView.detailView.actions, tabs: { - routerDetails: cloudStack.sections.network.sections.vpc.listView.detailView.tabs.router + routerDetails: cloudStack.sections.network.sections.vpc + .listView.detailView.tabs.router } }; }, From 58de3c3dfe250be87eb7fe8741d3cc3a4b61fc1a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:04:22 -0800 Subject: [PATCH 81/88] CS-16735: Tier details: Disable IP address tab Disables IP address tab for VPC tier detail view, as it is redundant with the 'view all' button. --- ui/scripts/vpc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 6ce7e8b2369..e805282aba0 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -1614,7 +1614,8 @@ } }); - var hiddenTabs = []; + var hiddenTabs = ['ipAddresses']; // Disable IP address tab; it is redundant with 'view all' button + if(networkOfferingHavingELB == false) hiddenTabs.push("addloadBalancer"); return hiddenTabs; From c3f1a694aec1d7e8deb6f881d9d339f2a015d054 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:24:30 -0800 Subject: [PATCH 82/88] Add network-level egress UI Adds new tab to network section details to add/remove/list network-level egress rules. This tab only appears for isolated source NAT networks. It is based on the same format as the firewall rule edit. --- ui/scripts/network.js | 306 ++++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 147 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index dae4dfb23b5..a7482f81f40 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -3416,172 +3416,184 @@ }, egressRules: { title: 'label.egress.rule', - custom: cloudStack.uiCustom.securityRules({ - noSelect: true, - noHeaderActionsColumn: true, - fields: { - 'protocol': { - label: 'label.protocol', - select: function(args) { - args.$select.change(function() { - var $inputs = args.$form.find('th, td'); - var $icmpFields = $inputs.filter(function() { - var name = $(this).attr('rel'); + custom: function(args) { + var context = args.context; - return $.inArray(name, [ - 'icmptype', - 'icmpcode' - ]) > -1; - }); - var $otherFields = $inputs.filter(function() { - var name = $(this).attr('rel'); + return $('
').multiEdit({ + context: context, + noSelect: true, + noHeaderActionsColumn: true, + fields: { + 'cidrlist': { edit: true, label: 'label.cidr' }, + 'protocol': { + label: 'label.protocol', + select: function(args) { + args.$select.change(function() { + var $inputs = args.$form.find('th, td'); + var $icmpFields = $inputs.filter(function() { + var name = $(this).attr('rel'); - return name != 'icmptype' && - name != 'icmpcode' && - name != 'protocol' && - name != 'add-rule' && - name != 'cidr' && - name != 'accountname' && - name != 'securitygroup'; + return $.inArray(name, [ + 'icmptype', + 'icmpcode' + ]) > -1; + }); + var $otherFields = $inputs.filter(function() { + var name = $(this).attr('rel'); + + return name != 'cidrlist' && + name != 'icmptype' && + name != 'icmpcode' && + name != 'protocol' && + name != 'add-rule'; + }); + + if ($(this).val() == 'icmp') { + $icmpFields.show(); + $otherFields.hide(); + } else { + $icmpFields.hide(); + $otherFields.show(); + } }); - if ($(this).val() == 'icmp') { - $icmpFields.show(); - $otherFields.hide(); - } else { - $icmpFields.hide(); - $otherFields.show(); + args.response.success({ + data: [ + { name: 'tcp', description: 'TCP' }, + { name: 'udp', description: 'UDP' }, + { name: 'icmp', description: 'ICMP' } + ] + }); + } + }, + 'startport': { edit: true, label: 'label.start.port' }, + 'endport': { edit: true, label: 'label.end.port' }, + 'icmptype': { edit: true, label: 'ICMP.type', isHidden: true }, + 'icmpcode': { edit: true, label: 'ICMP.code', isHidden: true }, + 'add-rule': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + var data = { + protocol: args.data.protocol, + cidrlist: args.data.cidrlist, + trafficType: 'Egress' + }; + + if (args.data.icmptype && args.data.icmpcode) { // ICMP + $.extend(data, { + icmptype: args.data.icmptype, + icmpcode: args.data.icmpcode + }); + } else { // TCP/UDP + $.extend(data, { + startport: args.data.startport, + endport: args.data.endport + }); + } + + // Get Source NAT IP + var sourceNATIP; + + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: { + listAll: true, + associatednetworkid: args.context.networks[0].id + }, + async: false, + success: function(json) { + var ipAddresses = json.listpublicipaddressesresponse.publicipaddress; + + sourceNATIP = $.grep(ipAddresses, function(ipAddress) { + return ipAddress.issourcenat; + })[0]; } }); - args.response.success({ - data: [ - { name: 'tcp', description: 'TCP' }, - { name: 'udp', description: 'UDP' }, - { name: 'icmp', description: 'ICMP' } - ] + data.ipaddressid = sourceNATIP.id; + + $.ajax({ + url: createURL('createFirewallRule'), + data: data, + dataType: 'json', + async: true, + success: function(json) { + var jobId = json.createfirewallruleresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobId + }, + notification: { + label: 'label.add.egress.rule', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } }); } }, - 'startport': { edit: true, label: 'label.start.port' }, - 'endport': { edit: true, label: 'label.end.port' }, - 'icmptype': { edit: true, label: 'ICMP.type', isHidden: true }, - 'icmpcode': { edit: true, label: 'ICMP.code', isHidden: true }, - 'cidr': { edit: true, label: 'label.cidr', isHidden: true }, - 'accountname': { - edit: true, - label: 'label.account.and.security.group', - isHidden: true, - range: ['accountname', 'securitygroup'] + actions: { + destroy: { + label: 'label.remove.rule', + action: function(args) { + $.ajax({ + url: createURL('deleteFirewallRule'), + data: { + id: args.context.multiRule[0].id + }, + dataType: 'json', + async: true, + success: function(data) { + var jobID = data.deletefirewallruleresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobID + }, + notification: { + label: 'label.remove.egress.rule', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } }, - 'add-rule': { - label: 'label.add', - addButton: true - } - }, - add: { - label: 'label.add', - action: function(args) { - var data = { - securitygroupid: args.context.securityGroups[0].id, - protocol: args.data.protocol, - domainid: args.context.securityGroups[0].domainid, - account: args.context.securityGroups[0].account - }; - - // TCP / ICMP - if (args.data.icmptype && args.data.icmpcode) { // ICMP - $.extend(data, { - icmptype: args.data.icmptype, - icmpcode: args.data.icmpcode - }); - } else { // TCP - $.extend(data, { - startport: args.data.startport, - endport: args.data.endport - }); - } - - // CIDR / account - if (args.data.cidr) { - data.cidrlist = args.data.cidr; - } else { - data['usersecuritygrouplist[0].account'] = args.data.accountname; - data['usersecuritygrouplist[0].group'] = args.data.securitygroup; - } - + ignoreEmptyFields: true, + dataProvider: function(args) { $.ajax({ - url: createURL('authorizeSecurityGroupEgress'), - data: data, + url: createURL('listFirewallRules'), + data: { + listAll: true, + networkid: args.context.networks[0].id, + trafficType: 'Egress' + }, dataType: 'json', async: true, - success: function(data) { - var jobId = data.authorizesecuritygroupegressresponse.jobid; - + success: function(json) { + var response = json.listfirewallrulesresponse.firewallrule; + args.response.success({ - _custom: { - jobId: jobId - }, - notification: { - label: 'label.add.egress.rule', - poll: pollAsyncJobResult - } + data: response }); } }); } - }, - actions: { - destroy: { - label: 'label.remove.rule', - action: function(args) { - $.ajax({ - url: createURL('revokeSecurityGroupEgress'), - data: { - domainid: args.context.securityGroups[0].domainid, - account: args.context.securityGroups[0].account, - id: args.context.multiRule[0].id - }, - dataType: 'json', - async: true, - success: function(data) { - var jobID = data.revokesecuritygroupegress.jobid; - - args.response.success({ - _custom: { - jobId: jobID - }, - notification: { - label: 'label.remove.egress.rule', - poll: pollAsyncJobResult - } - }); - } - }); - } - } - }, - ignoreEmptyFields: true, - dataProvider: function(args) { - $.ajax({ - url: createURL('listSecurityGroups'), - data: { - id: args.context.securityGroups[0].id - }, - dataType: 'json', - async: true, - success: function(data) { - args.response.success({ - data: $.map( - data.listsecuritygroupsresponse.securitygroup[0].egressrule ? - data.listsecuritygroupsresponse.securitygroup[0].egressrule : [], - ingressEgressDataMap - ) - }); - } - }); - } - }) + }); + } } }, From 7707b3b4b3a161480506feabad6f49cfb2782ece Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:20:42 -0800 Subject: [PATCH 83/88] Update tabFilter for egress rules --- ui/scripts/network.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index a7482f81f40..ace810beee1 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -762,6 +762,7 @@ tabFilter: function(args) { var networkOfferingHavingELB = false; var hasNetworkACL = false; + var isVPC = false; $.ajax({ url: createURL("listNetworkOfferings&id=" + args.context.networks[0].networkofferingid), @@ -770,6 +771,10 @@ success: function(json) { var networkoffering = json.listnetworkofferingsresponse.networkoffering[0]; + if (networkoffering.forvpc) { + isVPC = true; + } + $(networkoffering.service).each(function(){ var thisService = this; @@ -792,7 +797,7 @@ hiddenTabs.push("addloadBalancer"); } - if (!hasNetworkACL) { + if (!hasNetworkACL || isVPC) { hiddenTabs.push('egressRules'); } @@ -3414,6 +3419,7 @@ } }) }, + egressRules: { title: 'label.egress.rule', custom: function(args) { From 13f286158b75077d4c7ecae4ef3623c3e700f157 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:37:31 -0800 Subject: [PATCH 84/88] Disable egress UI, for now --- ui/scripts/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index ace810beee1..855e1c05023 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -791,7 +791,7 @@ } }); - var hiddenTabs = []; + var hiddenTabs = ['egressRules']; // Disable egress UI, for now if (!networkOfferingHavingELB) { hiddenTabs.push("addloadBalancer"); From d4c1848f33414e88bbde0eeec2c92f3c2d1bb198 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:50:03 -0800 Subject: [PATCH 85/88] Fix zone wizard multi-edit for Firefox --- ui/css/cloudstack3.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index f8bf872a4a8..4a067ea9ec1 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -6816,7 +6816,9 @@ label.error { width: 98%; } -.multi-wizard.zone-wizard .multi-edit table th { +.multi-wizard.zone-wizard .multi-edit table th, +.multi-wizard.zone-wizard .multi-edit table td { + min-width: 97px; max-width: 97px; } From ee72ae677fb0f6c84ae6a323e74e8bf4d848eefd Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 15:59:37 -0800 Subject: [PATCH 86/88] Fix input field widths for multi-edit Conflicts: ui/css/cloudstack3.css --- ui/css/cloudstack3.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 4a067ea9ec1..c50a412a3f7 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7212,9 +7212,10 @@ div.panel.ui-dialog div.list-view div.fixed-header { } .multi-edit .range input { - width: 70px; + width: 41px; margin-left: 13px; position: relative; + float: left; } .multi-edit .range label { @@ -10353,6 +10354,13 @@ div.ui-dialog div.acl div.multi-edit div.data div.data-body div.data-item table max-height: 600px; } +div.container div.panel div#details-tab-network.detail-group div div.multi-edit table.multi-edit tbody tr td, +div.container div.panel div#details-tab-network.detail-group div div.multi-edit table.multi-edit thead tr th { + min-width: 80px; + max-width: 80px; + font-size: 10px; +} + .ui-dialog div.autoscaler .detail-actions { } From 46cce3a891a77e91b5cb968bc4f31dc093ecc1c1 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 5 Nov 2012 16:02:07 -0800 Subject: [PATCH 87/88] Multi-edit: Fix width of select inputs --- ui/css/cloudstack3.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index c50a412a3f7..db5f0931651 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7199,6 +7199,11 @@ div.panel.ui-dialog div.list-view div.fixed-header { width: 70%; } +.detail-view .multi-edit select { + width: 93%; + font-size: 10px; +} + .multi-edit input { width: 85%; } From 126b4b57903f55c51a8272e0adae324bf5cdeb37 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 5 Nov 2012 16:32:14 -0800 Subject: [PATCH 88/88] CS-16483: cloudstack UI - template page, ISO page - delete action is available if template/ISO and the login user is under the same project. --- ui/scripts/templates.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index f6c209a4a30..c865d740671 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -1428,7 +1428,7 @@ // "Delete Template" //if (((isUser() && jsonObj.ispublic == true && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account))) - if (((isAdmin() == false && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account))) //if neither root-admin, nor item owner + if (((isAdmin() == false && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account) && !(jsonObj.domainid == g_domainid && jsonObj.projectid == cloudStack.context.projects[0].id))) //if neither root-admin, nor the same account, nor the same project || (jsonObj.isready == false && jsonObj.status != null && jsonObj.status.indexOf("Downloaded") != -1) || (jsonObj.account == "system")) { //do nothing @@ -1487,7 +1487,7 @@ // "Delete ISO" //if (((isUser() && jsonObj.ispublic == true && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account))) - if (((isAdmin() == false && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account))) //if neither root-admin, nor item owner + if (((isAdmin() == false && !(jsonObj.domainid == g_domainid && jsonObj.account == g_account) && !(jsonObj.domainid == g_domainid && jsonObj.projectid == cloudStack.context.projects[0].id))) //if neither root-admin, nor the same account, nor the same project || (jsonObj.isready == false && jsonObj.status != null && jsonObj.status.indexOf("Downloaded") != -1) || (jsonObj.account == "system") ) {