CLOUDSTACK-3049: Implemented role update for account. (#3058)

This commit is contained in:
Bitworks LLC 2019-01-25 19:02:56 +07:00 committed by Gabriel Beims Bräscher
parent 97ddd8dffd
commit d68712eb7b
8 changed files with 500 additions and 182 deletions

View File

@ -16,6 +16,7 @@
# under the License. # under the License.
cloudstack/tools/docker/Dockerfile cloudstack/tools/docker/Dockerfile
cloudstack/tools/docker/Dockerfile.smokedev
.dockerignore .dockerignore
.idea .idea
.git .git

View File

@ -21,6 +21,7 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import org.apache.cloudstack.api.response.RoleResponse;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -48,16 +49,19 @@ public class UpdateAccountCmd extends BaseCmd {
//////////////// API parameters ///////////////////// //////////////// API parameters /////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ACL(accessType = AccessType.OperateEntry) @ACL(accessType = AccessType.OperateEntry)
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account id") @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID")
private Long id; private Long id;
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the current account name") @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "Current account name")
private String accountName; private String accountName;
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the ID of the domain where the account exists") @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The UUID of the domain where the account exists")
private Long domainId; private Long domainId;
@Parameter(name = ApiConstants.NEW_NAME, type = CommandType.STRING, required = true, description = "new name for the account") @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "The UUID of the dynamic role to set for the account")
private Long roleId;
@Parameter(name = ApiConstants.NEW_NAME, type = CommandType.STRING, description = "New name for the account")
private String newName; private String newName;
@Parameter(name = ApiConstants.NETWORK_DOMAIN, @Parameter(name = ApiConstants.NETWORK_DOMAIN,
@ -65,7 +69,7 @@ public class UpdateAccountCmd extends BaseCmd {
description = "Network domain for the account's networks; empty string will update domainName with NULL value") description = "Network domain for the account's networks; empty string will update domainName with NULL value")
private String networkDomain; private String networkDomain;
@Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters") @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for the account used to store specific parameters")
private Map details; private Map details;
@Inject @Inject
@ -87,6 +91,8 @@ public class UpdateAccountCmd extends BaseCmd {
return domainId; return domainId;
} }
public Long getRoleId() { return roleId; }
public String getNewName() { public String getNewName() {
return newName; return newName;
} }

View File

@ -37,8 +37,10 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.QuerySelector; import org.apache.cloudstack.acl.QuerySelector;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -1019,8 +1021,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
@DB @DB
@ActionEvents({@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account"), @ActionEvents({@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account"),
@ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")}) @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")})
public UserAccount createUserAccount(final String userName, final String password, final String firstName, final String lastName, final String email, final String timezone, String accountName, public UserAccount createUserAccount(final String userName, final String password, final String firstName,
final short accountType, final Long roleId, Long domainId, final String networkDomain, final Map<String, String> details, String accountUUID, final String userUUID, final String lastName, final String email, final String timezone,
String accountName, final short accountType, final Long roleId, Long domainId,
final String networkDomain, final Map<String, String> details,
String accountUUID, final String userUUID,
final User.Source source) { final User.Source source) {
if (accountName == null) { if (accountName == null) {
@ -1155,7 +1160,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
UserVO user = retrieveAndValidateUser(updateUserCmd); UserVO user = retrieveAndValidateUser(updateUserCmd);
s_logger.debug("Updating user with Id: " + user.getUuid()); s_logger.debug("Updating user with Id: " + user.getUuid());
validateAndUpdatApiAndSecretKeyIfNeeded(updateUserCmd, user); validateAndUpdateApiAndSecretKeyIfNeeded(updateUserCmd, user);
Account account = retrieveAndValidateAccount(user); Account account = retrieveAndValidateAccount(user);
validateAndUpdateFirstNameIfNeeded(updateUserCmd, user); validateAndUpdateFirstNameIfNeeded(updateUserCmd, user);
@ -1344,7 +1349,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
* <li>If a pair of keys is provided, we validate to see if there is an user already using the provided API key. If there is someone else using, we throw an {@link InvalidParameterValueException} because two users cannot have the same API key. * <li>If a pair of keys is provided, we validate to see if there is an user already using the provided API key. If there is someone else using, we throw an {@link InvalidParameterValueException} because two users cannot have the same API key.
* </ul> * </ul>
*/ */
protected void validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmd updateUserCmd, UserVO user) { protected void validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmd updateUserCmd, UserVO user) {
String apiKey = updateUserCmd.getApiKey(); String apiKey = updateUserCmd.getApiKey();
String secretKey = updateUserCmd.getSecretKey(); String secretKey = updateUserCmd.getSecretKey();
@ -1687,6 +1692,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
public AccountVO updateAccount(UpdateAccountCmd cmd) { public AccountVO updateAccount(UpdateAccountCmd cmd) {
Long accountId = cmd.getId(); Long accountId = cmd.getId();
Long domainId = cmd.getDomainId(); Long domainId = cmd.getDomainId();
Long roleId = cmd.getRoleId();
String accountName = cmd.getAccountName(); String accountName = cmd.getAccountName();
String newAccountName = cmd.getNewName(); String newAccountName = cmd.getNewName();
String networkDomain = cmd.getNetworkDomain(); String networkDomain = cmd.getNetworkDomain();
@ -1700,6 +1706,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
account = _accountDao.findEnabledAccount(accountName, domainId); account = _accountDao.findEnabledAccount(accountName, domainId);
} }
final AccountVO acctForUpdate = _accountDao.findById(account.getId());
// Check if account exists // Check if account exists
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) { if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
s_logger.error("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId); s_logger.error("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
@ -1712,25 +1720,48 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
} }
// Check if user performing the action is allowed to modify this account // Check if user performing the action is allowed to modify this account
checkAccess(getCurrentCallingAccount(), _domainMgr.getDomain(account.getDomainId())); Account caller = getCurrentCallingAccount();
checkAccess(caller, _domainMgr.getDomain(account.getDomainId()));
// check if the given account name is unique in this domain for updating if(newAccountName != null) {
Account duplicateAcccount = _accountDao.findActiveAccount(newAccountName, domainId);
if (duplicateAcccount != null && duplicateAcccount.getId() != account.getId()) { if (newAccountName.isEmpty()) {
throw new InvalidParameterValueException( throw new InvalidParameterValueException("The new account name for account '" + account.getUuid() + "' " +
"There already exists an account with the name:" + newAccountName + " in the domain:" + domainId + " with existing account id:" + duplicateAcccount.getId()); "within domain '" + domainId + "' is empty string. Account will be not renamed.");
}
// check if the new proposed account name is absent in the domain
Account existingAccount = _accountDao.findActiveAccount(newAccountName, domainId);
if (existingAccount != null && existingAccount.getId() != account.getId()) {
throw new InvalidParameterValueException("The account with the proposed name '" +
newAccountName + "' exists in the domain '" +
domainId + "' with existing account id '" + existingAccount.getId() + "'");
}
acctForUpdate.setAccountName(newAccountName);
} }
if (networkDomain != null && !networkDomain.isEmpty()) { if (networkDomain != null && !networkDomain.isEmpty()) {
if (!NetUtils.verifyDomainName(networkDomain)) { if (!NetUtils.verifyDomainName(networkDomain)) {
throw new InvalidParameterValueException( throw new InvalidParameterValueException("Invalid network domain or format. " +
"Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " "Total length shouldn't exceed 190 chars. Every domain part must be between 1 and 63 " +
"characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
+ "and the hyphen ('-'); can't start or end with \"-\""); + "and the hyphen ('-'); can't start or end with \"-\"");
} }
} }
final AccountVO acctForUpdate = _accountDao.findById(account.getId());
acctForUpdate.setAccountName(newAccountName); if (roleId != null) {
final List<Role> roles = cmd.roleService.listRoles();
final boolean roleNotFound = roles.stream().filter(r -> r.getId() == roleId).count() == 0;
if (roleNotFound) {
throw new InvalidParameterValueException("Role with ID '" + roleId.toString() + "' " +
"is not found or not available for the account '" + account.getUuid() + "' " +
"in the domain '" + domainId + "'.");
}
acctForUpdate.setRoleId(roleId);
}
if (networkDomain != null) { if (networkDomain != null) {
if (networkDomain.isEmpty()) { if (networkDomain.isEmpty()) {
@ -1741,17 +1772,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
} }
final Account accountFinal = account; final Account accountFinal = account;
success = Transaction.execute(new TransactionCallback<Boolean>() { success = Transaction.execute((TransactionCallback<Boolean>) status -> {
@Override boolean success1 = _accountDao.update(accountFinal.getId(), acctForUpdate);
public Boolean doInTransaction(TransactionStatus status) {
boolean success = _accountDao.update(accountFinal.getId(), acctForUpdate);
if (details != null && success) { if (details != null && success1) {
_accountDetailsDao.update(accountFinal.getId(), details); _accountDetailsDao.update(accountFinal.getId(), details);
} }
return success; return success1;
}
}); });
if (success) { if (success) {

View File

@ -226,7 +226,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
private void prepareMockAndExecuteUpdateUserTest(int numberOfExpectedCallsForSetEmailAndSetTimeZone) { private void prepareMockAndExecuteUpdateUserTest(int numberOfExpectedCallsForSetEmailAndSetTimeZone) {
Mockito.doReturn(userVoMock).when(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock); Mockito.doReturn(userVoMock).when(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock);
Mockito.doNothing().when(accountManagerImpl).validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); Mockito.doNothing().when(accountManagerImpl).validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
Mockito.doReturn(accountMock).when(accountManagerImpl).retrieveAndValidateAccount(userVoMock); Mockito.doReturn(accountMock).when(accountManagerImpl).retrieveAndValidateAccount(userVoMock);
Mockito.doNothing().when(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); Mockito.doNothing().when(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock);
@ -242,7 +242,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
InOrder inOrder = Mockito.inOrder(userVoMock, accountManagerImpl, userDaoMock, userAccountDaoMock); InOrder inOrder = Mockito.inOrder(userVoMock, accountManagerImpl, userDaoMock, userAccountDaoMock);
inOrder.verify(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock); inOrder.verify(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock);
inOrder.verify(accountManagerImpl).validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); inOrder.verify(accountManagerImpl).validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
inOrder.verify(accountManagerImpl).retrieveAndValidateAccount(userVoMock); inOrder.verify(accountManagerImpl).retrieveAndValidateAccount(userVoMock);
inOrder.verify(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); inOrder.verify(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock);
@ -275,7 +275,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
@Test @Test
public void validateAndUpdatApiAndSecretKeyIfNeededTestNoKeys() { public void validateAndUpdatApiAndSecretKeyIfNeededTestNoKeys() {
accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); accountManagerImpl.validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
Mockito.verify(accountDaoMock, Mockito.times(0)).findUserAccountByApiKey(Mockito.anyString()); Mockito.verify(accountDaoMock, Mockito.times(0)).findUserAccountByApiKey(Mockito.anyString());
} }
@ -284,14 +284,14 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlyApiKeyInformed() { public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlyApiKeyInformed() {
Mockito.doReturn("apiKey").when(UpdateUserCmdMock).getApiKey(); Mockito.doReturn("apiKey").when(UpdateUserCmdMock).getApiKey();
accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); accountManagerImpl.validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
} }
@Test(expected = InvalidParameterValueException.class) @Test(expected = InvalidParameterValueException.class)
public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlySecretKeyInformed() { public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlySecretKeyInformed() {
Mockito.doReturn("secretKey").when(UpdateUserCmdMock).getSecretKey(); Mockito.doReturn("secretKey").when(UpdateUserCmdMock).getSecretKey();
accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); accountManagerImpl.validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
} }
@Test(expected = InvalidParameterValueException.class) @Test(expected = InvalidParameterValueException.class)
@ -308,7 +308,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class)); Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class));
Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey); Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey);
accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); accountManagerImpl.validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
} }
@Test @Test
@ -327,7 +327,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class)); Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class));
Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey); Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey);
accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); accountManagerImpl.validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
Mockito.verify(accountDaoMock).findUserAccountByApiKey(apiKey); Mockito.verify(accountDaoMock).findUserAccountByApiKey(apiKey);
Mockito.verify(userVoMock).setApiKey(apiKey); Mockito.verify(userVoMock).setApiKey(apiKey);

View File

@ -30,7 +30,7 @@ from marvin.lib.base import (Domain,
User, User,
NATRule, NATRule,
Template, Template,
PublicIPAddress) PublicIPAddress, Role)
from marvin.lib.common import (get_domain, from marvin.lib.common import (get_domain,
get_zone, get_zone,
get_test_template, get_test_template,
@ -67,6 +67,11 @@ class Services:
# username # username
"password": "fr3sca", "password": "fr3sca",
}, },
"role": {
"name": "MarvinFake Role",
"type": "User",
"description": "Fake Role created by Marvin test"
},
"user": { "user": {
"email": "user@test.com", "email": "user@test.com",
"firstname": "User", "firstname": "User",
@ -261,6 +266,53 @@ class TestAccounts(cloudstackTestCase):
return return
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"],
required_hardware="false")
def test_02_update_account(self):
"""
Tests that accounts can be updated with new name, network domain, dynamic role
:return:
"""
dynamic_roles_active = self.apiclient.listCapabilities(listCapabilities.listCapabilitiesCmd()).dynamicrolesenabled
if not dynamic_roles_active:
self.skipTest("Dynamic Role-Based API checker not enabled, skipping test")
ts = str(time.time())
network_domain = 'mycloud.com'
account = Account.create(self.apiclient, self.services['account'])
self.cleanup.append(account)
role = Role.create(self.apiclient, self.services['role'])
self.cleanup.append(role)
account.update(self.apiclient, newname=account.name + ts)
account.update(self.apiclient, roleid=role.id)
account.update(self.apiclient, networkdomain=network_domain)
list_accounts_response = list_accounts(self.apiclient, id=account.id)
test_account = list_accounts_response[0]
self.assertEqual(
test_account.roleid, role.id,
"Check the role for the account is changed")
self.assertEqual(
test_account.networkdomain, network_domain,
"Check the domain for the account is changed")
self.assertEqual(
test_account.name, account.name + ts,
"Check the name for the account is changed")
try:
account.update(self.apiclient, newname="")
self.fail("Account name change to empty name succeeded. Must be error.")
except CloudstackAPIException:
pass
class TestRemoveUserFromAccount(cloudstackTestCase): class TestRemoveUserFromAccount(cloudstackTestCase):
@classmethod @classmethod

View File

@ -0,0 +1,143 @@
# 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.
#
# CloudStack-simulator build
FROM ubuntu:16.04
MAINTAINER "Apache CloudStack" <dev@cloudstack.apache.org>
LABEL Vendor="Apache.org" License="ApacheV2" Version="4.12.0-SNAPSHOT"
RUN apt-get -y update && apt-get install -y \
genisoimage \
libffi-dev \
libssl-dev \
git \
sudo \
ipmitool \
maven \
openjdk-8-jdk \
python-dev \
python-setuptools \
python-pip \
python-mysql.connector \
supervisor \
python-crypto \
python-openssl
RUN echo 'mysql-server mysql-server/root_password password root' | debconf-set-selections; \
echo 'mysql-server mysql-server/root_password_again password root' | debconf-set-selections;
RUN apt-get install -qqy mysql-server && \
apt-get clean all && \
mkdir /var/run/mysqld; \
chown mysql /var/run/mysqld
#
# this package is needed if one wants to run marvin tests from
# inside the running simulator.
#
RUN pip install pyOpenSSL
RUN echo '''sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"''' >> /etc/mysql/mysql.conf.d/mysqld.cnf
RUN (/usr/bin/mysqld_safe &); sleep 5; mysqladmin -u root -proot password ''
COPY agent /root/agent
COPY api /root/api
COPY build /root/build
COPY client /root/client
COPY cloud-cli /root/cloud-cli
COPY cloudstack.iml /root/cloudstack.iml
COPY core /root/core
COPY debian /root/debian
COPY deps /root/deps
COPY developer /root/developer
COPY engine /root/engine
COPY framework /root/framework
COPY LICENSE.header /root/LICENSE.header
COPY LICENSE /root/LICENSE
COPY maven-standard /root/maven-standard
COPY NOTICE /root/NOTICE
COPY packaging /root/packaging
COPY plugins /root/plugins
COPY pom.xml /root/pom.xml
COPY python /root/python
COPY quickcloud /root/quickcloud
COPY requirements.txt /root/requirements.txt
COPY scripts /root/scripts
COPY server /root/server
COPY services /root/services
COPY setup /root/setup
COPY systemvm /root/systemvm
COPY target /root/target
COPY test/bindirbak /root/test/bindirbak
COPY test/conf /root/test/conf
COPY test/metadata /root/test/metadata
COPY test/pom.xml /root/test/pom.xml
COPY test/scripts /root/test/scripts
COPY test/selenium /root/test/selenium
COPY test/systemvm /root/test/systemvm
COPY test/target /root/test/target
COPY tools/pom.xml /root/tools/pom.xml
COPY tools/apidoc /root/tools/apidoc
COPY tools/checkstyle /root/tools/checkstyle
COPY tools/devcloud4/pom.xml /root/tools/devcloud4/pom.xml
COPY tools/devcloud-kvm/pom.xml /root/tools/devcloud-kvm/pom.xml
COPY tools/marvin/pom.xml /root/tools/marvin/pom.xml
COPY tools/pom.xml /root/tools/pom.xml
COPY ui /root/ui
COPY usage /root/usage
COPY utils /root/utils
COPY vmware-base /root/vmware-base
RUN cd /root && mvn -Pdeveloper -Dsimulator -DskipTests -pl "!:cloud-marvin" install
RUN (/usr/bin/mysqld_safe &) && \
sleep 5 && \
cd /root && \
mvn -Pdeveloper -pl developer -Ddeploydb && \
mvn -Pdeveloper -pl developer -Ddeploydb-simulator
COPY tools/marvin /root/tools/marvin
COPY tools/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY tools/docker/docker_run_tests.sh /root
RUN cd /root && mvn -Pdeveloper -Dsimulator -DskipTests -pl ":cloud-marvin"
RUN MARVIN_FILE=`find /root/tools/marvin/dist/ -name "Marvin*.tar.gz"` && pip install $MARVIN_FILE
COPY test/integration /root/test/integration
COPY tools /root/tools
RUN pip install --upgrade pyOpenSSL
EXPOSE 8080 8096
WORKDIR /root
CMD ["/usr/bin/supervisord"]
# --------------------------------
#
# docker run -v ~/dev/tmp:/tmp -v ~/IdeaProjects/cloudstack/test/integration/smoke:/root/test/integration/smoke -it
# --name simulator -p 8080:8080 -p8096:8096 simulator:4.12
#
# docker exec -it simulator bash
#
# cat /root/docker_run_tests.sh
# for instructions
#

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
MODE=${1:-advanced}
SUITE=${2:-smoke}
export MARVIN_CONFIG=setup/dev/$MODE.cfg
export TEST_SUITE=test/integration/$SUITE
export ZONE_NAME=Sandbox-simulator
cd /root
python tools/marvin/marvin/deployDataCenter.py -i setup/dev/$MODE.cfg
cat <<EOF
RUN WHOLE '$SUITE' SUITE
--------------------------
nosetests-2.7 \
--with-marvin \
--marvin-config=${MARVIN_CONFIG} \
-w ${TEST_SUITE} \
--with-xunit \
--xunit-file=/tmp/bvt_selfservice_cases.xml \
--zone=${ZONE_NAME} \
--hypervisor=simulator \
-a tags=$MODE,required_hardware=false
--------------------------
OR INDIVIDUAL TEST LIKE
--------------------------
nosetests-2.7 -s --with-marvin --marvin-config=${MARVIN_CONFIG} --zone=${ZONE_NAME} \
--hypervisor=simulator -a tags=$MODE,required_hardware=false \
test/integration/smoke/test_accounts.py:TestAccounts
--------------------------
EOF

View File

@ -32,8 +32,10 @@ import time
import hashlib import hashlib
import base64 import base64
class Domain: class Domain:
""" Domain Life Cycle """ """ Domain Life Cycle """
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -174,6 +176,7 @@ class RolePermission:
class Account: class Account:
""" Account Life Cycle """ """ Account Life Cycle """
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -238,9 +241,19 @@ class Account:
cmd.lock = lock cmd.lock = lock
apiclient.disableAccount(cmd) apiclient.disableAccount(cmd)
def update(self, apiclient, roleid=None, newname=None, networkdomain=""):
"""Update account"""
cmd = updateAccount.updateAccountCmd()
cmd.id = self.id
cmd.networkdomain = networkdomain
cmd.newname = newname
cmd.roleid = roleid
apiclient.updateAccount(cmd)
class User: class User:
""" User Life Cycle """ """ User Life Cycle """
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -342,6 +355,7 @@ class VirtualMachine:
EXPUNGING = EXPUNGING EXPUNGING = EXPUNGING
STOPPING = STOPPING STOPPING = STOPPING
STARTING = STARTING STARTING = STARTING
# Varibles denoting VM state - end # Varibles denoting VM state - end
def __init__(self, items, services): def __init__(self, items, services):
@ -941,6 +955,7 @@ class VirtualMachine:
class Volume: class Volume:
"""Manage Volume Life cycle """Manage Volume Life cycle
""" """
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -1147,6 +1162,7 @@ class Snapshot:
# Variables denoting possible Snapshot states - start # Variables denoting possible Snapshot states - start
BACKED_UP = BACKED_UP BACKED_UP = BACKED_UP
BACKING_UP = BACKING_UP BACKING_UP = BACKING_UP
# Variables denoting possible Snapshot states - end # Variables denoting possible Snapshot states - end
def __init__(self, items): def __init__(self, items):
@ -1336,7 +1352,6 @@ class Template:
if "directdownload" in services: if "directdownload" in services:
cmd.directdownload = services["directdownload"] cmd.directdownload = services["directdownload"]
# Register Template # Register Template
template = apiclient.registerTemplate(cmd) template = apiclient.registerTemplate(cmd)
@ -1865,7 +1880,6 @@ class StaticNATRule:
class EgressFireWallRule: class EgressFireWallRule:
"""Manage Egress Firewall rule""" """Manage Egress Firewall rule"""
def __init__(self, items): def __init__(self, items):
@ -1911,7 +1925,6 @@ class EgressFireWallRule:
class FireWallRule: class FireWallRule:
"""Manage Firewall rule""" """Manage Firewall rule"""
def __init__(self, items): def __init__(self, items):
@ -1958,7 +1971,6 @@ class FireWallRule:
class Autoscale: class Autoscale:
"""Manage Auto scale""" """Manage Auto scale"""
def __init__(self, items): def __init__(self, items):
@ -2115,7 +2127,6 @@ class Autoscale:
class ServiceOffering: class ServiceOffering:
"""Manage service offerings cycle""" """Manage service offerings cycle"""
def __init__(self, items): def __init__(self, items):
@ -2321,7 +2332,6 @@ class NetworkOffering:
if "servicepackagedescription" in services: if "servicepackagedescription" in services:
cmd.details[0]["servicepackagedescription"] = services["servicepackagedescription"] cmd.details[0]["servicepackagedescription"] = services["servicepackagedescription"]
cmd.availability = 'Optional' cmd.availability = 'Optional'
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
@ -2388,8 +2398,10 @@ class SnapshotPolicy:
cmd.listall = True cmd.listall = True
return (apiclient.listSnapshotPolicies(cmd)) return (apiclient.listSnapshotPolicies(cmd))
class GuestOs: class GuestOs:
"""Guest OS calls (currently read-only implemented)""" """Guest OS calls (currently read-only implemented)"""
def __init(self, items): def __init(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -2416,6 +2428,7 @@ class GuestOs:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.listOsTypes(cmd)) return (apiclient.listOsTypes(cmd))
class Hypervisor: class Hypervisor:
"""Manage Hypervisor""" """Manage Hypervisor"""
@ -2731,7 +2744,8 @@ class Host:
retry_interval = 10 retry_interval = 10
num_tries = 10 num_tries = 10
wait_result, return_val = wait_until(retry_interval, num_tries, Host._check_resource_state, apiclient, self.id, HOST_RS_MAINTENANCE) wait_result, return_val = wait_until(retry_interval, num_tries, Host._check_resource_state, apiclient, self.id,
HOST_RS_MAINTENANCE)
if not wait_result: if not wait_result:
raise Exception(return_val) raise Exception(return_val)
@ -2812,7 +2826,8 @@ class Host:
validationresult = validateList(hosts) validationresult = validateList(hosts)
if validationresult[0] == FAIL: if validationresult[0] == FAIL:
raise Exception("Host list validation failed: %s" % validationresult[2]) raise Exception("Host list validation failed: %s" % validationresult[2])
elif str(hosts[0].state).lower().decode("string_escape") == str(state).lower() and str(hosts[0].resourcestate).lower().decode("string_escape") == str(resourcestate).lower(): elif str(hosts[0].state).lower().decode("string_escape") == str(state).lower() and str(
hosts[0].resourcestate).lower().decode("string_escape") == str(resourcestate).lower():
returnValue = [PASS, None] returnValue = [PASS, None]
break break
except Exception as e: except Exception as e:
@ -2822,6 +2837,7 @@ class Host:
timeout -= 60 timeout -= 60
return returnValue return returnValue
class StoragePool: class StoragePool:
"""Manage Storage pools (Primary Storage)""" """Manage Storage pools (Primary Storage)"""
@ -2970,6 +2986,7 @@ class StoragePool:
timeout -= 60 timeout -= 60
return returnValue return returnValue
class Network: class Network:
"""Manage Network pools""" """Manage Network pools"""
@ -3389,6 +3406,7 @@ class Zone:
cmd.listall = True cmd.listall = True
return (apiclient.listZones(cmd)) return (apiclient.listZones(cmd))
class Pod: class Pod:
"""Manage Pod""" """Manage Pod"""
@ -3433,6 +3451,7 @@ class Pod:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return apiclient.updatePod(cmd) return apiclient.updatePod(cmd)
class PublicIpRange: class PublicIpRange:
"""Manage VlanIpRange""" """Manage VlanIpRange"""
@ -3540,6 +3559,7 @@ class PortablePublicIpRange:
cmd.listall = True cmd.listall = True
return (apiclient.listPortableIpRanges(cmd)) return (apiclient.listPortableIpRanges(cmd))
class SecondaryStagingStore: class SecondaryStagingStore:
"""Manage Staging Store""" """Manage Staging Store"""
@ -4036,7 +4056,6 @@ class Configurations:
cmd.storageid = storageid cmd.storageid = storageid
apiclient.updateConfiguration(cmd) apiclient.updateConfiguration(cmd)
@classmethod @classmethod
def list(cls, apiclient, **kwargs): def list(cls, apiclient, **kwargs):
"""Lists configurations""" """Lists configurations"""
@ -4054,6 +4073,7 @@ class Configurations:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.listCapabilities(cmd)) return (apiclient.listCapabilities(cmd))
class NetScaler: class NetScaler:
"""Manage external netscaler device""" """Manage external netscaler device"""
@ -4122,6 +4142,7 @@ class NetScaler:
cmd.listall = True cmd.listall = True
return (apiclient.listNetscalerLoadBalancers(cmd)) return (apiclient.listNetscalerLoadBalancers(cmd))
class NiciraNvp: class NiciraNvp:
def __init__(self, items): def __init__(self, items):
@ -4501,7 +4522,6 @@ class VPC:
cmd.resume = resume cmd.resume = resume
return (apiclient.migrateVPC(cmd)) return (apiclient.migrateVPC(cmd))
def delete(self, apiclient): def delete(self, apiclient):
"""Delete VPC network""" """Delete VPC network"""
@ -4603,8 +4623,10 @@ class AffinityGroup:
cmd.listall = True cmd.listall = True
return apiclient.listAffinityGroups(cmd) return apiclient.listAffinityGroups(cmd)
class StaticRoute: class StaticRoute:
"""Manage static route lifecycle""" """Manage static route lifecycle"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -4637,6 +4659,7 @@ class StaticRoute:
class VNMC: class VNMC:
"""Manage VNMC lifecycle""" """Manage VNMC lifecycle"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -4832,6 +4855,7 @@ class InstanceGroup:
class ASA1000V: class ASA1000V:
"""Manage ASA 1000v lifecycle""" """Manage ASA 1000v lifecycle"""
def create(cls, apiclient, hostname, insideportprofile, def create(cls, apiclient, hostname, insideportprofile,
clusterid, physicalnetworkid): clusterid, physicalnetworkid):
"""Registers ASA 1000v appliance""" """Registers ASA 1000v appliance"""
@ -4860,8 +4884,10 @@ class ASA1000V:
cmd.listall = True cmd.listall = True
return (apiclient.listCiscoAsa1000vResources(cmd)) return (apiclient.listCiscoAsa1000vResources(cmd))
class VmSnapshot: class VmSnapshot:
"""Manage VM Snapshot life cycle""" """Manage VM Snapshot life cycle"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -4899,8 +4925,10 @@ class VmSnapshot:
cmd.vmsnapshotid = vmsnapshotid cmd.vmsnapshotid = vmsnapshotid
return apiclient.deleteVMSnapshot(cmd) return apiclient.deleteVMSnapshot(cmd)
class Region: class Region:
""" Regions related Api """ """ Regions related Api """
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -5036,6 +5064,7 @@ class ApplicationLoadBalancer:
cmd.listall = True cmd.listall = True
return (apiclient.listLoadBalancerRules(cmd)) return (apiclient.listLoadBalancerRules(cmd))
class Resources: class Resources:
"""Manage resource limits""" """Manage resource limits"""
@ -5068,8 +5097,10 @@ class Resources:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.updateResourceCount(cmd)) return (apiclient.updateResourceCount(cmd))
class NIC: class NIC:
"""NIC related API""" """NIC related API"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -5099,8 +5130,10 @@ class NIC:
cmd.listall = True cmd.listall = True
return (apiclient.listNics(cmd)) return (apiclient.listNics(cmd))
class SimulatorMock: class SimulatorMock:
"""Manage simulator mock lifecycle""" """Manage simulator mock lifecycle"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -5142,8 +5175,10 @@ class SimulatorMock:
except Exception as e: except Exception as e:
raise e raise e
class Usage: class Usage:
"""Manage Usage Generation""" """Manage Usage Generation"""
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@ -5172,6 +5207,7 @@ class Usage:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.generateUsageRecords(cmd)) return (apiclient.generateUsageRecords(cmd))
class TrafficType: class TrafficType:
"""Manage different traffic types in the setup""" """Manage different traffic types in the setup"""
@ -5186,6 +5222,7 @@ class TrafficType:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.listTrafficTypes(cmd)) return (apiclient.listTrafficTypes(cmd))
class StorageNetworkIpRange: class StorageNetworkIpRange:
"""Manage Storage Network Ip Range""" """Manage Storage Network Ip Range"""
@ -5200,6 +5237,7 @@ class StorageNetworkIpRange:
[setattr(cmd, k, v) for k, v in kwargs.items()] [setattr(cmd, k, v) for k, v in kwargs.items()]
return (apiclient.listStorageNetworkIpRange(cmd)) return (apiclient.listStorageNetworkIpRange(cmd))
class RegisteredServicePackage: class RegisteredServicePackage:
"""Manage ServicePackage registered with NCC""" """Manage ServicePackage registered with NCC"""