mirror of https://github.com/apache/cloudstack.git
Merge branch 'main' into cks-enhancements-upstream
This commit is contained in:
commit
a9bc998d1a
|
|
@ -59,6 +59,7 @@ github:
|
|||
- nicoschmdt
|
||||
- abh1sar
|
||||
- sudo87
|
||||
- rosi-shapeblue
|
||||
|
||||
protected_branches: ~
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ jobs:
|
|||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid
|
||||
python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid pynose
|
||||
|
||||
- name: Install jacoco dependencies
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package com.cloud.storage;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
|
|
@ -25,6 +26,8 @@ public interface VMTemplateStorageResourceAssoc extends InternalIdentity {
|
|||
UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
|
||||
}
|
||||
|
||||
List<Status> PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
|
||||
|
||||
String getInstallPath();
|
||||
|
||||
long getTemplateId();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.cloudstack.api.command.user.iso;
|
||||
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
|
|
@ -101,7 +102,15 @@ public class ExtractIsoCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "extracting ISO: " + getId() + " from zone: " + getZoneId();
|
||||
String isoId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId());
|
||||
String baseDescription = String.format("Extracting ISO: %s", isoId);
|
||||
|
||||
Long zoneId = getZoneId();
|
||||
if (zoneId == null) {
|
||||
return baseDescription;
|
||||
}
|
||||
|
||||
return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -101,7 +101,15 @@ public class ExtractTemplateCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "extracting template: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()) + ((getZoneId() != null) ? " from zone: " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()) : "");
|
||||
String templateId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId());
|
||||
String baseDescription = String.format("Extracting template: %s", templateId);
|
||||
|
||||
Long zoneId = getZoneId();
|
||||
if (zoneId == null) {
|
||||
return baseDescription;
|
||||
}
|
||||
|
||||
return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.cloudstack.api.command.user.volume;
|
||||
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
|
|
@ -114,12 +115,15 @@ public class ExtractVolumeCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Extraction job";
|
||||
String volumeId = this._uuidMgr.getUuid(Volume.class, getId());
|
||||
String zoneId = this._uuidMgr.getUuid(DataCenter.class, getZoneId());
|
||||
|
||||
return String.format("Extracting volume: %s from zone: %s", volumeId, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()));
|
||||
CallContext.current().setEventDetails(getEventDescription());
|
||||
String uploadUrl = _volumeService.extractVolume(this);
|
||||
if (uploadUrl != null) {
|
||||
ExtractResponse response = _responseGenerator.createVolumeExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class PurgeExpungedResourcesCmdTest {
|
|||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
PurgeExpungedResourcesCmd spyCmd = Mockito.spy(new PurgeExpungedResourcesCmd());
|
||||
PurgeExpungedResourcesCmd spyCmd;
|
||||
|
||||
@Test
|
||||
public void testGetResourceType() {
|
||||
|
|
|
|||
|
|
@ -20,14 +20,11 @@ package org.apache.cloudstack.api.command.admin.storage;
|
|||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.api.response.ExtractResponse;
|
||||
import org.apache.cloudstack.storage.browser.StorageBrowser;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
|
@ -48,18 +45,6 @@ public class DownloadImageStoreObjectCmdTest {
|
|||
@Spy
|
||||
private DownloadImageStoreObjectCmd cmd;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
ReflectionTestUtils.setField(cmd, "storeId", 1L);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ public class MigrateVirtualMachineWithVolumeCmdTest {
|
|||
@Mock
|
||||
Host hostMock;
|
||||
|
||||
@Mock
|
||||
private Object job;
|
||||
|
||||
@Mock
|
||||
private Object _responseObject;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
MigrateVirtualMachineWithVolumeCmd cmdSpy;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package org.apache.cloudstack.api.command.admin.vpc;
|
|||
import com.cloud.network.vpc.VpcService;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
|
@ -34,7 +33,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||
import java.util.List;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CreateVPCCmdByAdminTest extends TestCase {
|
||||
public class CreateVPCCmdByAdminTest {
|
||||
|
||||
@Mock
|
||||
public VpcService _vpcService;
|
||||
|
|
@ -43,8 +42,10 @@ public class CreateVPCCmdByAdminTest extends TestCase {
|
|||
@Mock
|
||||
public AccountService _accountService;
|
||||
private ResponseGenerator responseGenerator;
|
||||
@Mock
|
||||
public Object job;
|
||||
@InjectMocks
|
||||
CreateVPCCmdByAdmin cmd = new CreateVPCCmdByAdmin();
|
||||
CreateVPCCmdByAdmin cmd;
|
||||
|
||||
@Test
|
||||
public void testBgpPeerIds() {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,10 @@ public class UpdateNetworkCmdTest {
|
|||
NetworkService networkService;
|
||||
@Mock
|
||||
public EntityManager _entityMgr;
|
||||
private ResponseGenerator responseGenerator;
|
||||
@Mock
|
||||
private ResponseGenerator _responseGenerator;
|
||||
@Mock
|
||||
private Object job;
|
||||
@InjectMocks
|
||||
UpdateNetworkCmd cmd = new UpdateNetworkCmd();
|
||||
|
||||
|
|
@ -176,15 +179,13 @@ public class UpdateNetworkCmdTest {
|
|||
ReflectionTestUtils.setField(cmd, "id", networkId);
|
||||
ReflectionTestUtils.setField(cmd, "publicMtu", publicmtu);
|
||||
Network network = Mockito.mock(Network.class);
|
||||
responseGenerator = Mockito.mock(ResponseGenerator.class);
|
||||
NetworkResponse response = Mockito.mock(NetworkResponse.class);
|
||||
response.setPublicMtu(publicmtu);
|
||||
Mockito.when(networkService.getNetwork(networkId)).thenReturn(network);
|
||||
Mockito.when(networkService.updateGuestNetwork(cmd)).thenReturn(network);
|
||||
cmd._responseGenerator = responseGenerator;
|
||||
Mockito.when(responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response);
|
||||
Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response);
|
||||
cmd.execute();
|
||||
Mockito.verify(responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class));
|
||||
Mockito.verify(_responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class));
|
||||
NetworkResponse actualResponse = (NetworkResponse) cmd.getResponseObject();
|
||||
|
||||
Assert.assertEquals(response, actualResponse);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import com.cloud.network.vpc.Vpc;
|
|||
import com.cloud.network.vpc.VpcService;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.response.VpcResponse;
|
||||
|
|
@ -39,7 +38,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CreateVPCCmdTest extends TestCase {
|
||||
public class CreateVPCCmdTest {
|
||||
|
||||
@Mock
|
||||
public VpcService _vpcService;
|
||||
|
|
@ -47,9 +46,13 @@ public class CreateVPCCmdTest extends TestCase {
|
|||
public EntityManager _entityMgr;
|
||||
@Mock
|
||||
public AccountService _accountService;
|
||||
private ResponseGenerator responseGenerator;
|
||||
@Mock
|
||||
private ResponseGenerator _responseGenerator;
|
||||
@Mock
|
||||
private Object job;
|
||||
|
||||
@InjectMocks
|
||||
CreateVPCCmd cmd = new CreateVPCCmd();
|
||||
CreateVPCCmd cmd;
|
||||
|
||||
@Test
|
||||
public void testGetAccountName() {
|
||||
|
|
@ -185,11 +188,9 @@ public class CreateVPCCmdTest extends TestCase {
|
|||
VpcResponse response = Mockito.mock(VpcResponse.class);
|
||||
|
||||
ReflectionTestUtils.setField(cmd, "id", 1L);
|
||||
responseGenerator = Mockito.mock(ResponseGenerator.class);
|
||||
Mockito.doNothing().when(_vpcService).startVpc(cmd);
|
||||
Mockito.when(_entityMgr.findById(Mockito.eq(Vpc.class), Mockito.any(Long.class))).thenReturn(vpc);
|
||||
cmd._responseGenerator = responseGenerator;
|
||||
Mockito.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response);
|
||||
Mockito.when(_responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response);
|
||||
cmd.execute();
|
||||
Mockito.verify(_vpcService, Mockito.times(1)).startVpc(cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5240,10 +5240,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
workJob = newVmWorkJobAndInfo.first();
|
||||
VmWorkMigrateAway workInfo = new VmWorkMigrateAway(newVmWorkJobAndInfo.second(), srcHostId);
|
||||
|
||||
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
|
||||
setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId);
|
||||
}
|
||||
|
||||
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vmId);
|
||||
|
||||
AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
|
||||
|
||||
|
|
|
|||
|
|
@ -208,9 +208,7 @@ public class DataMigrationUtility {
|
|||
List<TemplateInfo> files = new LinkedList<>();
|
||||
for (TemplateDataStoreVO template : templates) {
|
||||
VMTemplateVO templateVO = templateDao.findById(template.getTemplateId());
|
||||
if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null &&
|
||||
(!templateVO.isPublicTemplate() || (templateVO.isPublicTemplate() && templateVO.getUrl() == null)) &&
|
||||
templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator && templateVO.getParentTemplateId() == null) {
|
||||
if (shouldMigrateTemplate(template, templateVO)) {
|
||||
files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore));
|
||||
}
|
||||
}
|
||||
|
|
@ -231,6 +229,34 @@ public class DataMigrationUtility {
|
|||
return getAllReadyTemplates(srcDataStore, childTemplates, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a template should be migrated. A template should be migrated if:
|
||||
* <ol>
|
||||
* <li>its state is ready, and</li>
|
||||
* <li>its hypervisor type is not simulator, and</li>
|
||||
* <li>it is not a child template.</li>
|
||||
* </ol>
|
||||
*/
|
||||
protected boolean shouldMigrateTemplate(TemplateDataStoreVO template, VMTemplateVO templateVO) {
|
||||
if (template.getState() != State.Ready) {
|
||||
logger.debug("Template [{}] should not be migrated as it is not ready.", template);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (templateVO.getHypervisorType() == Hypervisor.HypervisorType.Simulator) {
|
||||
logger.debug("Template [{}] should not be migrated as its hypervisor type is simulator.", template);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (templateVO.getParentTemplateId() != null) {
|
||||
logger.debug("Template [{}] should not be migrated as it has a parent template.", template);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug("Template [{}] should be migrated.", template);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns parent snapshots and snapshots that do not have any children; snapshotChains comprises of the snapshot chain info
|
||||
* for each parent snapshot and the cumulative size of the chain - this is done to ensure that all the snapshots in a chain
|
||||
* are migrated to the same datastore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.engine.orchestration;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DataMigrationUtilityTest extends TestCase {
|
||||
|
||||
@Spy
|
||||
private DataMigrationUtility dataMigrationUtility;
|
||||
|
||||
@Mock
|
||||
private VMTemplateVO templateVoMock;
|
||||
|
||||
@Mock
|
||||
private TemplateDataStoreVO templateDataStoreVoMock;
|
||||
|
||||
private void prepareForShouldMigrateTemplateTests() {
|
||||
Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);
|
||||
Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
|
||||
Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMigrateTemplateTestReturnsFalseWhenTemplateIsNotReady() {
|
||||
prepareForShouldMigrateTemplateTests();
|
||||
Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Migrating);
|
||||
|
||||
boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMigrateTemplateTestReturnsFalseWhenHypervisorTypeIsSimulator() {
|
||||
prepareForShouldMigrateTemplateTests();
|
||||
Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.Simulator);
|
||||
|
||||
boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMigrateTemplateTestReturnsFalseWhenTemplateHasParentTemplate() {
|
||||
prepareForShouldMigrateTemplateTests();
|
||||
Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(1L);
|
||||
|
||||
boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMigrateTemplateTestReturnsTrueWhenTemplateIsReadyAndDoesNotHaveParentTemplateAndHypervisorTypeIsNotSimulator() {
|
||||
prepareForShouldMigrateTemplateTests();
|
||||
|
||||
boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -503,7 +503,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("VM instance %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceName", "uuid", "type"));
|
||||
return String.format("VM instance %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceName", "uuid", "type", "state"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -47,3 +47,25 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.storage_pool', 'used_iops', 'bigint
|
|||
|
||||
-- Add reason column for op_ha_work
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.op_ha_work', 'reason', 'varchar(32) DEFAULT NULL COMMENT "Reason for the HA work"');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Read-Only User - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Support User - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Read-Only Admin - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Support Admin - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ import java.util.concurrent.ExecutionException;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.download.DownloadListener;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
|
|
@ -118,26 +121,21 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService {
|
|||
}
|
||||
} else if (srcDataObject instanceof TemplateInfo && templateChain != null && templateChain.containsKey(srcDataObject)) {
|
||||
for (TemplateInfo templateInfo : templateChain.get(srcDataObject).first()) {
|
||||
if (templateIsOnDestination(templateInfo, destDatastore)) {
|
||||
res.setResult("Template already exists on destination.");
|
||||
res.setSuccess(true);
|
||||
logger.debug("Deleting template {} from source data store [{}].", srcDataObject.getTO().toString(),
|
||||
srcDataObject.getDataStore().getTO().toString());
|
||||
srcDataObject.getDataStore().delete(srcDataObject);
|
||||
future.complete(res);
|
||||
continue;
|
||||
}
|
||||
destDataObject = destDatastore.create(templateInfo);
|
||||
templateInfo.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested);
|
||||
destDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested);
|
||||
migrateJob(future, templateInfo, destDataObject, destDatastore);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check if template in destination store, if yes, do not proceed
|
||||
if (srcDataObject instanceof TemplateInfo) {
|
||||
logger.debug("Checking if template present at destination");
|
||||
TemplateDataStoreVO templateStoreVO = templateStoreDao.findByStoreTemplate(destDatastore.getId(), srcDataObject.getId());
|
||||
if (templateStoreVO != null) {
|
||||
String msg = "Template already exists in destination store";
|
||||
logger.debug(msg);
|
||||
res.setResult(msg);
|
||||
res.setSuccess(true);
|
||||
future.complete(res);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
destDataObject = destDatastore.create(srcDataObject);
|
||||
srcDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested);
|
||||
destDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested);
|
||||
|
|
@ -160,6 +158,69 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService {
|
|||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether a template is ready on the provided data store. If the template is being downloaded,
|
||||
* waits until the download finishes.
|
||||
* @param srcDataObject the template.
|
||||
* @param destDatastore the data store.
|
||||
*/
|
||||
protected boolean templateIsOnDestination(DataObject srcDataObject, DataStore destDatastore) {
|
||||
if (!(srcDataObject instanceof TemplateInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String templateAsString = srcDataObject.getTO().toString();
|
||||
String destDatastoreAsString = destDatastore.getTO().toString();
|
||||
TemplateDataStoreVO templateStoreVO;
|
||||
|
||||
long timer = getTemplateDownloadTimeout();
|
||||
long msToSleep = 10000L;
|
||||
int previousDownloadPercentage = -1;
|
||||
|
||||
while (true) {
|
||||
templateStoreVO = templateStoreDao.findByStoreTemplate(destDatastore.getId(), srcDataObject.getId());
|
||||
if (templateStoreVO == null) {
|
||||
logger.debug("{} is not present at destination [{}].", templateAsString, destDatastoreAsString);
|
||||
return false;
|
||||
}
|
||||
VMTemplateStorageResourceAssoc.Status downloadState = templateStoreVO.getDownloadState();
|
||||
if (downloadState == null || !VMTemplateStorageResourceAssoc.PENDING_DOWNLOAD_STATES.contains(downloadState)) {
|
||||
break;
|
||||
}
|
||||
if (previousDownloadPercentage == templateStoreVO.getDownloadPercent()) {
|
||||
timer -= msToSleep;
|
||||
} else {
|
||||
timer = getTemplateDownloadTimeout();
|
||||
}
|
||||
if (timer <= 0) {
|
||||
throw new CloudRuntimeException(String.format("Timeout while waiting for %s to be downloaded to image store [%s]. " +
|
||||
"The download percentage has not changed for %d milliseconds.", templateAsString, destDatastoreAsString, getTemplateDownloadTimeout()));
|
||||
}
|
||||
waitForTemplateDownload(msToSleep, templateAsString, destDatastoreAsString);
|
||||
}
|
||||
|
||||
if (templateStoreVO.getState() == ObjectInDataStoreStateMachine.State.Ready) {
|
||||
logger.debug("{} already exists on destination [{}].", templateAsString, destDatastoreAsString);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected long getTemplateDownloadTimeout() {
|
||||
return DownloadListener.DOWNLOAD_TIMEOUT;
|
||||
}
|
||||
|
||||
protected void waitForTemplateDownload(long msToSleep, String templateAsString, String destDatastoreAsString) {
|
||||
logger.debug("{} is being downloaded to destination [{}]; we will verify in {} milliseconds if the download has finished.",
|
||||
templateAsString, destDatastoreAsString, msToSleep);
|
||||
try {
|
||||
Thread.sleep(msToSleep);
|
||||
} catch (InterruptedException e) {
|
||||
logger.warn("[ignored] interrupted while waiting for template {} download to finish before trying to migrate it to data store [{}].",
|
||||
templateAsString, destDatastoreAsString);
|
||||
}
|
||||
}
|
||||
|
||||
protected void migrateJob(AsyncCallFuture<DataObjectResult> future, DataObject srcDataObject, DataObject destDataObject, DataStore destDatastore) throws ExecutionException, InterruptedException {
|
||||
MigrateDataContext<DataObjectResult> context = new MigrateDataContext<DataObjectResult>(null, future, srcDataObject, destDataObject, destDatastore);
|
||||
AsyncCallbackDispatcher<SecondaryStorageServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.storage.image;
|
||||
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SecondaryStorageServiceImplTest extends TestCase {
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private SecondaryStorageServiceImpl secondaryStorageService;
|
||||
|
||||
@Mock
|
||||
TemplateDataStoreDao templateDataStoreDaoMock;
|
||||
|
||||
@Mock
|
||||
TemplateDataStoreVO templateDataStoreVoMock;
|
||||
|
||||
@Mock
|
||||
TemplateInfo templateInfoMock;
|
||||
|
||||
@Mock
|
||||
TemplateObjectTO templateObjectToMock;
|
||||
|
||||
@Mock
|
||||
DataStore dataStoreMock;
|
||||
|
||||
@Mock
|
||||
DataStoreTO dataStoreToMock;
|
||||
|
||||
private void prepareForTemplateIsOnDestinationTests() {
|
||||
long dataStoreId = 1;
|
||||
long templateId = 2;
|
||||
|
||||
Mockito.when(dataStoreMock.getId()).thenReturn(dataStoreId);
|
||||
Mockito.when(dataStoreMock.getTO()).thenReturn(dataStoreToMock);
|
||||
Mockito.when(templateInfoMock.getId()).thenReturn(templateId);
|
||||
Mockito.when(templateInfoMock.getTO()).thenReturn(templateObjectToMock);
|
||||
Mockito.doReturn(templateDataStoreVoMock).when(templateDataStoreDaoMock).findByStoreTemplate(dataStoreId, templateId);
|
||||
Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateIsOnDestinationTestReturnsFalseWhenTemplateStoreRefDoesNotExist() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
Mockito.doReturn(null).when(templateDataStoreDaoMock).findByStoreTemplate(Mockito.anyLong(), Mockito.anyLong());
|
||||
|
||||
boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateIsOnDestinationTestReturnsTrueWhenTemplateIsReady() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
|
||||
boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateIsOnDestinationTestReturnsFalseWhenTemplateIsNotReady() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Creating);
|
||||
|
||||
boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateIsOnDestinationTestReturnsTrueIfTemplateIsDownloadedSuccessfully() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS);
|
||||
Mockito.doAnswer(I -> Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOADED)).when(secondaryStorageService).waitForTemplateDownload(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
|
||||
boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateIsOnDestinationTestReturnsFalseIfTemplateIsNotDownloadedSuccessfully() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS);
|
||||
Mockito.doAnswer(I -> {
|
||||
Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
|
||||
Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Failed);
|
||||
return "mocked download fail";
|
||||
}).when(secondaryStorageService).waitForTemplateDownload(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
|
||||
boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void templateIsOnDestinationTestThrowsExceptionIfDownloadTimesOut() {
|
||||
prepareForTemplateIsOnDestinationTests();
|
||||
Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS);
|
||||
Mockito.doReturn(0L).when(secondaryStorageService).getTemplateDownloadTimeout();
|
||||
|
||||
secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock);
|
||||
}
|
||||
}
|
||||
|
|
@ -73,8 +73,8 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
private static final int EXECUTOR_SHUTDOWN_TIMEOUT = 1000; // 1 second
|
||||
private static final int DEFAULT_OUTGOING_WORKERS = 5;
|
||||
|
||||
private final List<ClusterManagerListener> _listeners = new ArrayList<ClusterManagerListener>();
|
||||
private final Map<Long, ManagementServerHostVO> _activePeers = new HashMap<Long, ManagementServerHostVO>();
|
||||
private final List<ClusterManagerListener> _listeners = new ArrayList<>();
|
||||
private final Map<Long, ManagementServerHostVO> _activePeers = new HashMap<>();
|
||||
|
||||
private final Map<String, ClusterService> _clusterPeers;
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
|
||||
private final ScheduledExecutorService _heartbeatScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Cluster-Heartbeat"));
|
||||
private final ExecutorService _notificationExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("Cluster-Notification"));
|
||||
private final List<ClusterManagerMessage> _notificationMsgs = new ArrayList<ClusterManagerMessage>();
|
||||
private final List<ClusterManagerMessage> _notificationMsgs = new ArrayList<>();
|
||||
private ConnectionConcierge _heartbeatConnection = null;
|
||||
|
||||
private final ExecutorService _executor;
|
||||
|
|
@ -118,12 +118,12 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
|
||||
private String _clusterNodeIP = "127.0.0.1";
|
||||
|
||||
private final List<ClusterServicePdu> _clusterPduOutgoingQueue = new ArrayList<ClusterServicePdu>();
|
||||
private final List<ClusterServicePdu> _clusterPduIncomingQueue = new ArrayList<ClusterServicePdu>();
|
||||
private final Map<Long, ClusterServiceRequestPdu> _outgoingPdusWaitingForAck = new HashMap<Long, ClusterServiceRequestPdu>();
|
||||
private final List<ClusterServicePdu> _clusterPduOutgoingQueue = new ArrayList<>();
|
||||
private final List<ClusterServicePdu> _clusterPduIncomingQueue = new ArrayList<>();
|
||||
private final Map<Long, ClusterServiceRequestPdu> _outgoingPdusWaitingForAck = new HashMap<>();
|
||||
|
||||
public ClusterManagerImpl() {
|
||||
_clusterPeers = new HashMap<String, ClusterService>();
|
||||
_clusterPeers = new HashMap<>();
|
||||
|
||||
// executor to perform remote-calls in another thread context, to avoid potential
|
||||
// recursive remote calls between nodes
|
||||
|
|
@ -161,7 +161,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
}
|
||||
|
||||
private void cancelClusterRequestToPeer(final String strPeer) {
|
||||
final List<ClusterServiceRequestPdu> candidates = new ArrayList<ClusterServiceRequestPdu>();
|
||||
final List<ClusterServiceRequestPdu> candidates = new ArrayList<>();
|
||||
synchronized (_outgoingPdusWaitingForAck) {
|
||||
for (final Map.Entry<Long, ClusterServiceRequestPdu> entry : _outgoingPdusWaitingForAck.entrySet()) {
|
||||
if (entry.getValue().getDestPeer().equalsIgnoreCase(strPeer)) {
|
||||
|
|
@ -193,7 +193,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
synchronized (_clusterPduOutgoingQueue) {
|
||||
try {
|
||||
_clusterPduOutgoingQueue.wait(timeoutMs);
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
|
||||
if (_clusterPduOutgoingQueue.size() > 0) {
|
||||
|
|
@ -216,7 +216,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
synchronized (_clusterPduIncomingQueue) {
|
||||
try {
|
||||
_clusterPduIncomingQueue.wait(timeoutMs);
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
|
||||
if (_clusterPduIncomingQueue.size() > 0) {
|
||||
|
|
@ -449,7 +449,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
synchronized (pdu) {
|
||||
try {
|
||||
pdu.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -620,8 +620,8 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
|
||||
if (profiler.getDurationInMillis() >= HeartbeatInterval.value()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Management server heartbeat takes too long to finish. profiler: " + profiler.toString() + ", profilerHeartbeatUpdate: " +
|
||||
profilerHeartbeatUpdate.toString() + ", profilerPeerScan: " + profilerPeerScan.toString());
|
||||
logger.debug("Management server heartbeat takes too long to finish. profiler: " + profiler + ", profilerHeartbeatUpdate: " +
|
||||
profilerHeartbeatUpdate + ", profilerPeerScan: " + profilerPeerScan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -685,7 +685,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
synchronized (_notificationMsgs) {
|
||||
try {
|
||||
_notificationMsgs.wait(1000);
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -745,7 +745,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -816,7 +816,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
}
|
||||
}
|
||||
|
||||
final List<ManagementServerHostVO> downHostList = new ArrayList<ManagementServerHostVO>();
|
||||
final List<ManagementServerHostVO> downHostList = new ArrayList<>();
|
||||
for (final ManagementServerHostVO host : inactiveList) {
|
||||
// Check if peer state is Up in the period
|
||||
if (!_mshostPeerDao.isPeerUpState(_mshostId, host.getId(), new Date(cutTime.getTime() - HeartbeatThreshold.value()))) {
|
||||
|
|
@ -846,8 +846,8 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
|
||||
final Profiler profilerSyncClusterInfo = new Profiler();
|
||||
profilerSyncClusterInfo.start();
|
||||
final List<ManagementServerHostVO> removedNodeList = new ArrayList<ManagementServerHostVO>();
|
||||
final List<ManagementServerHostVO> invalidatedNodeList = new ArrayList<ManagementServerHostVO>();
|
||||
final List<ManagementServerHostVO> removedNodeList = new ArrayList<>();
|
||||
final List<ManagementServerHostVO> invalidatedNodeList = new ArrayList<>();
|
||||
|
||||
if (_mshostId != null) {
|
||||
|
||||
|
|
@ -941,7 +941,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
try {
|
||||
JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId());
|
||||
} catch (final Exception e) {
|
||||
logger.warn("Unable to deregister cluster node from JMX monitoring due to exception " + e.toString());
|
||||
logger.warn("Unable to deregister cluster node from JMX monitoring due to exception " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -961,7 +961,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
try {
|
||||
JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId());
|
||||
} catch (final Exception e) {
|
||||
logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString());
|
||||
logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e);
|
||||
}
|
||||
} else {
|
||||
logger.info("Management node {} is detected inactive by timestamp but sent node status to this node", mshost);
|
||||
|
|
@ -975,7 +975,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
}
|
||||
|
||||
private void processNewNodes(Date cutTime, List<ManagementServerHostVO> currentList) {
|
||||
final List<ManagementServerHostVO> newNodeList = new ArrayList<ManagementServerHostVO>();
|
||||
final List<ManagementServerHostVO> newNodeList = new ArrayList<>();
|
||||
for (final ManagementServerHostVO mshost : currentList) {
|
||||
if (!_activePeers.containsKey(mshost.getId())) {
|
||||
_activePeers.put(mshost.getId(), mshost);
|
||||
|
|
@ -1037,7 +1037,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
logger.info("Starting Cluster manager, msid: {}, mshost: {}", _msId, _mshost);
|
||||
}
|
||||
|
||||
final ManagementServerHostVO mshost = Transaction.execute(new TransactionCallback<ManagementServerHostVO>() {
|
||||
final ManagementServerHostVO mshost = Transaction.execute(new TransactionCallback<>() {
|
||||
@Override
|
||||
public ManagementServerHostVO doInTransaction(final TransactionStatus status) {
|
||||
|
||||
|
|
@ -1105,11 +1105,13 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
}
|
||||
|
||||
if (_mshostId != null) {
|
||||
final ManagementServerHostVO mshost = _mshostDao.findByMsid(_msId);
|
||||
ManagementServerHostVO mshost = _mshostDao.findByMsid(_msId);
|
||||
if (mshost != null) {
|
||||
ManagementServerStatusVO mshostStatus = mshostStatusDao.findByMsId(mshost.getUuid());
|
||||
if (mshostStatus != null) {
|
||||
mshost.setState(ManagementServerHost.State.Down);
|
||||
mshostStatus.setLastJvmStop(new Date());
|
||||
_mshostDao.update(_mshostId, mshost);
|
||||
mshostStatusDao.update(mshostStatus.getId(), mshostStatus);
|
||||
} else {
|
||||
logger.warn("Found a management server host [{}] without a status. This should never happen!", mshost);
|
||||
|
|
@ -1135,7 +1137,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
try {
|
||||
_heartbeatScheduler.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
_executor.awaitTermination(EXECUTOR_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -1271,14 +1273,14 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
|
|||
if (sch != null) {
|
||||
try {
|
||||
sch.close();
|
||||
} catch (final IOException e) {
|
||||
} catch (final IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (final InterruptedException ex) {
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,15 +94,17 @@ public class RandomAllocator extends AdapterBase implements HostAllocator {
|
|||
return suitableHosts;
|
||||
}
|
||||
String offeringHostTag = offering.getHostTag();
|
||||
|
||||
VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
|
||||
String templateTag = template.getTemplateTag();
|
||||
String hostTag = null;
|
||||
if (ObjectUtils.anyNull(offeringHostTag, templateTag)) {
|
||||
hostTag = offeringHostTag;
|
||||
hostTag = hostTag == null ? templateTag : String.format("%s, %s", hostTag, templateTag);
|
||||
logger.debug(String.format("Looking for hosts in dc [%s], pod [%s], cluster [%s] and complying with host tag(s): [%s]", dcId, podId, clusterId, hostTag));
|
||||
if (ObjectUtils.anyNotNull(offeringHostTag, templateTag)) {
|
||||
hostTag = ObjectUtils.allNotNull(offeringHostTag, templateTag) ?
|
||||
String.format("%s, %s", offeringHostTag, templateTag) :
|
||||
ObjectUtils.firstNonNull(offeringHostTag, templateTag);
|
||||
logger.debug("Looking for hosts in dc [{}], pod [{}], cluster [{}] and complying with host tag(s): [{}]", dcId, podId, clusterId, hostTag);
|
||||
} else {
|
||||
logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId);
|
||||
logger.debug("Looking for hosts in dc: {} pod: {} cluster: {}", dcId , podId, clusterId);
|
||||
}
|
||||
if (hosts != null) {
|
||||
// retain all computing hosts, regardless of whether they support routing...it's random after all
|
||||
|
|
|
|||
|
|
@ -704,7 +704,7 @@ public class LibvirtMigrateCommandWrapperTest {
|
|||
|
||||
@Test
|
||||
public void deleteOrDisconnectDisksOnSourcePoolTest() {
|
||||
LibvirtMigrateCommandWrapper spyLibvirtMigrateCmdWrapper = Mockito.spy(libvirtMigrateCmdWrapper);
|
||||
LibvirtMigrateCommandWrapper spyLibvirtMigrateCmdWrapper = libvirtMigrateCmdWrapper;
|
||||
Mockito.doNothing().when(spyLibvirtMigrateCmdWrapper).deleteLocalVolume("volPath");
|
||||
|
||||
List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
|
|
@ -42,7 +43,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LibvirtRevertSnapshotCommandWrapperTest {
|
||||
|
||||
LibvirtRevertSnapshotCommandWrapper libvirtRevertSnapshotCommandWrapperSpy = Mockito.spy(LibvirtRevertSnapshotCommandWrapper.class);
|
||||
@Spy
|
||||
LibvirtRevertSnapshotCommandWrapper libvirtRevertSnapshotCommandWrapperSpy;
|
||||
|
||||
@Mock
|
||||
KVMStoragePool kvmStoragePoolPrimaryMock;
|
||||
|
|
@ -107,7 +109,7 @@ public class LibvirtRevertSnapshotCommandWrapperTest {
|
|||
Mockito.doReturn(snapshotPath).when(snapshotObjectToPrimaryMock).getPath();
|
||||
|
||||
try (MockedStatic<Files> ignored = Mockito.mockStatic(Files.class)) {
|
||||
Mockito.when(Files.exists(Mockito.any(Path.class), Mockito.any())).thenReturn(true);
|
||||
Mockito.when(Files.exists(Mockito.any(Path.class))).thenReturn(true);
|
||||
|
||||
Pair<String, SnapshotObjectTO> result = libvirtRevertSnapshotCommandWrapperSpy.getSnapshot(
|
||||
snapshotObjectToPrimaryMock, snapshotObjectToSecondaryMock,
|
||||
|
|
|
|||
|
|
@ -5104,9 +5104,9 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
|||
answer.setVolumeChainInfo(chainInfo);
|
||||
return answer;
|
||||
} catch (Exception e) {
|
||||
String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
|
||||
String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.getMessage();
|
||||
logger.error(msg, e);
|
||||
return new MigrateVolumeAnswer(cmd, false, msg, null);
|
||||
return new MigrateVolumeAnswer(cmd, false, e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
} else {
|
||||
answer = agentMgr.sendTo(sourcePool.getDataCenterId(), HypervisorType.VMware, cmd);
|
||||
}
|
||||
updateVolumeAfterMigration(answer, srcData, destData);
|
||||
handleAnswerAndUpdateVolumeAfterMigration(answer, srcData, destData);
|
||||
CopyCommandResult result = new CopyCommandResult(null, answer);
|
||||
callback.complete(result);
|
||||
}
|
||||
|
|
@ -286,21 +286,19 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
return hostId;
|
||||
}
|
||||
|
||||
private void updateVolumeAfterMigration(Answer answer, DataObject srcData, DataObject destData) {
|
||||
private void handleAnswerAndUpdateVolumeAfterMigration(Answer answer, DataObject srcData, DataObject destData) {
|
||||
VolumeVO destinationVO = volDao.findById(destData.getId());
|
||||
if (!(answer instanceof MigrateVolumeAnswer)) {
|
||||
// OfflineVmwareMigration: reset states and such
|
||||
VolumeVO sourceVO = volDao.findById(srcData.getId());
|
||||
sourceVO.setState(Volume.State.Ready);
|
||||
volDao.update(sourceVO.getId(), sourceVO);
|
||||
if (destinationVO.getId() != sourceVO.getId()) {
|
||||
destinationVO.setState(Volume.State.Expunged);
|
||||
destinationVO.setRemoved(new Date());
|
||||
volDao.update(destinationVO.getId(), destinationVO);
|
||||
}
|
||||
resetVolumeState(srcData, destinationVO);
|
||||
throw new CloudRuntimeException("unexpected answer from hypervisor agent: " + answer.getDetails());
|
||||
}
|
||||
MigrateVolumeAnswer ans = (MigrateVolumeAnswer) answer;
|
||||
if (!answer.getResult()) {
|
||||
String msg = "Unable to migrate volume: " + srcData.getName() + " due to " + answer.getDetails();
|
||||
resetVolumeState(srcData, destinationVO);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
String format = "retrieved '%s' as new path for volume(%d)";
|
||||
logger.debug(String.format(format, ans.getVolumePath(), destData.getId()));
|
||||
|
|
@ -311,6 +309,17 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
|||
volDao.update(destinationVO.getId(), destinationVO);
|
||||
}
|
||||
|
||||
private void resetVolumeState(DataObject srcData, VolumeVO destinationVO) {
|
||||
VolumeVO sourceVO = volDao.findById(srcData.getId());
|
||||
sourceVO.setState(Volume.State.Ready);
|
||||
volDao.update(sourceVO.getId(), sourceVO);
|
||||
if (destinationVO.getId() != sourceVO.getId()) {
|
||||
destinationVO.setState(Volume.State.Expunged);
|
||||
destinationVO.setRemoved(new Date());
|
||||
volDao.update(destinationVO.getId(), destinationVO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
Answer answer = null;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ public class VmwareManagerImplTest {
|
|||
private Map<String, String> clusterDetails;
|
||||
@Mock
|
||||
private Map<String, String> hostDetails;
|
||||
@Mock
|
||||
private Map<String, Object> _configParams;
|
||||
|
||||
@Before
|
||||
public void beforeTest() {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.ScaleVmAnswer;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
|
||||
|
|
@ -52,7 +54,6 @@ import org.apache.cloudstack.storage.command.CopyCommand;
|
|||
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer;
|
||||
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -62,17 +63,14 @@ import org.mockito.Mock;
|
|||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.CheckGuestOsMappingAnswer;
|
||||
import com.cloud.agent.api.CheckGuestOsMappingCommand;
|
||||
import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
|
||||
import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
|
||||
import com.cloud.agent.api.ScaleVmAnswer;
|
||||
import com.cloud.agent.api.ScaleVmCommand;
|
||||
import com.cloud.agent.api.routing.GetAutoScaleMetricsAnswer;
|
||||
import com.cloud.agent.api.routing.GetAutoScaleMetricsCommand;
|
||||
|
|
@ -185,6 +183,8 @@ public class VmwareResourceTest {
|
|||
VimPortType vimService;
|
||||
@Mock
|
||||
HostCapability hostCapability;
|
||||
@Mock
|
||||
ManagedObjectReference _morHyperHost;
|
||||
|
||||
CopyCommand storageCmd;
|
||||
EnumMap<VmwareStorageProcessorConfigurableFields, Object> params = new EnumMap<VmwareStorageProcessorConfigurableFields,Object>(VmwareStorageProcessorConfigurableFields.class);
|
||||
|
|
@ -201,11 +201,9 @@ public class VmwareResourceTest {
|
|||
|
||||
private Map<String,String> specsArray = new HashMap<String,String>();
|
||||
|
||||
AutoCloseable closeable;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
storageCmd = mock(CopyCommand.class);
|
||||
doReturn(context).when(_resource).getServiceContext(null);
|
||||
when(cmd.getVirtualMachine()).thenReturn(vmSpec);
|
||||
|
|
@ -234,11 +232,6 @@ public class VmwareResourceTest {
|
|||
when(hostCapability.isNestedHVSupported()).thenReturn(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
//Test successful scaling up the vm
|
||||
@Test
|
||||
public void testScaleVMF1() throws Exception {
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ public class ManagementServerMaintenanceManagerImplTest {
|
|||
ManagementServerHostVO msHost2 = mock(ManagementServerHostVO.class);
|
||||
List<ManagementServerHostVO> msHostList = new ArrayList<>();
|
||||
msHostList.add(msHost2);
|
||||
Mockito.when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.lenient().when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.when(msHostDao.findById(1L)).thenReturn(msHost1);
|
||||
PrepareForShutdownCmd cmd = mock(PrepareForShutdownCmd.class);
|
||||
Mockito.when(cmd.getManagementServerId()).thenReturn(1L);
|
||||
|
|
@ -169,7 +169,7 @@ public class ManagementServerMaintenanceManagerImplTest {
|
|||
ManagementServerHostVO msHost = mock(ManagementServerHostVO.class);
|
||||
Mockito.when(msHost.getState()).thenReturn(ManagementServerHost.State.Up);
|
||||
List<ManagementServerHostVO> msHostList = new ArrayList<>();
|
||||
Mockito.when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.lenient().when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.when(msHostDao.findById(1L)).thenReturn(msHost);
|
||||
PrepareForShutdownCmd cmd = mock(PrepareForShutdownCmd.class);
|
||||
Mockito.when(cmd.getManagementServerId()).thenReturn(1L);
|
||||
|
|
@ -185,7 +185,7 @@ public class ManagementServerMaintenanceManagerImplTest {
|
|||
ManagementServerHostVO msHost = mock(ManagementServerHostVO.class);
|
||||
Mockito.when(msHost.getState()).thenReturn(ManagementServerHost.State.Up);
|
||||
List<ManagementServerHostVO> msHostList = new ArrayList<>();
|
||||
Mockito.when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.lenient().when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.when(msHostDao.findById(1L)).thenReturn(msHost);
|
||||
PrepareForShutdownCmd cmd = mock(PrepareForShutdownCmd.class);
|
||||
Mockito.when(cmd.getManagementServerId()).thenReturn(1L);
|
||||
|
|
@ -200,7 +200,7 @@ public class ManagementServerMaintenanceManagerImplTest {
|
|||
public void prepareForShutdownCmdSuccessResponseFromClusterManager() {
|
||||
ManagementServerHostVO msHost = mock(ManagementServerHostVO.class);
|
||||
Mockito.when(msHost.getState()).thenReturn(ManagementServerHost.State.Up);
|
||||
Mockito.when(msHostDao.listBy(any())).thenReturn(new ArrayList<>());
|
||||
Mockito.lenient().when(msHostDao.listBy(any())).thenReturn(new ArrayList<>());
|
||||
Mockito.when(msHostDao.findById(1L)).thenReturn(msHost);
|
||||
Mockito.when(hostDao.listByMs(anyLong())).thenReturn(new ArrayList<>());
|
||||
PrepareForShutdownCmd cmd = mock(PrepareForShutdownCmd.class);
|
||||
|
|
@ -279,7 +279,7 @@ public class ManagementServerMaintenanceManagerImplTest {
|
|||
ManagementServerHostVO msHost2 = mock(ManagementServerHostVO.class);
|
||||
List<ManagementServerHostVO> msHostList = new ArrayList<>();
|
||||
msHostList.add(msHost2);
|
||||
Mockito.when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.lenient().when(msHostDao.listBy(any())).thenReturn(msHostList);
|
||||
Mockito.when(msHostDao.findById(1L)).thenReturn(msHost1);
|
||||
TriggerShutdownCmd cmd = mock(TriggerShutdownCmd.class);
|
||||
Mockito.when(cmd.getManagementServerId()).thenReturn(1L);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${cs.mockito.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -776,7 +776,8 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
|||
|
||||
if (answer != null && answer.getResult()) {
|
||||
// successfully downloaded template to primary storage
|
||||
answer = createVolumeSnapshot(cmd, size, conn, volName, dstTO);
|
||||
TemplateObjectTO templ = (TemplateObjectTO) ((CopyCmdAnswer) answer).getNewData();
|
||||
answer = createVolumeSnapshot(cmd, size, conn, volName, templ);
|
||||
} else {
|
||||
err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned.";
|
||||
}
|
||||
|
|
@ -983,7 +984,6 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
|||
} else {
|
||||
dstTO.setPath(StorPoolUtil.devPath(
|
||||
StorPoolUtil.getSnapshotNameFromResponse(resp, false, StorPoolUtil.GLOBAL_ID)));
|
||||
dstTO.setSize(size);
|
||||
answer = new CopyCmdAnswer(dstTO);
|
||||
}
|
||||
return answer;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${cs.mockito.version}</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.14.5</version>
|
||||
<version>1.15.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
|
|
|||
|
|
@ -59,6 +59,12 @@
|
|||
<artifactId>assertj-core</artifactId>
|
||||
<version>${cs.assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
|
|||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "User uuid")
|
||||
@Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "User uuid")
|
||||
private Long userId;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, required = false, description = "Domain uuid")
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain uuid")
|
||||
private Long domainId;
|
||||
|
||||
@Override
|
||||
|
|
@ -131,10 +131,12 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
|
|||
}
|
||||
|
||||
if (userUuid != null && domainUuid != null) {
|
||||
logger.debug("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserAccount.getId() + "] to useraccount [" + userUuid + "] in domain [" + domainUuid + "]");
|
||||
final User user = _userDao.findByUuid(userUuid);
|
||||
final Domain domain = _domainDao.findByUuid(domainUuid);
|
||||
final UserAccount nextUserAccount = _accountService.getUserAccountById(user.getId());
|
||||
if (nextUserAccount != null && !nextUserAccount.getAccountState().equals(Account.State.ENABLED.toString())) {
|
||||
logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account [" + nextUserAccount.getAccountName() + "] is not enabled");
|
||||
throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
|
||||
"The requested user account is locked and cannot be switched to, please contact your administrator.",
|
||||
params, responseType));
|
||||
|
|
@ -145,25 +147,31 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
|
|||
|| !nextUserAccount.getExternalEntity().equals(currentUserAccount.getExternalEntity())
|
||||
|| (nextUserAccount.getDomainId() != domain.getId())
|
||||
|| (nextUserAccount.getSource() != User.Source.SAML2)) {
|
||||
logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account is not found or invalid");
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
|
||||
"User account is not allowed to switch to the requested account",
|
||||
params, responseType));
|
||||
}
|
||||
try {
|
||||
if (_apiServer.verifyUser(nextUserAccount.getId())) {
|
||||
logger.info("User [" + currentUserAccount.getUsername() + "] user profile switch is accepted: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]");
|
||||
// need to set a sessoin variable to inform the login function of the specific user to login as, rather than using email only (which could have multiple matches)
|
||||
session.setAttribute("nextUserId", user.getId());
|
||||
final LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, nextUserAccount.getUsername(), nextUserAccount.getUsername() + nextUserAccount.getSource().toString(),
|
||||
nextUserAccount.getDomainId(), null, remoteAddress, params);
|
||||
SAMLUtils.setupSamlUserCookies(loginResponse, resp);
|
||||
resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
|
||||
session.removeAttribute("nextUserId");
|
||||
logger.debug("User [" + currentUserAccount.getUsername() + "] user profile switch cookies set: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]");
|
||||
//resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
|
||||
return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
|
||||
}
|
||||
} catch (CloudAuthenticationException | IOException exception) {
|
||||
logger.debug("Failed to switch to request SAML user account due to: " + exception.getMessage());
|
||||
logger.debug("User [{}] user profile switch cookies set FAILED: from [{}] to user profile [{}] in domain [{}] with account [{}]", currentUserAccount.getUsername(), currentUserId, userUuid, domainUuid, nextUserAccount.getAccountName(), exception);
|
||||
}
|
||||
} else {
|
||||
List<UserAccountVO> switchableAccounts = _userAccountDao.getAllUsersByNameAndEntity(currentUserAccount.getUsername(), currentUserAccount.getExternalEntity());
|
||||
if (switchableAccounts != null && switchableAccounts.size() > 0 && currentUserId != User.UID_SYSTEM) {
|
||||
List<SamlUserAccountResponse> accountResponses = new ArrayList<SamlUserAccountResponse>();
|
||||
if (switchableAccounts != null && !switchableAccounts.isEmpty() && currentUserId != User.UID_SYSTEM) {
|
||||
List<SamlUserAccountResponse> accountResponses = new ArrayList<>();
|
||||
for (UserAccountVO userAccount: switchableAccounts) {
|
||||
User user = _userDao.getUser(userAccount.getId());
|
||||
Domain domain = _domainService.getDomain(userAccount.getDomainId());
|
||||
|
|
@ -176,8 +184,9 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
|
|||
accountResponse.setAccountName(userAccount.getAccountName());
|
||||
accountResponse.setIdpId(user.getExternalEntity());
|
||||
accountResponses.add(accountResponse);
|
||||
logger.debug("Returning available useraccount for [{}]: UserUUID: [{}], DomainUUID: [{}], Account: [{}]", currentUserAccount.getUsername(), user.getUuid(), domain.getUuid(), userAccount.getAccountName());
|
||||
}
|
||||
ListResponse<SamlUserAccountResponse> response = new ListResponse<SamlUserAccountResponse>();
|
||||
ListResponse<SamlUserAccountResponse> response = new ListResponse<>();
|
||||
response.setResponses(accountResponses);
|
||||
response.setResponseName(getCommandName());
|
||||
return ApiResponseSerializer.toSerializedString(response, responseType);
|
||||
|
|
@ -196,7 +205,7 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
|
|||
@Override
|
||||
public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
|
||||
for (PluggableAPIAuthenticator authManager: authenticators) {
|
||||
if (authManager != null && authManager instanceof SAML2AuthManager) {
|
||||
if (authManager instanceof SAML2AuthManager) {
|
||||
_samlAuthManager = (SAML2AuthManager) authManager;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ import com.cloud.user.UserAccountVO;
|
|||
import com.cloud.user.dao.UserAccountDao;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
|
||||
@APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {})
|
||||
@APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", responseObject = LoginCmdResponse.class)
|
||||
public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator, Configurable {
|
||||
private static final String s_name = "loginresponse";
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
@Inject
|
||||
private UserAccountDao userAccountDao;
|
||||
|
||||
protected static ConfigKey<String> saml2FailedLoginRedirectUrl = new ConfigKey<String>("Advanced", String.class, "saml2.failed.login.redirect.url", "",
|
||||
protected static ConfigKey<String> saml2FailedLoginRedirectUrl = new ConfigKey<>("Advanced", String.class, "saml2.failed.login.redirect.url", "",
|
||||
"The URL to redirect the SAML2 login failed message (the default vaulue is empty).", true);
|
||||
|
||||
SAML2AuthManager samlAuthManager;
|
||||
|
|
@ -190,7 +190,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
String authnId = SAMLUtils.generateSecureRandomId();
|
||||
samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
|
||||
logger.debug("Sending SAMLRequest id=" + authnId);
|
||||
String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value());
|
||||
String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), SAML2AuthManager.SAMLRequirePasswordLogin.value());
|
||||
resp.sendRedirect(redirectUrl);
|
||||
return "";
|
||||
} if (params.containsKey("SAMLart")) {
|
||||
|
|
@ -207,7 +207,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
params, responseType));
|
||||
}
|
||||
|
||||
String username = null;
|
||||
String username;
|
||||
Issuer issuer = processedSAMLResponse.getIssuer();
|
||||
SAMLProviderMetadata spMetadata = samlAuthManager.getSPMetadata();
|
||||
SAMLProviderMetadata idpMetadata = samlAuthManager.getIdPMetadata(issuer.getValue());
|
||||
|
|
@ -273,7 +273,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
try {
|
||||
assertion = decrypter.decrypt(encryptedAssertion);
|
||||
} catch (DecryptionException e) {
|
||||
logger.warn("SAML EncryptedAssertion error: " + e.toString());
|
||||
logger.warn("SAML EncryptedAssertion error: " + e);
|
||||
}
|
||||
if (assertion == null) {
|
||||
continue;
|
||||
|
|
@ -310,7 +310,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
|
||||
UserAccount userAccount = null;
|
||||
List<UserAccountVO> possibleUserAccounts = userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue());
|
||||
if (possibleUserAccounts != null && possibleUserAccounts.size() > 0) {
|
||||
if (possibleUserAccounts != null && !possibleUserAccounts.isEmpty()) {
|
||||
// Log into the first enabled user account
|
||||
// Users can switch to other allowed accounts later
|
||||
for (UserAccountVO possibleUserAccount : possibleUserAccounts) {
|
||||
|
|
@ -370,7 +370,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
@Override
|
||||
public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
|
||||
for (PluggableAPIAuthenticator authManager: authenticators) {
|
||||
if (authManager != null && authManager instanceof SAML2AuthManager) {
|
||||
if (authManager instanceof SAML2AuthManager) {
|
||||
samlAuthManager = (SAML2AuthManager) authManager;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableSe
|
|||
ConfigKey<String> SAMLUserSessionKeyPathAttribute = new ConfigKey<String>("Advanced", String.class, "saml2.user.sessionkey.path", "",
|
||||
"The Path attribute of sessionkey cookie when SAML users have logged in. If not set, it will be set to the path of SAML redirection URL (saml2.redirect.url).", true);
|
||||
|
||||
ConfigKey<Boolean> SAMLRequirePasswordLogin = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.require.password", "true",
|
||||
"When enabled SAML2 will validate that the SAML login was performed with a password. If disabled, other forms of authentication are allowed (two-factor, certificate, etc) on the SAML Authentication Provider", true);
|
||||
|
||||
|
||||
SAMLProviderMetadata getSPMetadata();
|
||||
SAMLProviderMetadata getIdPMetadata(String entityId);
|
||||
Collection<SAMLProviderMetadata> getAllIdPMetadata();
|
||||
|
|
|
|||
|
|
@ -541,6 +541,6 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
|
|||
SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
|
||||
SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
|
||||
SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature,
|
||||
SAMLForceAuthn, SAMLUserSessionKeyPathAttribute};
|
||||
SAMLForceAuthn, SAMLUserSessionKeyPathAttribute, SAMLRequirePasswordLogin};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,11 +152,11 @@ public class SAMLUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm) {
|
||||
public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm, boolean requirePasswordAuthentication) {
|
||||
String redirectUrl = "";
|
||||
try {
|
||||
DefaultBootstrap.bootstrap();
|
||||
AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl());
|
||||
AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl(), requirePasswordAuthentication);
|
||||
PrivateKey privateKey = null;
|
||||
if (spMetadata.getKeyPair() != null) {
|
||||
privateKey = spMetadata.getKeyPair().getPrivate();
|
||||
|
|
@ -169,13 +169,36 @@ public class SAMLUtils {
|
|||
return redirectUrl;
|
||||
}
|
||||
|
||||
public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl) {
|
||||
public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl, boolean requirePasswordAuthentication) {
|
||||
// Issuer object
|
||||
IssuerBuilder issuerBuilder = new IssuerBuilder();
|
||||
Issuer issuer = issuerBuilder.buildObject();
|
||||
issuer.setValue(spId);
|
||||
|
||||
// AuthnContextClass
|
||||
// Creation of AuthRequestObject
|
||||
AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
|
||||
AuthnRequest authnRequest = authRequestBuilder.buildObject();
|
||||
|
||||
// AuthnContextClass. When this is false, the authentication requirements are defered to the SAML IDP and its default or configured workflow
|
||||
if (requirePasswordAuthentication) {
|
||||
setRequestedAuthnContext(authnRequest, requirePasswordAuthentication);
|
||||
}
|
||||
|
||||
authnRequest.setID(authnId);
|
||||
authnRequest.setDestination(idpUrl);
|
||||
authnRequest.setVersion(SAMLVersion.VERSION_20);
|
||||
authnRequest.setForceAuthn(SAML2AuthManager.SAMLForceAuthn.value());
|
||||
authnRequest.setIsPassive(false);
|
||||
authnRequest.setIssueInstant(new DateTime());
|
||||
authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
|
||||
authnRequest.setAssertionConsumerServiceURL(consumerUrl);
|
||||
authnRequest.setProviderName(spId);
|
||||
authnRequest.setIssuer(issuer);
|
||||
|
||||
return authnRequest;
|
||||
}
|
||||
|
||||
public static void setRequestedAuthnContext(AuthnRequest authnRequest, boolean requirePasswordAuthentication) {
|
||||
AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
|
||||
AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
|
||||
SAMLConstants.SAML20_NS,
|
||||
|
|
@ -187,23 +210,7 @@ public class SAMLUtils {
|
|||
RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
|
||||
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
|
||||
requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
|
||||
|
||||
// Creation of AuthRequestObject
|
||||
AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
|
||||
AuthnRequest authnRequest = authRequestBuilder.buildObject();
|
||||
authnRequest.setID(authnId);
|
||||
authnRequest.setDestination(idpUrl);
|
||||
authnRequest.setVersion(SAMLVersion.VERSION_20);
|
||||
authnRequest.setForceAuthn(SAML2AuthManager.SAMLForceAuthn.value());
|
||||
authnRequest.setIsPassive(false);
|
||||
authnRequest.setIssueInstant(new DateTime());
|
||||
authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
|
||||
authnRequest.setAssertionConsumerServiceURL(consumerUrl);
|
||||
authnRequest.setProviderName(spId);
|
||||
authnRequest.setIssuer(issuer);
|
||||
authnRequest.setRequestedAuthnContext(requestedAuthnContext);
|
||||
|
||||
return authnRequest;
|
||||
}
|
||||
|
||||
public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) {
|
||||
|
|
@ -285,23 +292,6 @@ public class SAMLUtils {
|
|||
}
|
||||
|
||||
public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp) throws IOException {
|
||||
resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("isSAML", URLEncoder.encode("true", HttpUtils.UTF_8)));
|
||||
resp.addCookie(new Cookie("twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8)));
|
||||
String providerFor2FA = loginResponse.getProviderFor2FA();
|
||||
if (StringUtils.isNotEmpty(providerFor2FA)) {
|
||||
resp.addCookie(new Cookie("twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), HttpUtils.UTF_8)));
|
||||
}
|
||||
String timezone = loginResponse.getTimeZone();
|
||||
if (timezone != null) {
|
||||
resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
|
||||
}
|
||||
resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
|
||||
|
||||
String redirectUrl = SAML2AuthManager.SAMLCloudStackRedirectionUrl.value();
|
||||
String path = SAML2AuthManager.SAMLUserSessionKeyPathAttribute.value();
|
||||
String domain = null;
|
||||
|
|
@ -317,6 +307,18 @@ public class SAMLUtils {
|
|||
} catch (URISyntaxException ex) {
|
||||
throw new CloudRuntimeException("Invalid URI: " + redirectUrl);
|
||||
}
|
||||
|
||||
addBaseCookies(loginResponse, resp, domain, path);
|
||||
|
||||
String providerFor2FA = loginResponse.getProviderFor2FA();
|
||||
if (StringUtils.isNotEmpty(providerFor2FA)) {
|
||||
resp.addCookie(newCookie(domain, path,"twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), HttpUtils.UTF_8)));
|
||||
}
|
||||
String timezone = loginResponse.getTimeZone();
|
||||
if (timezone != null) {
|
||||
resp.addCookie(newCookie(domain, path,"timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
|
||||
}
|
||||
|
||||
String sameSite = ApiServlet.getApiSessionKeySameSite();
|
||||
String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite);
|
||||
LOGGER.debug("Adding sessionkey cookie to response: " + sessionKeyCookie);
|
||||
|
|
@ -324,6 +326,24 @@ public class SAMLUtils {
|
|||
resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), sameSite));
|
||||
}
|
||||
|
||||
private static void addBaseCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp, String domain, String path) throws IOException {
|
||||
resp.addCookie(newCookie(domain, path, "userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"isSAML", URLEncoder.encode("true", HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8)));
|
||||
resp.addCookie(newCookie(domain, path,"userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
|
||||
}
|
||||
|
||||
private static Cookie newCookie(final String domain, final String path, final String name, final String value) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
cookie.setDomain(domain);
|
||||
cookie.setPath(path);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns base64 encoded PublicKey
|
||||
* @param key PublicKey
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public class SAMLUtilsTest extends TestCase {
|
|||
String idpUrl = "http://idp.domain.example";
|
||||
String spId = "cloudstack";
|
||||
String authnId = SAMLUtils.generateSecureRandomId();
|
||||
AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl);
|
||||
AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl, true);
|
||||
assertEquals(req.getAssertionConsumerServiceURL(), consumerUrl);
|
||||
assertEquals(req.getDestination(), idpUrl);
|
||||
assertEquals(req.getIssuer().getValue(), spId);
|
||||
|
|
@ -86,7 +86,7 @@ public class SAMLUtilsTest extends TestCase {
|
|||
idpMetadata.setSsoUrl(idpUrl);
|
||||
idpMetadata.setEntityId(idpId);
|
||||
|
||||
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
|
||||
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true));
|
||||
assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest");
|
||||
assertEquals(urlScheme, redirectUrl.getScheme());
|
||||
assertEquals(idpDomain, redirectUrl.getHost());
|
||||
|
|
@ -115,7 +115,7 @@ public class SAMLUtilsTest extends TestCase {
|
|||
idpMetadata.setSsoUrl(idpUrl);
|
||||
idpMetadata.setEntityId(idpId);
|
||||
|
||||
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
|
||||
URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true));
|
||||
assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest");
|
||||
assertEquals(urlScheme, redirectUrl.getScheme());
|
||||
assertEquals(idpDomain, redirectUrl.getHost());
|
||||
|
|
|
|||
|
|
@ -213,7 +213,6 @@ public class ListAndSwitchSAMLAccountCmdTest extends TestCase {
|
|||
loginCmdResponse.set2FAenabled("false");
|
||||
Mockito.when(apiServer.loginUser(nullable(HttpSession.class), nullable(String.class), nullable(String.class),
|
||||
nullable(Long.class), nullable(String.class), nullable(InetAddress.class), nullable(Map.class))).thenReturn(loginCmdResponse);
|
||||
Mockito.doNothing().when(resp).sendRedirect(nullable(String.class));
|
||||
try {
|
||||
cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
|
||||
} catch (ServerApiException exception) {
|
||||
|
|
@ -221,7 +220,6 @@ public class ListAndSwitchSAMLAccountCmdTest extends TestCase {
|
|||
} finally {
|
||||
// accountService should have been called 4 times by now, for this case twice and 2 for cases above
|
||||
Mockito.verify(accountService, Mockito.times(4)).getUserAccountById(Mockito.anyLong());
|
||||
Mockito.verify(resp, Mockito.times(1)).sendRedirect(anyString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ public class SAML2LoginAPIAuthenticatorCmdTest {
|
|||
@Mock
|
||||
HttpServletRequest req;
|
||||
|
||||
@Mock
|
||||
Object _responseObject;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private SAML2LoginAPIAuthenticatorCmd cmdSpy;
|
||||
|
|
|
|||
4
pom.xml
4
pom.xml
|
|
@ -116,7 +116,7 @@
|
|||
<cs.junit.dataprovider.version>1.13.1</cs.junit.dataprovider.version>
|
||||
<cs.junit.jupiter.version>5.9.1</cs.junit.jupiter.version>
|
||||
<cs.guava-testlib.version>18.0</cs.guava-testlib.version>
|
||||
<cs.mockito.version>4.11.0</cs.mockito.version>
|
||||
<cs.mockito.version>5.16.1</cs.mockito.version>
|
||||
<cs.selenium.server.version>1.0-20081010.060147</cs.selenium.server.version>
|
||||
<cs.selenium-java-client-driver.version>1.0.1</cs.selenium-java-client-driver.version>
|
||||
<cs.testng.version>7.1.0</cs.testng.version>
|
||||
|
|
@ -750,7 +750,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${cs.mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import java.util.Map;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -130,8 +129,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
|
|||
// FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
logger.debug("Looking for hosts in zone [{}], pod [{}], cluster [{}]", dcId, podId, clusterId);
|
||||
String paramAsStringToLog = String.format("zone [%s], pod [%s], cluster [%s]", dcId, podId, clusterId);
|
||||
logger.debug("Looking for hosts in {}", paramAsStringToLog);
|
||||
|
||||
String hostTagOnOffering = offering.getHostTag();
|
||||
String hostTagOnTemplate = template.getTemplateTag();
|
||||
|
|
@ -203,8 +202,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
|
|||
|
||||
|
||||
if (clusterHosts.isEmpty()) {
|
||||
logger.error("No suitable host found for vm [{}] with tags [{}].", vmProfile, hostTagOnOffering);
|
||||
throw new CloudRuntimeException(String.format("No suitable host found for vm [%s].", vmProfile));
|
||||
logger.warn("No suitable host found for VM [{}] with tags {} in {}.", vmProfile, hostTagOnOffering, paramAsStringToLog);
|
||||
return null;
|
||||
}
|
||||
// add all hosts that we are not considering to the avoid list
|
||||
List<HostVO> allhostsInCluster = _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, null);
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ import static org.apache.cloudstack.user.UserPasswordResetManager.UserPasswordRe
|
|||
|
||||
@Component
|
||||
public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiServerService, Configurable {
|
||||
private static final Logger ACCESSLOGGER = LogManager.getLogger("apiserver." + ApiServer.class.getName());
|
||||
|
||||
private static final String SANITIZATION_REGEX = "[\n\r]";
|
||||
|
||||
|
|
@ -240,9 +241,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
private EventDistributor eventDistributor = null;
|
||||
private static int s_workerCount = 0;
|
||||
private static Map<String, List<Class<?>>> s_apiNameCmdClassMap = new HashMap<String, List<Class<?>>>();
|
||||
private static Map<String, List<Class<?>>> s_apiNameCmdClassMap = new HashMap<>();
|
||||
|
||||
private static ExecutorService s_executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory(
|
||||
private static ExecutorService s_executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory(
|
||||
"ApiServer"));
|
||||
|
||||
@Inject
|
||||
|
|
@ -358,7 +359,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
String jobEvent = eventInfo.second();
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Handle asyjob publish event " + jobEvent);
|
||||
logger.trace("Handle asyjob publish event {}", jobEvent);
|
||||
if (eventDistributor == null) {
|
||||
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||
}
|
||||
|
|
@ -383,7 +384,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
cmdEventType = eventTypeObj;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieved cmdEventType from job info: " + cmdEventType);
|
||||
logger.debug("Retrieved cmdEventType from job info: {}", cmdEventType);
|
||||
} else {
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Unable to locate cmdEventType marker in job info. publish as unknown event");
|
||||
|
|
@ -427,11 +428,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
protected void setupIntegrationPortListener(Integer apiPort) {
|
||||
if (apiPort == null || apiPort <= 0) {
|
||||
logger.trace(String.format("Skipping setting up listener for integration port as %s is set to %d",
|
||||
IntegrationAPIPort.key(), apiPort));
|
||||
logger.trace("Skipping setting up listener for integration port as {} is set to {}",
|
||||
IntegrationAPIPort.key(), apiPort);
|
||||
return;
|
||||
}
|
||||
logger.debug(String.format("Setting up integration API service listener on port: %d", apiPort));
|
||||
logger.debug("Setting up integration API service listener on port: {}", apiPort);
|
||||
final ListenerThread listenerThread = new ListenerThread(this, apiPort);
|
||||
listenerThread.start();
|
||||
}
|
||||
|
|
@ -442,24 +443,24 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
Integer apiPort = IntegrationAPIPort.value(); // api port, null by default
|
||||
|
||||
final Long snapshotLimit = ConcurrentSnapshotsThresholdPerHost.value();
|
||||
if (snapshotLimit == null || snapshotLimit.longValue() <= 0) {
|
||||
if (snapshotLimit == null || snapshotLimit <= 0) {
|
||||
logger.debug("Global concurrent snapshot config parameter " + ConcurrentSnapshotsThresholdPerHost.value() + " is less or equal 0; defaulting to unlimited");
|
||||
} else {
|
||||
dispatcher.setCreateSnapshotQueueSizeLimit(snapshotLimit);
|
||||
}
|
||||
|
||||
final Long migrationLimit = VolumeApiService.ConcurrentMigrationsThresholdPerDatastore.value();
|
||||
if (migrationLimit == null || migrationLimit.longValue() <= 0) {
|
||||
if (migrationLimit == null || migrationLimit <= 0) {
|
||||
logger.debug("Global concurrent migration config parameter " + VolumeApiService.ConcurrentMigrationsThresholdPerDatastore.value() + " is less or equal 0; defaulting to unlimited");
|
||||
} else {
|
||||
dispatcher.setMigrateQueueSizeLimit(migrationLimit);
|
||||
}
|
||||
|
||||
final Set<Class<?>> cmdClasses = new HashSet<Class<?>>();
|
||||
final Set<Class<?>> cmdClasses = new HashSet<>();
|
||||
for (final PluggableService pluggableService : pluggableServices) {
|
||||
cmdClasses.addAll(pluggableService.getCommands());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Discovered plugin " + pluggableService.getClass().getSimpleName());
|
||||
logger.debug("Discovered plugin {}", pluggableService.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +473,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
String apiName = at.name();
|
||||
List<Class<?>> apiCmdList = s_apiNameCmdClassMap.get(apiName);
|
||||
if (apiCmdList == null) {
|
||||
apiCmdList = new ArrayList<Class<?>>();
|
||||
apiCmdList = new ArrayList<>();
|
||||
s_apiNameCmdClassMap.put(apiName, apiCmdList);
|
||||
}
|
||||
apiCmdList.add(cmdClass);
|
||||
|
|
@ -574,14 +575,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
throw e;
|
||||
}
|
||||
} finally {
|
||||
logger.info(sb.toString());
|
||||
ACCESSLOGGER.info(sb.toString());
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void checkCharacterInkParams(final Map params) {
|
||||
final Map<String, String> stringMap = new HashMap<String, String>();
|
||||
final Map<String, String> stringMap = new HashMap<>();
|
||||
final Set keys = params.keySet();
|
||||
final Iterator keysIter = keys.iterator();
|
||||
while (keysIter.hasNext()) {
|
||||
|
|
@ -604,7 +605,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
public String handleRequest(final Map params, final String responseType, final StringBuilder auditTrailSb) throws ServerApiException {
|
||||
checkCharacterInkParams(params);
|
||||
|
||||
String response = null;
|
||||
String response;
|
||||
String[] command = null;
|
||||
|
||||
try {
|
||||
|
|
@ -625,7 +626,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
if (authManager.getAPIAuthenticator(command[0]) != null) {
|
||||
return null;
|
||||
}
|
||||
final Map<String, String> paramMap = new HashMap<String, String>();
|
||||
final Map<String, String> paramMap = new HashMap<>();
|
||||
final Set keys = params.keySet();
|
||||
final Iterator keysIter = keys.iterator();
|
||||
while (keysIter.hasNext()) {
|
||||
|
|
@ -641,16 +642,16 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
if (cmdClass != null) {
|
||||
APICommand annotation = cmdClass.getAnnotation(APICommand.class);
|
||||
if (annotation == null) {
|
||||
logger.error("No APICommand annotation found for class " + cmdClass.getCanonicalName());
|
||||
logger.error("No APICommand annotation found for class {}", cmdClass.getCanonicalName());
|
||||
throw new CloudRuntimeException("No APICommand annotation found for class " + cmdClass.getCanonicalName());
|
||||
}
|
||||
|
||||
BaseCmd cmdObj = (BaseCmd)cmdClass.newInstance();
|
||||
BaseCmd cmdObj = (BaseCmd)cmdClass.getDeclaredConstructor().newInstance();
|
||||
cmdObj = ComponentContext.inject(cmdObj);
|
||||
cmdObj.configure();
|
||||
cmdObj.setFullUrlParams(paramMap);
|
||||
cmdObj.setResponseType(responseType);
|
||||
cmdObj.setHttpMethod(paramMap.get(ApiConstants.HTTPMETHOD).toString());
|
||||
cmdObj.setHttpMethod(paramMap.get(ApiConstants.HTTPMETHOD));
|
||||
|
||||
// This is where the command is either serialized, or directly dispatched
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
|
@ -659,14 +660,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
} else {
|
||||
final String errorString = "Unknown API command: " + command[0];
|
||||
logger.warn(errorString);
|
||||
auditTrailSb.append(" " + errorString);
|
||||
auditTrailSb.append(" ").append(errorString);
|
||||
throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, errorString);
|
||||
}
|
||||
}
|
||||
} catch (final InvalidParameterValueException ex) {
|
||||
logger.info(ex.getMessage());
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
} catch (final InvalidParameterValueException | IllegalArgumentException ex) {
|
||||
logger.info(ex.getMessage());
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
|
||||
} catch (final PermissionDeniedException ex) {
|
||||
|
|
@ -679,9 +677,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
buf.append(obj.getUuid());
|
||||
buf.append(" ");
|
||||
}
|
||||
logger.info("PermissionDenied: " + ex.getMessage() + " on objs: [" + buf.toString() + "]");
|
||||
logger.info("PermissionDenied: " + ex.getMessage() + " on objs: [" + buf + "]");
|
||||
} else {
|
||||
logger.info("PermissionDenied: " + ex.getMessage());
|
||||
logger.info("PermissionDenied: {}", ex.getMessage());
|
||||
}
|
||||
throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, ex.getMessage(), ex);
|
||||
} catch (final AccountLimitException ex) {
|
||||
|
|
@ -756,7 +754,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
throw new ServerApiException(ApiErrorCode.SERVICE_UNAVAILABLE, msg);
|
||||
}
|
||||
Long objectId = null;
|
||||
String objectUuid = null;
|
||||
String objectUuid;
|
||||
if (cmdObj instanceof BaseAsyncCreateCmd) {
|
||||
final BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd)cmdObj;
|
||||
dispatcher.dispatchCreateCmd(createCmd, params);
|
||||
|
|
@ -797,7 +795,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
}
|
||||
|
||||
params.put("ctxStartEventId", String.valueOf(startEventId));
|
||||
params.put("cmdEventType", asyncCmd.getEventType().toString());
|
||||
params.put("cmdEventType", asyncCmd.getEventType());
|
||||
params.put("ctxDetails", ApiGsonHelper.getBuilder().create().toJson(ctx.getContextParameters()));
|
||||
if (asyncCmd.getHttpMethod() != null) {
|
||||
params.put(ApiConstants.HTTPMETHOD, asyncCmd.getHttpMethod().toString());
|
||||
|
|
@ -860,9 +858,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void buildAsyncListResponse(final BaseListCmd command, final Account account) {
|
||||
final List<ResponseObject> responses = ((ListResponse)command.getResponseObject()).getResponses();
|
||||
if (responses != null && responses.size() > 0) {
|
||||
List<? extends AsyncJob> jobs = null;
|
||||
final List<ResponseObject> responses = ((ListResponse<ResponseObject>)command.getResponseObject()).getResponses();
|
||||
if (responses != null && !responses.isEmpty()) {
|
||||
List<? extends AsyncJob> jobs;
|
||||
|
||||
// list all jobs for ROOT admin
|
||||
if (accountMgr.isRootAdmin(account.getId())) {
|
||||
|
|
@ -871,11 +869,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
jobs = asyncMgr.findInstancePendingAsyncJobs(command.getApiResourceType().toString(), account.getId());
|
||||
}
|
||||
|
||||
if (jobs.size() == 0) {
|
||||
if (jobs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, AsyncJob> objectJobMap = new HashMap<String, AsyncJob>();
|
||||
final Map<String, AsyncJob> objectJobMap = new HashMap<>();
|
||||
for (final AsyncJob job : jobs) {
|
||||
if (job.getInstanceId() == null) {
|
||||
continue;
|
||||
|
|
@ -912,7 +910,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
if (Boolean.TRUE.equals(apiKeyAccessEnabled)) {
|
||||
return true;
|
||||
} else {
|
||||
logger.info("Api-Key access is disabled for the User " + user.toString());
|
||||
logger.info("Api-Key access is disabled for the User {}", user);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -921,7 +919,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
if (Boolean.TRUE.equals(apiKeyAccessEnabled)) {
|
||||
return true;
|
||||
} else {
|
||||
logger.info("Api-Key access is disabled for the Account " + account.toString());
|
||||
logger.info("Api-Key access is disabled for the Account {}", account);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -938,7 +936,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
public boolean verifyRequest(final Map<String, Object[]> requestParameters, final Long userId, InetAddress remoteAddress) throws ServerApiException {
|
||||
try {
|
||||
String apiKey = null;
|
||||
String secretKey = null;
|
||||
String secretKey;
|
||||
String signature = null;
|
||||
String unsignedRequest = null;
|
||||
|
||||
|
|
@ -966,11 +964,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
// - build a request string with sorted params, make sure it's all lowercase
|
||||
// - sign the request, verify the signature is the same
|
||||
final List<String> parameterNames = new ArrayList<String>();
|
||||
|
||||
for (final Object paramNameObj : requestParameters.keySet()) {
|
||||
parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later
|
||||
}
|
||||
// put the name in a list that we'll sort later
|
||||
final List<String> parameterNames = new ArrayList<>(requestParameters.keySet());
|
||||
|
||||
Collections.sort(parameterNames);
|
||||
|
||||
|
|
@ -1006,7 +1002,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
return false; // no signature, bad request
|
||||
}
|
||||
|
||||
Date expiresTS = null;
|
||||
Date expiresTS;
|
||||
// FIXME: Hard coded signature, why not have an enum
|
||||
if ("3".equals(signatureVersion)) {
|
||||
// New signature authentication. Check for expire parameter and its validity
|
||||
|
|
@ -1026,18 +1022,18 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
if (expiresTS.before(now)) {
|
||||
signature = signature.replaceAll(SANITIZATION_REGEX, "_");
|
||||
apiKey = apiKey.replaceAll(SANITIZATION_REGEX, "_");
|
||||
logger.debug(String.format("Request expired -- ignoring ...sig [%s], apiKey [%s].", signature, apiKey));
|
||||
logger.debug("Request expired -- ignoring ...sig [{}], apiKey [{}].", signature, apiKey);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
|
||||
txn.close();
|
||||
User user = null;
|
||||
User user;
|
||||
// verify there is a user with this api key
|
||||
final Pair<User, Account> userAcctPair = accountMgr.findUserByApiKey(apiKey);
|
||||
if (userAcctPair == null) {
|
||||
logger.debug("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey);
|
||||
logger.debug("apiKey does not map to a valid user -- ignoring request, apiKey: {}", apiKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1078,7 +1074,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
if (!equalSig) {
|
||||
signature = signature.replaceAll(SANITIZATION_REGEX, "_");
|
||||
logger.info(String.format("User signature [%s] is not equaled to computed signature [%s].", signature, computedSignature));
|
||||
logger.info("User signature [{}] is not equaled to computed signature [{}].", signature, computedSignature);
|
||||
} else {
|
||||
CallContext.register(user, account);
|
||||
}
|
||||
|
|
@ -1137,10 +1133,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
session.removeAttribute("domain_UUID");
|
||||
}
|
||||
|
||||
final Enumeration attrNames = session.getAttributeNames();
|
||||
final Enumeration<String> attrNames = session.getAttributeNames();
|
||||
if (attrNames != null) {
|
||||
while (attrNames.hasMoreElements()) {
|
||||
final String attrName = (String) attrNames.nextElement();
|
||||
final String attrName = attrNames.nextElement();
|
||||
final Object attrObj = session.getAttribute(attrName);
|
||||
if (ApiConstants.USERNAME.equalsIgnoreCase(attrName)) {
|
||||
response.setUsername(attrObj.toString());
|
||||
|
|
@ -1202,7 +1198,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
domainId = userDomain.getId();
|
||||
}
|
||||
|
||||
UserAccount userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
|
||||
Long userId = (Long)session.getAttribute("nextUserId");
|
||||
UserAccount userAcct = null;
|
||||
if (userId != null) {
|
||||
userAcct = accountMgr.getUserAccountById(userId);
|
||||
} else {
|
||||
userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
|
||||
}
|
||||
|
||||
if (userAcct != null) {
|
||||
final String timezone = userAcct.getTimezone();
|
||||
float offsetInHrs = 0f;
|
||||
|
|
@ -1214,7 +1217,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
final long longDate = date.getTime();
|
||||
final float offsetInMs = (t.getOffset(longDate));
|
||||
offsetInHrs = offsetInMs / (1000 * 60 * 60);
|
||||
logger.info("Timezone offset from UTC is: " + offsetInHrs);
|
||||
logger.info("Timezone offset from UTC is: {}", offsetInHrs);
|
||||
}
|
||||
|
||||
final Account account = accountMgr.getAccount(userAcct.getAccountId());
|
||||
|
|
@ -1272,7 +1275,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
// (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that
|
||||
// to the login response so that session-based authenticators know to send the key back
|
||||
final SecureRandom sesssionKeyRandom = new SecureRandom();
|
||||
final byte sessionKeyBytes[] = new byte[20];
|
||||
final byte[] sessionKeyBytes = new byte[20];
|
||||
sesssionKeyRandom.nextBytes(sessionKeyBytes);
|
||||
final String sessionKey = Base64.encodeBase64URLSafeString(sessionKeyBytes);
|
||||
session.setAttribute(ApiConstants.SESSIONKEY, sessionKey);
|
||||
|
|
@ -1285,7 +1288,6 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
@Override
|
||||
public void logoutUser(final long userId) {
|
||||
accountMgr.logoutUser(userId);
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1313,30 +1315,26 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
if (StringUtils.isBlank(userAccount.getEmail())) {
|
||||
logger.error(String.format(
|
||||
"Email is not set. username: %s account id: %d domain id: %d",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), userAccount.getDomainId()));
|
||||
logger.error("Email is not set. username: {} account id: {} domain id: {}",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), userAccount.getDomainId());
|
||||
throw new CloudRuntimeException("Email is not set for the user.");
|
||||
}
|
||||
|
||||
if (!EnumUtils.getEnumIgnoreCase(Account.State.class, userAccount.getState()).equals(Account.State.ENABLED)) {
|
||||
logger.error(String.format(
|
||||
"User is not enabled. username: %s account id: %d domain id: %s",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid()));
|
||||
logger.error("User is not enabled. username: {} account id: {} domain id: {}",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid());
|
||||
throw new CloudRuntimeException("User is not enabled.");
|
||||
}
|
||||
|
||||
if (!EnumUtils.getEnumIgnoreCase(Account.State.class, userAccount.getAccountState()).equals(Account.State.ENABLED)) {
|
||||
logger.error(String.format(
|
||||
"Account is not enabled. username: %s account id: %d domain id: %s",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid()));
|
||||
logger.error("Account is not enabled. username: {} account id: {} domain id: {}",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid());
|
||||
throw new CloudRuntimeException("Account is not enabled.");
|
||||
}
|
||||
|
||||
if (!domain.getState().equals(Domain.State.Active)) {
|
||||
logger.error(String.format(
|
||||
"Domain is not active. username: %s account id: %d domain id: %s",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid()));
|
||||
logger.error("Domain is not active. username: {} account id: {} domain id: {}",
|
||||
userAccount.getUsername(), userAccount.getAccountId(), domain.getUuid());
|
||||
throw new CloudRuntimeException("Domain is not active.");
|
||||
}
|
||||
|
||||
|
|
@ -1444,7 +1442,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
// code to be very specific to our needs
|
||||
static class ListenerThread extends Thread {
|
||||
|
||||
private static Logger LOGGER = LogManager.getLogger(ListenerThread.class);
|
||||
private static final Logger LOGGER = LogManager.getLogger(ListenerThread.class);
|
||||
private HttpService _httpService = null;
|
||||
private ServerSocket _serverSocket = null;
|
||||
private HttpParams _params = null;
|
||||
|
|
@ -1483,7 +1481,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
LOGGER.info("ApiServer listening on port " + _serverSocket.getLocalPort());
|
||||
LOGGER.info("ApiServer listening on port {}", _serverSocket.getLocalPort());
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
// Set up HTTP connection
|
||||
|
|
@ -1526,10 +1524,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
}
|
||||
} catch (final IOException ex) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("ApiServer: IOException - " + ex);
|
||||
logger.trace("ApiServer: IOException - {}", ex.toString());
|
||||
}
|
||||
} catch (final HttpException ex) {
|
||||
logger.warn("ApiServer: Unrecoverable HTTP protocol violation" + ex);
|
||||
logger.warn("ApiServer: Unrecoverable HTTP protocol violation {}", ex.toString());
|
||||
} finally {
|
||||
try {
|
||||
_conn.shutdown();
|
||||
|
|
@ -1542,7 +1540,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
@Override
|
||||
public String getSerializedApiError(final int errorCode, final String errorText, final Map<String, Object[]> apiCommandParams, final String responseType) {
|
||||
String responseName = null;
|
||||
Class<?> cmdClass = null;
|
||||
Class<?> cmdClass;
|
||||
String responseText = null;
|
||||
|
||||
try {
|
||||
|
|
@ -1555,7 +1553,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
final String cmdName = ((String[])cmdObj)[0];
|
||||
cmdClass = getCmdClass(cmdName);
|
||||
if (cmdClass != null) {
|
||||
responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName();
|
||||
responseName = ((BaseCmd)cmdClass.getDeclaredConstructor().newInstance()).getCommandName();
|
||||
} else {
|
||||
responseName = "errorresponse";
|
||||
}
|
||||
|
|
@ -1577,7 +1575,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
@Override
|
||||
public String getSerializedApiError(final ServerApiException ex, final Map<String, Object[]> apiCommandParams, final String responseType) {
|
||||
String responseName = null;
|
||||
Class<?> cmdClass = null;
|
||||
Class<?> cmdClass;
|
||||
String responseText = null;
|
||||
|
||||
if (ex == null) {
|
||||
|
|
@ -1595,7 +1593,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
final String cmdName = ((String[])cmdObj)[0];
|
||||
cmdClass = getCmdClass(cmdName);
|
||||
if (cmdClass != null) {
|
||||
responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName();
|
||||
responseName = ((BaseCmd)cmdClass.getDeclaredConstructor().newInstance()).getCommandName();
|
||||
} else {
|
||||
responseName = "errorresponse";
|
||||
}
|
||||
|
|
@ -1607,8 +1605,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||
apiResponse.setResponseName(responseName);
|
||||
final ArrayList<ExceptionProxyObject> idList = ex.getIdProxyList();
|
||||
if (idList != null) {
|
||||
for (int i = 0; i < idList.size(); i++) {
|
||||
apiResponse.addProxyObject(idList.get(i));
|
||||
for (ExceptionProxyObject exceptionProxyObject : idList) {
|
||||
apiResponse.addProxyObject(exceptionProxyObject);
|
||||
}
|
||||
}
|
||||
// Also copy over the cserror code and the function/layer in which
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import java.net.InetAddress;
|
|||
import java.net.URLDecoder;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -76,9 +75,7 @@ import com.cloud.utils.net.NetUtils;
|
|||
@Component("apiServlet")
|
||||
public class ApiServlet extends HttpServlet {
|
||||
protected static Logger LOGGER = LogManager.getLogger(ApiServlet.class);
|
||||
private final static List<String> s_clientAddressHeaders = Collections
|
||||
.unmodifiableList(Arrays.asList("X-Forwarded-For",
|
||||
"HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR", "Remote_Addr"));
|
||||
private static final Logger ACCESSLOGGER = LogManager.getLogger("apiserver." + ApiServlet.class.getName());
|
||||
private static final String REPLACEMENT = "_";
|
||||
private static final String LOGGER_REPLACEMENTS = "[\n\r\t]";
|
||||
|
||||
|
|
@ -374,7 +371,7 @@ public class ApiServlet extends HttpServlet {
|
|||
LOGGER.error("unknown exception writing api response", ex);
|
||||
auditTrailSb.append(" unknown exception writing api response");
|
||||
} finally {
|
||||
LOGGER.info(auditTrailSb.toString());
|
||||
ACCESSLOGGER.info(auditTrailSb.toString());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("===END=== " + reqStr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,11 +151,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
|||
activeTmpltSearch.and("store_id", activeTmpltSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ);
|
||||
activeTmpltSearch.and("type", activeTmpltSearch.entity().getTemplateType(), SearchCriteria.Op.EQ);
|
||||
activeTmpltSearch.and("templateState", activeTmpltSearch.entity().getTemplateState(), SearchCriteria.Op.EQ);
|
||||
activeTmpltSearch.and().op("public", activeTmpltSearch.entity().isPublicTemplate(), SearchCriteria.Op.EQ);
|
||||
activeTmpltSearch.or().op("publicNoUrl", activeTmpltSearch.entity().isPublicTemplate(), SearchCriteria.Op.EQ);
|
||||
activeTmpltSearch.and("url", activeTmpltSearch.entity().getUrl(), SearchCriteria.Op.NULL);
|
||||
activeTmpltSearch.cp();
|
||||
activeTmpltSearch.cp();
|
||||
activeTmpltSearch.done();
|
||||
|
||||
publicTmpltSearch = createSearchBuilder();
|
||||
|
|
@ -688,8 +683,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
|||
sc.setParameters("store_id", storeId);
|
||||
sc.setParameters("type", TemplateType.USER);
|
||||
sc.setParameters("templateState", VirtualMachineTemplate.State.Active);
|
||||
sc.setParameters("public", Boolean.FALSE);
|
||||
sc.setParameters("publicNoUrl",Boolean.TRUE);
|
||||
return searchIncludingRemoved(sc, null, null, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -384,11 +384,10 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean scheduleMigration(final VMInstanceVO vm, ReasonType reasonType) {
|
||||
public boolean scheduleMigration(final VMInstanceVO vm, HighAvailabilityManager.ReasonType reasonType) {
|
||||
if (vm.getHostId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VmHaEnabled.valueIn(vm.getDataCenterId())) {
|
||||
String message = String.format("Unable to schedule migration for the VM %s on host %s, VM high availability manager is disabled.", vm, _hostDao.findById(vm.getHostId()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -398,6 +397,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
return false;
|
||||
}
|
||||
|
||||
Long hostId = VirtualMachine.State.Migrating.equals(vm.getState()) ? vm.getLastHostId() : vm.getHostId();
|
||||
final HaWorkVO work = new HaWorkVO(vm.getId(), vm.getType(), WorkType.Migration, Step.Scheduled, vm.getHostId(), vm.getState(), 0, vm.getUpdated(), reasonType);
|
||||
_haDao.persist(work);
|
||||
logger.info("Scheduled migration work of VM {} from host {} with HAWork {}", vm, _hostDao.findById(vm.getHostId()), work);
|
||||
|
|
@ -813,6 +813,18 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
return null;
|
||||
}
|
||||
logger.info("Migration attempt: for VM {}from host {}. Starting attempt: {}/{} times.", vm, srcHost, 1 + work.getTimesTried(), _maxRetries);
|
||||
|
||||
if (VirtualMachine.State.Stopped.equals(vm.getState())) {
|
||||
logger.info(String.format("vm %s is Stopped, skipping migrate.", vm));
|
||||
return null;
|
||||
}
|
||||
if (VirtualMachine.State.Running.equals(vm.getState()) && srcHostId != vm.getHostId()) {
|
||||
logger.info(String.format("VM %s is running on a different host %s, skipping migration", vm, vm.getHostId()));
|
||||
return null;
|
||||
}
|
||||
logger.info("Migration attempt: for VM " + vm.getUuid() + "from host id " + srcHostId +
|
||||
". Starting attempt: " + (1 + work.getTimesTried()) + "/" + _maxRetries + " times.");
|
||||
|
||||
try {
|
||||
work.setStep(Step.Migrating);
|
||||
_haDao.update(work.getId(), work);
|
||||
|
|
@ -1148,6 +1160,15 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
@Override
|
||||
public void run() {
|
||||
logger.info("Starting work");
|
||||
try {
|
||||
synchronized (this) {
|
||||
wait(_timeToSleep);
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
logger.info("Interrupted");
|
||||
}
|
||||
logger.info("Starting work");
|
||||
|
||||
while (!_stopped) {
|
||||
_managedContext.runWithContext(new Runnable() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1038,36 +1038,48 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
@Override
|
||||
public void markPublicIpAsAllocated(final IPAddressVO addr) {
|
||||
synchronized (allocatedLock) {
|
||||
Transaction.execute(new TransactionCallbackNoReturn() {
|
||||
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
Account owner = _accountMgr.getAccount(addr.getAllocatedToAccountId());
|
||||
if (_ipAddressDao.lockRow(addr.getId(), true) != null) {
|
||||
final IPAddressVO userIp = _ipAddressDao.findById(addr.getId());
|
||||
if (userIp.getState() == IpAddress.State.Allocating || addr.getState() == IpAddress.State.Free || addr.getState() == IpAddress.State.Reserved) {
|
||||
boolean shouldUpdateIpResourceCount = checkIfIpResourceCountShouldBeUpdated(addr);
|
||||
addr.setState(IpAddress.State.Allocated);
|
||||
if (_ipAddressDao.update(addr.getId(), addr)) {
|
||||
// Save usage event
|
||||
if (owner.getAccountId() != Account.ACCOUNT_ID_SYSTEM) {
|
||||
VlanVO vlan = _vlanDao.findById(addr.getVlanId());
|
||||
String guestType = vlan.getVlanType().toString();
|
||||
if (!isIpDedicated(addr)) {
|
||||
final boolean usageHidden = isUsageHidden(addr);
|
||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(), addr.getDataCenterId(), addr.getId(),
|
||||
addr.getAddress().toString(), addr.isSourceNat(), guestType, addr.getSystem(), usageHidden,
|
||||
addr.getClass().getName(), addr.getUuid());
|
||||
}
|
||||
if (shouldUpdateIpResourceCount) {
|
||||
_resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.public_ip);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error("Failed to mark public IP as allocated: {}", addr);
|
||||
final IPAddressVO userIp = _ipAddressDao.lockRow(addr.getId(), true);
|
||||
if (userIp == null) {
|
||||
logger.error(String.format("Failed to acquire row lock to mark public IP as allocated with ID [%s] and address [%s]", addr.getId(), addr.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
List<IpAddress.State> expectedIpAddressStates = List.of(IpAddress.State.Allocating, IpAddress.State.Free, IpAddress.State.Reserved);
|
||||
if (!expectedIpAddressStates.contains(userIp.getState())) {
|
||||
logger.debug(String.format("Not marking public IP with ID [%s] and address [%s] as allocated, since it is in the [%s] state.", addr.getId(), addr.getAddress(), userIp.getState()));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean shouldUpdateIpResourceCount = checkIfIpResourceCountShouldBeUpdated(addr);
|
||||
addr.setState(IpAddress.State.Allocated);
|
||||
boolean updatedIpAddress = _ipAddressDao.update(addr.getId(), addr);
|
||||
if (!updatedIpAddress) {
|
||||
logger.error(String.format("Failed to mark public IP as allocated with ID [%s] and address [%s]", addr.getId(), addr.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.getAccountId() != Account.ACCOUNT_ID_SYSTEM) {
|
||||
if (shouldUpdateIpResourceCount) {
|
||||
try (CheckedReservation publicIpReservation = new CheckedReservation(owner, ResourceType.public_ip, 1L, reservationDao, _resourceLimitMgr)) {
|
||||
_resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.public_ip);
|
||||
} catch (Exception e) {
|
||||
_ipAddressDao.unassignIpAddress(addr.getId());
|
||||
throw new CloudRuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.error("Failed to acquire row lock to mark public IP as allocated: {}", addr);
|
||||
|
||||
VlanVO vlan = _vlanDao.findById(addr.getVlanId());
|
||||
String guestType = vlan.getVlanType().toString();
|
||||
if (!isIpDedicated(addr)) {
|
||||
final boolean usageHidden = isUsageHidden(addr);
|
||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(), addr.getDataCenterId(), addr.getId(),
|
||||
addr.getAddress().toString(), addr.isSourceNat(), guestType, addr.getSystem(), usageHidden,
|
||||
addr.getClass().getName(), addr.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1553,27 +1565,31 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
|
||||
boolean isSourceNat = isSourceNatAvailableForNetwork(owner, ipToAssoc, network);
|
||||
|
||||
logger.debug("Associating ip " + ipToAssoc + " to network " + network);
|
||||
logger.debug(String.format("Associating IP [%s] to network [%s].", ipToAssoc, network));
|
||||
|
||||
boolean success = false;
|
||||
IPAddressVO ip = null;
|
||||
try (CheckedReservation publicIpReservation = new CheckedReservation(owner, ResourceType.public_ip, 1l, reservationDao, _resourceLimitMgr)) {
|
||||
ip = _ipAddressDao.findById(ipId);
|
||||
//update ip address with networkId
|
||||
ip.setAssociatedWithNetworkId(networkId);
|
||||
ip.setSourceNat(isSourceNat);
|
||||
_ipAddressDao.update(ipId, ip);
|
||||
try {
|
||||
Pair<IPAddressVO, Boolean> updatedIpAddress = Transaction.execute((TransactionCallbackWithException<Pair<IPAddressVO, Boolean>, Exception>) status -> {
|
||||
IPAddressVO ipAddress = _ipAddressDao.findById(ipId);
|
||||
ipAddress.setAssociatedWithNetworkId(networkId);
|
||||
ipAddress.setSourceNat(isSourceNat);
|
||||
_ipAddressDao.update(ipId, ipAddress);
|
||||
return new Pair<>(_ipAddressDao.findById(ipId), applyIpAssociations(network, false));
|
||||
});
|
||||
|
||||
success = applyIpAssociations(network, false);
|
||||
ip = updatedIpAddress.first();
|
||||
success = updatedIpAddress.second();
|
||||
if (success) {
|
||||
logger.debug("Successfully associated ip address " + ip.getAddress().addr() + " to network " + network);
|
||||
logger.debug(String.format("Successfully associated IP address [%s] to network [%s]", ip.getAddress().addr(), network));
|
||||
} else {
|
||||
logger.warn("Failed to associate ip address " + ip.getAddress().addr() + " to network " + network);
|
||||
logger.warn(String.format("Failed to associate IP address [%s] to network [%s]", ip.getAddress().addr(), network));
|
||||
}
|
||||
return _ipAddressDao.findById(ipId);
|
||||
return ip;
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Failed to associate ip address %s to network %s", ipToAssoc, network), e);
|
||||
throw new CloudRuntimeException(String.format("Failed to associate ip address %s to network %s", ipToAssoc, network), e);
|
||||
String errorMessage = String.format("Failed to associate IP address [%s] to network [%s]", ipToAssoc, network);
|
||||
logger.error(errorMessage, e);
|
||||
throw new CloudRuntimeException(errorMessage, e);
|
||||
} finally {
|
||||
if (!success && releaseOnFailure) {
|
||||
if (ip != null) {
|
||||
|
|
|
|||
|
|
@ -1782,10 +1782,19 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), NETWORK_OFFERING_ID);
|
||||
}
|
||||
|
||||
if (GuestType.Shared == ntwkOff.getGuestType() && !ntwkOff.isSpecifyVlan() && Objects.isNull(associatedNetworkId)) {
|
||||
throw new CloudRuntimeException("Associated network must be provided when creating Shared networks when specifyVlan is false");
|
||||
}
|
||||
|
||||
|
||||
if (GuestType.Shared == ntwkOff.getGuestType()) {
|
||||
if (!ntwkOff.isSpecifyIpRanges()) {
|
||||
throw new CloudRuntimeException("The 'specifyipranges' parameter should be true for Shared Networks");
|
||||
}
|
||||
if (ipv4 && Objects.isNull(startIP)) {
|
||||
throw new CloudRuntimeException("IPv4 address range needs to be provided");
|
||||
}
|
||||
if (ipv6 && Objects.isNull(startIPv6)) {
|
||||
throw new CloudRuntimeException("IPv6 address range needs to be provided");
|
||||
}
|
||||
}
|
||||
Pair<Integer, Integer> interfaceMTUs = validateMtuConfig(publicMtu, privateMtu, zone.getId());
|
||||
mtuCheckForVpcNetwork(vpcId, interfaceMTUs, publicMtu);
|
||||
|
||||
|
|
|
|||
|
|
@ -203,8 +203,8 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
|||
private static Map<Service, Map<Capability, String>> setCapabilities() {
|
||||
Map<Service, Map<Capability, String>> capabilities = new HashMap<>();
|
||||
capabilities.put(Service.UserData, null);
|
||||
capabilities.put(Service.Dhcp, new HashMap<>());
|
||||
capabilities.put(Service.Dns, new HashMap<>());
|
||||
capabilities.put(Service.Dhcp, Map.of(Network.Capability.DhcpAccrossMultipleSubnets, "true"));
|
||||
capabilities.put(Service.Dns, Map.of(Capability.AllowDnsSuffixModification, "true"));
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
|
@ -841,7 +841,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
|||
public boolean configDhcpSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm,
|
||||
DeployDestination dest,
|
||||
ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ import com.cloud.dc.dao.ASNumberDao;
|
|||
import com.cloud.dc.Vlan;
|
||||
import com.cloud.network.dao.NsxProviderDao;
|
||||
import com.cloud.network.element.NsxProviderVO;
|
||||
import com.cloud.resourcelimit.CheckedReservation;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.alert.AlertService;
|
||||
|
|
@ -3206,32 +3205,27 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
// check permissions
|
||||
_accountMgr.checkAccess(caller, null, false, owner, vpc);
|
||||
|
||||
logger.debug("Associating ip " + ipToAssoc + " to vpc " + vpc);
|
||||
logger.debug(String.format("Associating IP [%s] to VPC [%s]", ipToAssoc, vpc));
|
||||
|
||||
final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(vpc.getAccountId(), vpcId, false) == null;
|
||||
try (CheckedReservation publicIpReservation = new CheckedReservation(owner, ResourceType.public_ip, 1l, reservationDao, _resourceLimitMgr)) {
|
||||
Transaction.execute(new TransactionCallbackNoReturn() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
||||
try {
|
||||
IPAddressVO updatedIpAddress = Transaction.execute((TransactionCallbackWithException<IPAddressVO, CloudRuntimeException>) status -> {
|
||||
final IPAddressVO ip = _ipAddressDao.findById(ipId);
|
||||
// update ip address with networkId
|
||||
ip.setVpcId(vpcId);
|
||||
ip.setSourceNat(isSourceNatFinal);
|
||||
|
||||
_ipAddressDao.update(ipId, ip);
|
||||
|
||||
// mark ip as allocated
|
||||
_ipAddrMgr.markPublicIpAsAllocated(ip);
|
||||
}
|
||||
return _ipAddressDao.findById(ipId);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to associate ip " + ipToAssoc + " to vpc " + vpc, e);
|
||||
throw new CloudRuntimeException("Failed to associate ip " + ipToAssoc + " to vpc " + vpc, e);
|
||||
}
|
||||
|
||||
logger.debug("Successfully assigned ip " + ipToAssoc + " to vpc " + vpc);
|
||||
CallContext.current().putContextParameter(IpAddress.class, ipToAssoc.getUuid());
|
||||
return _ipAddressDao.findById(ipId);
|
||||
logger.debug(String.format("Successfully assigned IP [%s] to VPC [%s]", ipToAssoc, vpc));
|
||||
CallContext.current().putContextParameter(IpAddress.class, ipToAssoc.getUuid());
|
||||
return updatedIpAddress;
|
||||
} catch (Exception e) {
|
||||
String errorMessage = String.format("Failed to associate IP address [%s] to VPC [%s]", ipToAssoc, vpc);
|
||||
logger.error(errorMessage, e);
|
||||
throw new CloudRuntimeException(errorMessage, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -3740,7 +3740,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
VolumeApiResult result = future.get();
|
||||
if (result.isFailed()) {
|
||||
logger.debug("migrate volume failed:" + result.getResult());
|
||||
throw new StorageUnavailableException("Migrate volume failed: " + result.getResult(), destPool.getId());
|
||||
throw new CloudRuntimeException("Migrate volume failed: " + result.getResult());
|
||||
}
|
||||
return result.getVolume();
|
||||
} catch (InterruptedException e) {
|
||||
|
|
@ -4117,7 +4117,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
Optional<String> extractUrl = setExtractVolumeSearchCriteria(sc, volume);
|
||||
if (extractUrl.isPresent()) {
|
||||
return extractUrl.get();
|
||||
String url = extractUrl.get();
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, volume ID: %s", url, volume.getUuid()));
|
||||
return url;
|
||||
}
|
||||
|
||||
VMInstanceVO vm = null;
|
||||
|
|
@ -4134,7 +4136,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
VmWorkJobVO placeHolder = null;
|
||||
placeHolder = createPlaceHolderWork(vm.getId());
|
||||
try {
|
||||
return orchestrateExtractVolume(volume.getId(), zoneId);
|
||||
String url = orchestrateExtractVolume(volume.getId(), zoneId);
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, volume ID: %s", url, volume.getUuid()));
|
||||
return url;
|
||||
} finally {
|
||||
_workJobDao.expunge(placeHolder.getId());
|
||||
}
|
||||
|
|
@ -4163,13 +4167,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
// retrieve the entity url from job result
|
||||
if (jobResult != null && jobResult instanceof String) {
|
||||
return (String)jobResult;
|
||||
String url = (String) jobResult;
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, volume ID: %s", url, volume.getUuid()));
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return orchestrateExtractVolume(volume.getId(), zoneId);
|
||||
String url = orchestrateExtractVolume(volume.getId(), zoneId);
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, volume ID: %s", url, volume.getUuid()));
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public abstract class DownloadActiveState extends DownloadState {
|
|||
getDownloadListener().log("handleTimeout, updateMs=" + updateMs + ", curr state= " + getName(), Level.TRACE);
|
||||
}
|
||||
String newState = getName();
|
||||
if (updateMs > 5 * DownloadListener.STATUS_POLL_INTERVAL) {
|
||||
if (updateMs > DownloadListener.DOWNLOAD_TIMEOUT) {
|
||||
newState = Status.DOWNLOAD_ERROR.toString();
|
||||
getDownloadListener().log("timeout: transitioning to download error state, currstate=" + getName(), Level.DEBUG);
|
||||
} else if (updateMs > 3 * DownloadListener.STATUS_POLL_INTERVAL) {
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ public class DownloadListener implements Listener {
|
|||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
public static final int SMALL_DELAY = 100;
|
||||
public static final long STATUS_POLL_INTERVAL = 10000L;
|
||||
public static final long DOWNLOAD_TIMEOUT = 5 * STATUS_POLL_INTERVAL;
|
||||
|
||||
public static final String DOWNLOADED = Status.DOWNLOADED.toString();
|
||||
public static final String NOT_DOWNLOADED = Status.NOT_DOWNLOADED.toString();
|
||||
|
|
|
|||
|
|
@ -390,15 +390,27 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
|||
|
||||
boolean result = snapshotStrategy.revertSnapshot(snapshotInfo);
|
||||
if (result) {
|
||||
// update volume size and primary storage count
|
||||
_resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize() - snapshot.getSize()));
|
||||
volume.setSize(snapshot.getSize());
|
||||
_volsDao.update(volume.getId(), volume);
|
||||
updateVolumeSizeAndPrimaryStorageCount(volume, snapshot);
|
||||
return snapshotInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateVolumeSizeAndPrimaryStorageCount(VolumeVO volume, SnapshotVO snapshot) {
|
||||
Long differenceBetweenVolumeAndSnapshotSize = new Long(volume.getSize() - snapshot.getSize());
|
||||
if (differenceBetweenVolumeAndSnapshotSize != 0) {
|
||||
if (differenceBetweenVolumeAndSnapshotSize > 0) {
|
||||
_resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, differenceBetweenVolumeAndSnapshotSize);
|
||||
} else if (differenceBetweenVolumeAndSnapshotSize < 0) {
|
||||
_resourceLimitMgr.incrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, differenceBetweenVolumeAndSnapshotSize * -1L);
|
||||
}
|
||||
volume.setSize(snapshot.getSize());
|
||||
_volsDao.update(volume.getId(), volume);
|
||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
|
||||
volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_POLICY_UPDATE, eventDescription = "updating snapshot policy", async = true)
|
||||
public SnapshotPolicy updateSnapshotPolicy(UpdateSnapshotPolicyCmd cmd) {
|
||||
|
|
@ -805,7 +817,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
|||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to delete snapshot {}:{}", snapshotCheck, e.toString());
|
||||
logger.debug("Failed to delete snapshot {}:{}", snapshotCheck.getId(), e.toString());
|
||||
|
||||
throw new CloudRuntimeException("Failed to delete snapshot:" + e.toString());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* For each zone ID in {@link TemplateProfile#zoneIdList}, verifies if there is active heuristic rules for allocating template and returns the
|
||||
* For each zone ID in {@link TemplateProfile#getZoneIdList()}, verifies if there is active heuristic rules for allocating template and returns the
|
||||
* {@link DataStore} returned by the heuristic rule. If there is not an active heuristic rule, then allocate it to a random {@link DataStore}, if the ISO/template is private
|
||||
* or allocate it to all {@link DataStore} in the zone, if it is public.
|
||||
* @param profile
|
||||
|
|
@ -454,10 +454,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
|
||||
/**
|
||||
* If the template/ISO is marked as private, then it is allocated to a random secondary storage; otherwise, allocates to every storage pool in every zone given by the
|
||||
* {@link TemplateProfile#zoneIdList}.
|
||||
* {@link TemplateProfile#getZoneIdList()}.
|
||||
*/
|
||||
private void postUploadAllocation(List<DataStore> imageStores, VMTemplateVO template, List<TemplateOrVolumePostUploadCommand> payloads) {
|
||||
Set<Long> zoneSet = new HashSet<Long>();
|
||||
Set<Long> zoneSet = new HashSet<>();
|
||||
Collections.shuffle(imageStores);
|
||||
for (DataStore imageStore : imageStores) {
|
||||
Long zoneId_is = imageStore.getScope().getScopeId();
|
||||
|
|
@ -698,8 +698,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
}
|
||||
|
||||
// delete all cache entries for this template
|
||||
List<TemplateInfo> cacheTmpls = imageFactory.listTemplateOnCache(template.getId());
|
||||
for (TemplateInfo tmplOnCache : cacheTmpls) {
|
||||
List<TemplateInfo> cachedTemplates = imageFactory.listTemplateOnCache(template.getId());
|
||||
for (TemplateInfo tmplOnCache : cachedTemplates) {
|
||||
logger.info("Delete template: {} from image cache store: {}", tmplOnCache, tmplOnCache.getDataStore());
|
||||
tmplOnCache.delete();
|
||||
}
|
||||
|
|
@ -728,27 +728,32 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
}
|
||||
|
||||
// remove its related ACL permission
|
||||
Pair<Class<?>, Long> tmplt = new Pair<Class<?>, Long>(VirtualMachineTemplate.class, template.getId());
|
||||
_messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, tmplt);
|
||||
|
||||
checkAndRemoveTemplateDetails(template);
|
||||
|
||||
// Remove comments (if any)
|
||||
AnnotationService.EntityType entityType = template.getFormat().equals(ImageFormat.ISO) ?
|
||||
AnnotationService.EntityType.ISO : AnnotationService.EntityType.TEMPLATE;
|
||||
annotationDao.removeByEntityType(entityType.name(), template.getUuid());
|
||||
Pair<Class<?>, Long> templateClassForId = new Pair<>(VirtualMachineTemplate.class, template.getId());
|
||||
_messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, templateClassForId);
|
||||
|
||||
List<VMTemplateZoneVO> zoneRegistrations = templateZoneDao.listByTemplateId(template.getId());
|
||||
if (zoneRegistrations.isEmpty()) {
|
||||
removeTemplateDetails(template);
|
||||
removeTemplateAnnotations(template);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private void removeTemplateAnnotations(VMTemplateVO template) {
|
||||
// Remove comments (if any)
|
||||
AnnotationService.EntityType entityType = template.getFormat().equals(ImageFormat.ISO) ?
|
||||
AnnotationService.EntityType.ISO : AnnotationService.EntityType.TEMPLATE;
|
||||
annotationDao.removeByEntityType(entityType.name(), template.getUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* removes details of the template and
|
||||
* if the template is registered as deploy as is,
|
||||
* then it also deletes the details related to deploy as is only if there are no VMs using the template
|
||||
* @param template
|
||||
*/
|
||||
void checkAndRemoveTemplateDetails(VMTemplateVO template) {
|
||||
private void removeTemplateDetails(VMTemplateVO template) {
|
||||
templateDetailsDao.removeDetails(template.getId());
|
||||
|
||||
if (template.isDeployAsIs()) {
|
||||
|
|
|
|||
|
|
@ -489,7 +489,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||
String mode = cmd.getMode();
|
||||
Long eventId = cmd.getStartEventId();
|
||||
|
||||
return extract(account, templateId, url, zoneId, mode, eventId, true);
|
||||
String extractUrl = extract(account, templateId, url, zoneId, mode, eventId, true);
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, ISO ID: %s", extractUrl, _tmpltDao.findById(templateId).getUuid()));
|
||||
return extractUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -507,7 +509,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||
throw new InvalidParameterValueException("unable to find template with id " + templateId);
|
||||
}
|
||||
|
||||
return extract(caller, templateId, url, zoneId, mode, eventId, false);
|
||||
String extractUrl = extract(caller, templateId, url, zoneId, mode, eventId, false);
|
||||
CallContext.current().setEventDetails(String.format("Download URL: %s, template ID: %s", extractUrl, template.getUuid()));
|
||||
return extractUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -386,6 +386,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
true,
|
||||
ConfigKey.Scope.Domain);
|
||||
|
||||
static ConfigKey<Boolean> userAllowMultipleAccounts = new ConfigKey<>("Advanced",
|
||||
Boolean.class,
|
||||
"user.allow.multiple.accounts",
|
||||
"false",
|
||||
"Determines if the same username can be added to more than one account in the same domain (SAML-only).",
|
||||
true,
|
||||
ConfigKey.Scope.Domain);
|
||||
|
||||
protected AccountManagerImpl() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -1289,7 +1297,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
// Check permissions
|
||||
checkAccess(getCurrentCallingAccount(), domain);
|
||||
|
||||
if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
|
||||
if (!userAllowMultipleAccounts.valueInScope(ConfigKey.Scope.Domain, domainId) && !_userAccountDao.validateUsernameInDomain(userName, domainId)) {
|
||||
throw new InvalidParameterValueException(String.format("The user %s already exists in domain %s", userName, domain));
|
||||
}
|
||||
|
||||
|
|
@ -1477,9 +1485,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
throw new PermissionDeniedException(String.format("Account: %s is a system account, can't add a user to it", account));
|
||||
}
|
||||
|
||||
if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
|
||||
throw new CloudRuntimeException(String.format("The user %s already exists in domain %s", userName, domain));
|
||||
if (!userAllowMultipleAccounts.valueInScope(ConfigKey.Scope.Domain, domainId) && !_userAccountDao.validateUsernameInDomain(userName, domainId)) {
|
||||
throw new CloudRuntimeException("The user " + userName + " already exists in domain " + domainId);
|
||||
}
|
||||
List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName);
|
||||
for (UserVO duplicatedUser : duplicatedUsers) {
|
||||
// users can't exist in same account
|
||||
assertUserNotAlreadyInAccount(duplicatedUser, account);
|
||||
}
|
||||
|
||||
UserVO user;
|
||||
user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source);
|
||||
return user;
|
||||
|
|
@ -1607,7 +1621,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
* <li> The username must be unique in each domain. Therefore, if there is already another user with the same username, an {@link InvalidParameterValueException} is thrown.
|
||||
* </ul>
|
||||
*/
|
||||
protected void validateAndUpdateUsernameIfNeeded(UpdateUserCmd updateUserCmd, UserVO user, Account account) {
|
||||
protected void validateAndUpdateUsernameIfNeeded(UpdateUserCmd updateUserCmd, UserVO newUser, Account newAccount) {
|
||||
String userName = updateUserCmd.getUsername();
|
||||
if (userName == null) {
|
||||
return;
|
||||
|
|
@ -1615,18 +1629,21 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
if (StringUtils.isBlank(userName)) {
|
||||
throw new InvalidParameterValueException("Username cannot be empty.");
|
||||
}
|
||||
List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName);
|
||||
for (UserVO duplicatedUser : duplicatedUsers) {
|
||||
if (duplicatedUser.getId() == user.getId()) {
|
||||
List<UserVO> existingUsers = _userDao.findUsersByName(userName);
|
||||
for (UserVO existingUser : existingUsers) {
|
||||
if (existingUser.getId() == newUser.getId()) {
|
||||
continue;
|
||||
}
|
||||
Account duplicatedUserAccountWithUserThatHasTheSameUserName = _accountDao.findById(duplicatedUser.getAccountId());
|
||||
if (duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId() == account.getDomainId()) {
|
||||
DomainVO domain = _domainDao.findById(duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId());
|
||||
throw new InvalidParameterValueException(String.format("Username (%s) already exists in domain (%s)", duplicatedUser, domain));
|
||||
|
||||
// duplicate usernames cannot exist in same domain unless explicitly configured
|
||||
if (!userAllowMultipleAccounts.valueInScope(ConfigKey.Scope.Domain, newAccount.getDomainId())) {
|
||||
assertUserNotAlreadyInDomain(existingUser, newAccount);
|
||||
}
|
||||
|
||||
// can't rename a username to an existing one in the same account
|
||||
assertUserNotAlreadyInAccount(existingUser, newAccount);
|
||||
}
|
||||
user.setUsername(userName);
|
||||
newUser.setUsername(userName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1895,7 +1912,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
|
||||
// make sure the account is enabled too
|
||||
// if the user is either locked already or disabled already, don't change state...only lock currently enabled
|
||||
// users
|
||||
// users
|
||||
boolean success;
|
||||
if (user.getState().equals(State.LOCKED)) {
|
||||
// already locked...no-op
|
||||
|
|
@ -3408,7 +3425,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
|
||||
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess};
|
||||
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess,
|
||||
userAllowMultipleAccounts};
|
||||
}
|
||||
|
||||
public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
|
||||
|
|
@ -3593,4 +3611,21 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
return userAccountVO;
|
||||
});
|
||||
}
|
||||
|
||||
void assertUserNotAlreadyInAccount(User existingUser, Account newAccount) {
|
||||
System.out.println(existingUser.getAccountId());
|
||||
System.out.println(newAccount.getId());
|
||||
if (existingUser.getAccountId() == newAccount.getId()) {
|
||||
AccountVO existingAccount = _accountDao.findById(newAccount.getId());
|
||||
throw new InvalidParameterValueException(String.format("Username [%s] already exists in account [id=%s,name=%s]", existingUser.getUsername(), existingAccount.getUuid(), existingAccount.getAccountName()));
|
||||
}
|
||||
}
|
||||
|
||||
void assertUserNotAlreadyInDomain(User existingUser, Account originalAccount) {
|
||||
Account existingAccount = _accountDao.findById(existingUser.getAccountId());
|
||||
if (existingAccount.getDomainId() == originalAccount.getDomainId()) {
|
||||
DomainVO existingDomain = _domainDao.findById(existingAccount.getDomainId());
|
||||
throw new InvalidParameterValueException(String.format("Username [%s] already exists in domain [id=%s,name=%s] user account [id=%s,name=%s]", existingUser.getUsername(), existingDomain.getUuid(), existingDomain.getName(), existingAccount.getUuid(), existingAccount.getAccountName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
|
@ -3106,42 +3105,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
}
|
||||
|
||||
boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware);
|
||||
|
||||
if (securityGroupIdList != null && isVMware) {
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
|
||||
} else {
|
||||
// Get default guest network in Basic zone
|
||||
Network defaultNetwork = null;
|
||||
try {
|
||||
DataCenterVO zone = _dcDao.findById(vm.getDataCenterId());
|
||||
if (zone.getNetworkType() == NetworkType.Basic) {
|
||||
// Get default guest network in Basic zone
|
||||
defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId());
|
||||
} else if (_networkModel.checkSecurityGroupSupportForNetwork(_accountMgr.getActiveAccountById(vm.getAccountId()), zone, Collections.emptyList(), securityGroupIdList)) {
|
||||
NicVO defaultNic = _nicDao.findDefaultNicForVM(vm.getId());
|
||||
if (defaultNic != null) {
|
||||
defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
|
||||
}
|
||||
}
|
||||
} catch (InvalidParameterValueException e) {
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(e.getMessage(),e);
|
||||
}
|
||||
defaultNetwork = _networkModel.getDefaultNetworkForVm(id);
|
||||
}
|
||||
|
||||
if (securityGroupIdList != null && _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkModel.canAddDefaultSecurityGroup()) {
|
||||
if (vm.getState() == State.Stopped) {
|
||||
// Remove instance from security groups
|
||||
_securityGroupMgr.removeInstanceFromGroups(vm);
|
||||
// Add instance in provided groups
|
||||
_securityGroupMgr.addInstanceToGroups(vm, securityGroupIdList);
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Virtual machine must be stopped prior to update security groups ");
|
||||
}
|
||||
}
|
||||
}
|
||||
List<? extends Nic> nics = _nicDao.listByVmId(vm.getId());
|
||||
if (hostName != null) {
|
||||
// Check is hostName is RFC compliant
|
||||
|
|
@ -3174,6 +3137,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
.getUuid(), nic.getId(), extraDhcpOptionsMap);
|
||||
}
|
||||
|
||||
checkAndUpdateSecurityGroupForVM(securityGroupIdList, vm, networks);
|
||||
|
||||
_vmDao.updateVM(id, displayName, ha, osTypeId, userData, userDataId,
|
||||
userDataDetails, isDisplayVmEnabled, isDynamicallyScalable,
|
||||
deleteProtection, customId, hostName, instanceName);
|
||||
|
|
@ -3189,6 +3154,48 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
return _vmDao.findById(id);
|
||||
}
|
||||
|
||||
private void checkAndUpdateSecurityGroupForVM(List<Long> securityGroupIdList, UserVmVO vm, List<NetworkVO> networks) {
|
||||
boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware);
|
||||
|
||||
if (securityGroupIdList != null && isVMware) {
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor");
|
||||
} else if (securityGroupIdList != null) {
|
||||
DataCenterVO zone = _dcDao.findById(vm.getDataCenterId());
|
||||
List<Long> networkIds = new ArrayList<>();
|
||||
try {
|
||||
if (zone.getNetworkType() == NetworkType.Basic) {
|
||||
// Get default guest network in Basic zone
|
||||
Network defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId());
|
||||
networkIds.add(defaultNetwork.getId());
|
||||
} else {
|
||||
networkIds = networks.stream().map(Network::getId).collect(Collectors.toList());
|
||||
}
|
||||
} catch (InvalidParameterValueException e) {
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(e.getMessage(),e);
|
||||
}
|
||||
}
|
||||
|
||||
if (_networkModel.checkSecurityGroupSupportForNetwork(
|
||||
_accountMgr.getActiveAccountById(vm.getAccountId()),
|
||||
zone, networkIds, securityGroupIdList)
|
||||
) {
|
||||
updateSecurityGroup(vm, securityGroupIdList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSecurityGroup(UserVmVO vm, List<Long> securityGroupIdList) {
|
||||
if (vm.getState() == State.Stopped) {
|
||||
// Remove instance from security groups
|
||||
_securityGroupMgr.removeInstanceFromGroups(vm);
|
||||
// Add instance in provided groups
|
||||
_securityGroupMgr.addInstanceToGroups(vm, securityGroupIdList);
|
||||
} else {
|
||||
throw new InvalidParameterValueException(String.format("VM %s must be stopped prior to update security groups", vm.getUuid()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateUserData(UserVm vm) throws ResourceUnavailableException, InsufficientCapacityException {
|
||||
boolean result = updateUserDataInternal(vm);
|
||||
if (result) {
|
||||
|
|
@ -3696,7 +3703,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware));
|
||||
|
||||
if (securityGroupIdList != null && isVmWare) {
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor");
|
||||
} else if (!isVmWare && _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkModel.canAddDefaultSecurityGroup()) {
|
||||
//add the default securityGroup only if no security group is specified
|
||||
if (securityGroupIdList == null || securityGroupIdList.isEmpty()) {
|
||||
|
|
@ -3756,7 +3763,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
} else if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) {
|
||||
if (isVmWare) {
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
|
||||
throw new InvalidParameterValueException("Security group feature is not supported for VMware hypervisor");
|
||||
}
|
||||
// Only one network can be specified, and it should be security group enabled
|
||||
if (networkIdList.size() > 1 && template.getHypervisorType() != HypervisorType.KVM && hypervisor != HypervisorType.KVM) {
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public class QueryManagerImplTest {
|
|||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private QueryManagerImpl queryManagerImplSpy = new QueryManagerImpl();
|
||||
private QueryManagerImpl queryManagerImplSpy;
|
||||
|
||||
@Mock
|
||||
EntityManager entityManager;
|
||||
|
|
@ -225,7 +225,7 @@ public class QueryManagerImplTest {
|
|||
Mockito.when(entityManager.findByUuidIncludingRemoved(Network.class, uuid)).thenReturn(network);
|
||||
Mockito.doNothing().when(accountManager).checkAccess(account, SecurityChecker.AccessType.ListEntry, true, network);
|
||||
Mockito.when(eventDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class))).thenReturn(pair);
|
||||
Mockito.when(eventJoinDao.searchByIds(Mockito.any())).thenReturn(eventJoins);
|
||||
Mockito.lenient().when(eventJoinDao.searchByIds(Mockito.any())).thenReturn(eventJoins);
|
||||
List<EventResponse> respList = new ArrayList<EventResponse>();
|
||||
for (EventJoinVO vt : eventJoins) {
|
||||
respList.add(eventJoinDao.newEventResponse(vt));
|
||||
|
|
|
|||
|
|
@ -118,6 +118,11 @@ import com.cloud.vm.dao.NicDao;
|
|||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class NetworkServiceImplTest {
|
||||
@Mock
|
||||
Object job;
|
||||
@Mock
|
||||
Object _responseObject;
|
||||
|
||||
@Mock
|
||||
AccountManager accountManager;
|
||||
@Mock
|
||||
|
|
@ -141,11 +146,11 @@ public class NetworkServiceImplTest {
|
|||
@Mock
|
||||
VpcManager vpcMgr;
|
||||
@Mock
|
||||
NetworkOrchestrationService networkManager;
|
||||
NetworkOrchestrationService _networkMgr;
|
||||
@Mock
|
||||
AlertManager alertManager;
|
||||
@Mock
|
||||
DataCenterDao dcDao;
|
||||
DataCenterDao _dcDao;
|
||||
@Mock
|
||||
UserDao userDao;
|
||||
@Mock
|
||||
|
|
@ -165,7 +170,7 @@ public class NetworkServiceImplTest {
|
|||
@Mock
|
||||
DomainRouterDao routerDao;
|
||||
@Mock
|
||||
AccountService accountService;
|
||||
AccountService _accountService;
|
||||
@Mock
|
||||
NetworkHelper networkHelper;
|
||||
@Mock
|
||||
|
|
@ -192,7 +197,7 @@ public class NetworkServiceImplTest {
|
|||
@Mock
|
||||
private DomainVO domainVOMock;
|
||||
@InjectMocks
|
||||
NetworkServiceImpl service = new NetworkServiceImpl();
|
||||
NetworkServiceImpl service;
|
||||
|
||||
@Mock
|
||||
DomainDao domainDaoMock;
|
||||
|
|
@ -297,14 +302,11 @@ public class NetworkServiceImplTest {
|
|||
vpc = Mockito.mock(VpcVO.class);
|
||||
service._networkOfferingDao = networkOfferingDao;
|
||||
service._physicalNetworkDao = physicalNetworkDao;
|
||||
service._dcDao = dcDao;
|
||||
service._accountMgr = accountManager;
|
||||
service._networkMgr = networkManager;
|
||||
service.alertManager = alertManager;
|
||||
service._configMgr = configMgr;
|
||||
service._vpcDao = vpcDao;
|
||||
service._vpcMgr = vpcMgr;
|
||||
service._accountService = accountService;
|
||||
service._networksDao = networkDao;
|
||||
service._nicDao = nicDao;
|
||||
service._ipAddressDao = ipAddressDao;
|
||||
|
|
@ -323,7 +325,7 @@ public class NetworkServiceImplTest {
|
|||
Mockito.when(entityMgr.findById(NetworkOffering.class, 1L)).thenReturn(networkOffering);
|
||||
Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering);
|
||||
Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet);
|
||||
Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc);
|
||||
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dc);
|
||||
Mockito.when(accountManager.isRootAdmin(accountMock.getId())).thenReturn(true);
|
||||
}
|
||||
|
||||
|
|
@ -442,12 +444,12 @@ public class NetworkServiceImplTest {
|
|||
Mockito.when(dc.getId()).thenReturn(1L);
|
||||
Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||
Map<String, String> networkProvidersMap = new HashMap<String, String>();
|
||||
Mockito.when(networkManager.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap);
|
||||
Mockito.when(_networkMgr.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap);
|
||||
Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(false);
|
||||
Mockito.when(offering.isInternalLb()).thenReturn(false);
|
||||
|
||||
service.createGuestNetwork(createNetworkCmd);
|
||||
Mockito.verify(networkManager, times(1)).createGuestNetwork(1L, "testNetwork", "Test Network", null,
|
||||
Mockito.verify(_networkMgr, times(1)).createGuestNetwork(1L, "testNetwork", "Test Network", null,
|
||||
null, null, false, null, accountMock, null, phyNet,
|
||||
1L, null, null, null, null, null,
|
||||
true, null, null, null, null, null,
|
||||
|
|
@ -769,7 +771,7 @@ public class NetworkServiceImplTest {
|
|||
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
when(cmd.getZoneId()).thenReturn(zoneId);
|
||||
when(dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(_dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(zone.getId()).thenReturn(zoneId);
|
||||
|
||||
try {
|
||||
|
|
@ -795,7 +797,7 @@ public class NetworkServiceImplTest {
|
|||
ReflectionTestUtils.setField(createNetworkCmd, "vpcId", vpcId);
|
||||
|
||||
dc = Mockito.mock(DataCenterVO.class);
|
||||
Mockito.when(dcDao.findById(zoneId)).thenReturn(dc);
|
||||
Mockito.when(_dcDao.findById(zoneId)).thenReturn(dc);
|
||||
Mockito.when(dc.getId()).thenReturn(zoneId);
|
||||
vpc = Mockito.mock(VpcVO.class);
|
||||
Mockito.when(vpc.getName()).thenReturn("Vpc 1");
|
||||
|
|
@ -831,7 +833,7 @@ public class NetworkServiceImplTest {
|
|||
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
when(cmd.getZoneId()).thenReturn(zoneId);
|
||||
when(dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(_dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(zone.getId()).thenReturn(zoneId);
|
||||
|
||||
try {
|
||||
|
|
@ -859,7 +861,7 @@ public class NetworkServiceImplTest {
|
|||
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
when(cmd.getZoneId()).thenReturn(zoneId);
|
||||
when(dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(_dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(zone.getId()).thenReturn(zoneId);
|
||||
|
||||
try {
|
||||
|
|
@ -889,7 +891,7 @@ public class NetworkServiceImplTest {
|
|||
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
when(cmd.getZoneId()).thenReturn(zoneId);
|
||||
when(dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(_dcDao.findById(zoneId)).thenReturn(zone);
|
||||
when(zone.getId()).thenReturn(zoneId);
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,9 @@ public class VirtualRouterElementTest {
|
|||
@Mock private RemoteAccessVpnDao _vpnDao;
|
||||
@Mock private VpnUserDao _vpnUsersDao;
|
||||
@Mock private VirtualRouterProviderDao _vrProviderDao;
|
||||
@Mock private LoadBalancerDao loadBalancerDao;
|
||||
@Mock private LoadBalancerDao _lbDao;
|
||||
@Mock private NetworkDao networkDao;
|
||||
@Mock private NetworkDao _networksDao;
|
||||
@Mock private OvsProviderDao _ovsProviderDao;
|
||||
|
||||
|
|
@ -180,6 +182,7 @@ public class VirtualRouterElementTest {
|
|||
|
||||
@Mock private AccountManager _accountMgr;
|
||||
@Mock private ConfigurationManager _configMgr;
|
||||
@Mock private NetworkModel networkModel;
|
||||
@Mock private NetworkModel _networkMdl;
|
||||
@Mock private NetworkOrchestrationService _networkMgr;
|
||||
@Mock private ResourceManager _resourceMgr;
|
||||
|
|
|
|||
|
|
@ -86,13 +86,11 @@ import com.cloud.vm.dao.UserVmDao;
|
|||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vpc.MockResourceLimitManagerImpl;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ResourceLimitManagerImplTest extends TestCase {
|
||||
public class ResourceLimitManagerImplTest {
|
||||
private Logger logger = LogManager.getLogger(ResourceLimitManagerImplTest.class);
|
||||
|
||||
MockResourceLimitManagerImpl _resourceLimitService = new MockResourceLimitManagerImpl();
|
||||
|
|
@ -142,14 +140,11 @@ public class ResourceLimitManagerImplTest extends TestCase {
|
|||
f.set(configKey, o);
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
try {
|
||||
overrideDefaultConfigValue(ResourceLimitService.ResourceLimitHostTags, "_defaultValue", StringUtils.join(hostTags, ","));
|
||||
overrideDefaultConfigValue(ResourceLimitService.ResourceLimitStorageTags, "_defaultValue", StringUtils.join(storageTags, ","));
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
logger.error("Failed to update configurations");
|
||||
}
|
||||
public void setUp() throws Exception {
|
||||
overrideDefaultConfigValue(ResourceLimitService.ResourceLimitHostTags, "_defaultValue", StringUtils.join(hostTags, ","));
|
||||
overrideDefaultConfigValue(ResourceLimitService.ResourceLimitStorageTags, "_defaultValue", StringUtils.join(storageTags, ","));
|
||||
|
||||
Account account = mock(Account.class);
|
||||
User user = mock(User.class);
|
||||
|
|
@ -288,6 +283,7 @@ public class ResourceLimitManagerImplTest extends TestCase {
|
|||
Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0));
|
||||
Account account = Mockito.mock(Account.class);
|
||||
try {
|
||||
Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any());
|
||||
Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
|
||||
resourceLimitManager.checkVmResourceLimit(account, true, serviceOffering, template);
|
||||
List<String> tags = new ArrayList<>();
|
||||
|
|
@ -347,6 +343,7 @@ public class ResourceLimitManagerImplTest extends TestCase {
|
|||
Mockito.when(diskOffering.getTagsArray()).thenReturn(new String[]{checkTag});
|
||||
Account account = Mockito.mock(Account.class);
|
||||
try {
|
||||
Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any());
|
||||
Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
|
||||
resourceLimitManager.checkVolumeResourceLimit(account, true, 100L, diskOffering);
|
||||
List<String> tags = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -235,7 +235,6 @@ public class SnapshotManagerTest {
|
|||
|
||||
doNothing().when(_resourceLimitMgr).checkResourceLimit(any(Account.class), any(ResourceType.class));
|
||||
doNothing().when(_resourceLimitMgr).checkResourceLimit(any(Account.class), any(ResourceType.class), anyLong());
|
||||
doNothing().when(_resourceLimitMgr).decrementResourceCount(anyLong(), any(ResourceType.class), anyLong());
|
||||
doNothing().when(_resourceLimitMgr).incrementResourceCount(anyLong(), any(ResourceType.class));
|
||||
doNothing().when(_resourceLimitMgr).incrementResourceCount(anyLong(), any(ResourceType.class), anyLong());
|
||||
|
||||
|
|
@ -352,7 +351,6 @@ public class SnapshotManagerTest {
|
|||
when(_vmDao.findById(anyLong())).thenReturn(vmMock);
|
||||
when(vmMock.getState()).thenReturn(State.Stopped);
|
||||
when (snapshotStrategy.revertSnapshot(any(SnapshotInfo.class))).thenReturn(true);
|
||||
when(_volumeDao.update(anyLong(), any(VolumeVO.class))).thenReturn(true);
|
||||
doReturn(DataStoreRole.Image).when(snapshotHelperMock).getDataStoreRole(any());
|
||||
Snapshot snapshot = _snapshotMgr.revertSnapshot(TEST_SNAPSHOT_ID);
|
||||
Assert.assertNotNull(snapshot);
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||
|
||||
@Mock
|
||||
private AccountVO accountVoMock;
|
||||
@Mock
|
||||
private AccountVO _systemAccount;
|
||||
|
||||
@Mock
|
||||
private ProjectAccountVO projectAccountVO;
|
||||
|
|
@ -1406,4 +1408,75 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||
Assert.assertNull(updatedUser.getUser2faProvider());
|
||||
Assert.assertNull(updatedUser.getKeyFor2fa());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testAssertUserNotAlreadyInAccount_UserExistsInAccount() {
|
||||
User existingUser = new UserVO();
|
||||
existingUser.setUsername("testuser");
|
||||
existingUser.setAccountId(1L);
|
||||
|
||||
Account newAccount = Mockito.mock(Account.class);
|
||||
Mockito.when(newAccount.getId()).thenReturn(1L);
|
||||
|
||||
AccountVO existingAccount = Mockito.mock(AccountVO.class);
|
||||
Mockito.when(existingAccount.getUuid()).thenReturn("existing-account-uuid");
|
||||
Mockito.when(existingAccount.getAccountName()).thenReturn("existing-account");
|
||||
|
||||
Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
|
||||
|
||||
accountManagerImpl.assertUserNotAlreadyInAccount(existingUser, newAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertUserNotAlreadyInAccount_UserExistsInDiffAccount() {
|
||||
User existingUser = new UserVO();
|
||||
existingUser.setUsername("testuser");
|
||||
existingUser.setAccountId(2L);
|
||||
|
||||
Account newAccount = Mockito.mock(Account.class);
|
||||
Mockito.when(newAccount.getId()).thenReturn(1L);
|
||||
|
||||
accountManagerImpl.assertUserNotAlreadyInAccount(existingUser, newAccount);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testAssertUserNotAlreadyInDomain_UserExistsInDomain() {
|
||||
User existingUser = new UserVO();
|
||||
existingUser.setUsername("testuser");
|
||||
existingUser.setAccountId(1L);
|
||||
|
||||
Account originalAccount = Mockito.mock(Account.class);
|
||||
Mockito.when(originalAccount.getDomainId()).thenReturn(1L);
|
||||
|
||||
AccountVO existingAccount = Mockito.mock(AccountVO.class);
|
||||
Mockito.when(existingAccount.getDomainId()).thenReturn(1L);
|
||||
Mockito.when(existingAccount.getUuid()).thenReturn("existing-account-uuid");
|
||||
Mockito.when(existingAccount.getAccountName()).thenReturn("existing-account");
|
||||
|
||||
DomainVO existingDomain = Mockito.mock(DomainVO.class);
|
||||
Mockito.when(existingDomain.getUuid()).thenReturn("existing-domain-uuid");
|
||||
Mockito.when(existingDomain.getName()).thenReturn("existing-domain");
|
||||
|
||||
Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
|
||||
Mockito.when(_domainDao.findById(1L)).thenReturn(existingDomain);
|
||||
|
||||
accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertUserNotAlreadyInDomain_UserExistsInDiffDomain() {
|
||||
User existingUser = new UserVO();
|
||||
existingUser.setUsername("testuser");
|
||||
existingUser.setAccountId(1L);
|
||||
|
||||
Account originalAccount = Mockito.mock(Account.class);
|
||||
Mockito.when(originalAccount.getDomainId()).thenReturn(1L);
|
||||
|
||||
AccountVO existingAccount = Mockito.mock(AccountVO.class);
|
||||
Mockito.when(existingAccount.getDomainId()).thenReturn(2L);
|
||||
|
||||
Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
|
||||
|
||||
accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
|
@ -150,7 +150,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
|
|||
|
||||
lenient().when(_domainMgr.getDomain(nullable(Long.class))).thenReturn(domain);
|
||||
|
||||
Mockito.doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class));
|
||||
lenient().doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ public class SnapshotHelperTest {
|
|||
|
||||
@Test (expected = CloudRuntimeException.class)
|
||||
public void validateThrowCloudRuntimeExceptionOfSnapshotsOnlyInPrimaryStorage() {
|
||||
Mockito.doReturn(new ArrayList<>()).when(snapshotDaoMock).listByIds(Mockito.any());
|
||||
Mockito.lenient().doReturn(new ArrayList<>()).when(snapshotDaoMock).listByIds(Mockito.any());
|
||||
snapshotHelperSpy.throwCloudRuntimeExceptionOfSnapshotsOnlyInPrimaryStorage(null, new HashSet<>());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import org.apache.logging.log4j.Logger;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
|
@ -104,7 +103,7 @@ public class NfsSecondaryStorageResourceTest {
|
|||
@Test
|
||||
public void testCleanupStagingNfs() throws Exception{
|
||||
|
||||
NfsSecondaryStorageResource spyResource = spy(resource);
|
||||
NfsSecondaryStorageResource spyResource = resource;
|
||||
spyResource.logger = loggerMock;
|
||||
RuntimeException exception = new RuntimeException();
|
||||
doThrow(exception).when(spyResource).execute(any(DeleteCommand.class));
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class CsDhcp(CsDataBag):
|
|||
CsHelper.service("dnsmasq", "reload")
|
||||
|
||||
def configure_server(self):
|
||||
# self.conf.addeq("dhcp-hostsfile=%s" % DHCP_HOSTS)
|
||||
self.conf.add("bind-interfaces", 0)
|
||||
idx = 0
|
||||
listen_address = ["127.0.0.1"]
|
||||
for i in self.devinfo:
|
||||
|
|
|
|||
|
|
@ -2878,7 +2878,7 @@
|
|||
"message.attach.volume.progress": "Attaching volume",
|
||||
"message.attach.volume.success": "Successfully attached the volume to the instance",
|
||||
"message.authorization.failed": "Session expired, authorization verification failed.",
|
||||
"message.autoscale.loadbalancer.update": "The load balancer rule can be updated only when autoscaling group is DISABLED.",
|
||||
"message.autoscale.loadbalancer.update": "The load balancer rule can be updated. However, instance can be removed only when autoscaling group is DISABLED.",
|
||||
"message.autoscale.policies.update": "The scale up/down policies can be updated only when autoscaling group is DISABLED.",
|
||||
"message.autoscale.vm.networks": "Please choose at least one Network for Instances in the autoscaling group. The default Network must be an Isolated Network or VPC Network Tier which supports Instance AutoScaling and has load balancing rules.",
|
||||
"message.autoscale.vmprofile.update": "The autoscale Instance profile can be updated only when autoscaling group is DISABLED.",
|
||||
|
|
@ -2960,6 +2960,7 @@
|
|||
"message.confirm.remove.vmware.datacenter": "Please confirm you want to remove VMware datacenter.",
|
||||
"message.confirm.remove.vpc.offering": "Are you sure you want to remove this VPC offering?",
|
||||
"message.confirm.replace.acl.new.one": "Do you want to replace the ACL with a new one?",
|
||||
"message.confirm.reset.configuration.value": "Are you sure you want reset configuration - %x?",
|
||||
"message.confirm.reset.network.permissions": "Are you sure you want to reset this Network permissions?",
|
||||
"message.confirm.scale.up.router.vm": "Do you really want to scale up the router Instance?",
|
||||
"message.confirm.scale.up.system.vm": "Do you really want to scale up the system VM?",
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ export default {
|
|||
this.showSwitcher = false
|
||||
return
|
||||
}
|
||||
this.samlAccounts = samlAccounts
|
||||
this.samlAccounts = _.orderBy(samlAccounts, ['domainPath'], ['asc'])
|
||||
const currentAccount = this.samlAccounts.filter(x => {
|
||||
return x.userId === store.getters.userInfo.id
|
||||
|
|
@ -109,6 +110,8 @@ export default {
|
|||
this.$message.success(`Switched to "${account.accountName} (${account.domainPath})"`)
|
||||
this.$router.go()
|
||||
})
|
||||
}).else(error => {
|
||||
console.log('error refreshing with new user context: ' + error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -673,7 +673,7 @@
|
|||
<span v-else>{{ resource.podname || resource.pod || resource.podid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.zoneid">
|
||||
<div class="resource-detail-item" v-if="resource.zoneid && !['template', 'iso'].includes($route.path.split('/')[1])">
|
||||
<div class="resource-detail-item__label">{{ $t('label.zone') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.zone">
|
||||
|
|
@ -760,7 +760,7 @@
|
|||
<span v-else>{{ resource.managementserver || resource.managementserverid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.created">
|
||||
<div class="resource-detail-item" v-if="resource.created && !['template', 'iso'].includes($route.path.split('/')[1])">
|
||||
<div class="resource-detail-item__label">{{ $t('label.created') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<calendar-outlined />{{ $toLocaleDate(resource.created) }}
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@
|
|||
iconTwoToneColor="#52c41a" />
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.reset.config.value')"
|
||||
@onClick="resetConfig(record)"
|
||||
@onClick="$resetConfigurationValueConfirm(item, resetConfig)"
|
||||
v-if="editableValueKey !== record.key"
|
||||
icon="reload-outlined"
|
||||
:disabled="!('updateConfiguration' in $store.getters.apis)" />
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ import {
|
|||
resourceTypePlugin,
|
||||
fileSizeUtilPlugin,
|
||||
genericUtilPlugin,
|
||||
localesPlugin
|
||||
localesPlugin,
|
||||
dialogUtilPlugin
|
||||
} from './utils/plugins'
|
||||
import { VueAxios } from './utils/request'
|
||||
import directives from './utils/directives'
|
||||
|
|
@ -51,6 +52,7 @@ vueApp.use(resourceTypePlugin)
|
|||
vueApp.use(fileSizeUtilPlugin)
|
||||
vueApp.use(localesPlugin)
|
||||
vueApp.use(genericUtilPlugin)
|
||||
vueApp.use(dialogUtilPlugin)
|
||||
vueApp.use(extensions)
|
||||
vueApp.use(directives)
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ const user = {
|
|||
commit('SET_MS_ID', msId)
|
||||
|
||||
// Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded
|
||||
api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
|
||||
api('listUsers', { id: Cookies.get('userid'), listall: true }).then(response => {
|
||||
const result = response.listusersresponse.user[0]
|
||||
commit('SET_INFO', result)
|
||||
commit('SET_NAME', result.firstname + ' ' + result.lastname)
|
||||
|
|
@ -404,7 +404,7 @@ const user = {
|
|||
}).catch(ignored => {})
|
||||
}
|
||||
|
||||
api('listUsers', { username: Cookies.get('username') }).then(response => {
|
||||
api('listUsers', { id: Cookies.get('userid') }).then(response => {
|
||||
const result = response.listusersresponse.user[0]
|
||||
commit('SET_INFO', result)
|
||||
commit('SET_NAME', result.firstname + ' ' + result.lastname)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
import _ from 'lodash'
|
||||
import { i18n } from '@/locales'
|
||||
import { api } from '@/api'
|
||||
import { message, notification } from 'ant-design-vue'
|
||||
import { message, notification, Modal } from 'ant-design-vue'
|
||||
import eventBus from '@/config/eventBus'
|
||||
import store from '@/store'
|
||||
import { sourceToken } from '@/utils/request'
|
||||
|
|
@ -526,3 +526,18 @@ export function createPathBasedOnVmType (vmtype, virtualmachineid) {
|
|||
|
||||
return path + virtualmachineid
|
||||
}
|
||||
|
||||
export const dialogUtilPlugin = {
|
||||
install (app) {
|
||||
app.config.globalProperties.$resetConfigurationValueConfirm = function (configRecord, callback) {
|
||||
Modal.confirm({
|
||||
title: i18n.global.t('label.reset.config.value'),
|
||||
content: `${i18n.global.t('message.confirm.reset.configuration.value').replace('%x', configRecord.name)}`,
|
||||
okText: i18n.global.t('label.yes'),
|
||||
cancelText: i18n.global.t('label.no'),
|
||||
okType: 'primary',
|
||||
onOk: () => callback(configRecord)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ export default {
|
|||
zoneid: this.resource.zoneid
|
||||
}).then(response => {
|
||||
const zone = response?.listzonesresponse?.zone || []
|
||||
this.securityGroupsEnabled = zone?.[0]?.securitygroupsenabled
|
||||
this.securityGroupsEnabled = zone?.[0]?.securitygroupsenabled || this.$store.getters.showSecurityGroups
|
||||
})
|
||||
},
|
||||
fetchSecurityGroups () {
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ export default {
|
|||
vm: {},
|
||||
totalStorage: 0,
|
||||
currentTab: 'details',
|
||||
showUpdateSecurityGroupsModal: false,
|
||||
showAddVolumeModal: false,
|
||||
diskOfferings: [],
|
||||
annotations: [],
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@
|
|||
<span v-if="record.isready">{{ $t('label.yes') }}</span>
|
||||
<span v-else>{{ $t('label.no') }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'created'">
|
||||
<span v-if="record.created">{{ $toLocaleDate(record.created) }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'actions'">
|
||||
<span style="margin-right: 5px">
|
||||
<tooltip-button
|
||||
|
|
@ -262,6 +265,11 @@ export default {
|
|||
title: this.$t('label.zonename'),
|
||||
dataIndex: 'zonename'
|
||||
},
|
||||
{
|
||||
key: 'created',
|
||||
title: this.$t('label.created'),
|
||||
dataIndex: 'created'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.status'),
|
||||
dataIndex: 'status'
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@
|
|||
<span v-if="record.isready">{{ $t('label.yes') }}</span>
|
||||
<span v-else>{{ $t('label.no') }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'created'">
|
||||
<span v-if="record.created">{{ $toLocaleDate(record.created) }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'actions'">
|
||||
<tooltip-button
|
||||
style="margin-right: 5px"
|
||||
|
|
@ -308,6 +311,11 @@ export default {
|
|||
title: this.$t('label.zonename'),
|
||||
dataIndex: 'zonename'
|
||||
},
|
||||
{
|
||||
key: 'created',
|
||||
title: this.$t('label.created'),
|
||||
dataIndex: 'created'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.status'),
|
||||
dataIndex: 'status'
|
||||
|
|
|
|||
|
|
@ -703,6 +703,7 @@ export default {
|
|||
title = this.$t('label.view')
|
||||
break
|
||||
case 'virtualmachinename':
|
||||
title = this.$t('label.virtualmachinename')
|
||||
dataIndex = 'name'
|
||||
break
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -449,12 +449,16 @@ export default {
|
|||
services.push({
|
||||
name: 'Dhcp',
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' }
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'ConfigDrive' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Dns',
|
||||
provider: [{ name: 'VpcVirtualRouter' }]
|
||||
provider: [
|
||||
{ name: 'VpcVirtualRouter' },
|
||||
{ name: 'ConfigDrive' }
|
||||
]
|
||||
})
|
||||
services.push({
|
||||
name: 'Lb',
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
:disabled="valueLoading" />
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.reset.config.value')"
|
||||
@onClick="resetConfigurationValue(configrecord)"
|
||||
@onClick="$resetConfigurationValueConfirm(configrecord, resetConfigurationValue)"
|
||||
v-if="editableValueKey === null"
|
||||
icon="reload-outlined"
|
||||
:disabled="(!('resetConfiguration' in $store.getters.apis) || configDisabled || valueLoading)" />
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ import {
|
|||
showIconPlugin,
|
||||
resourceTypePlugin,
|
||||
fileSizeUtilPlugin,
|
||||
genericUtilPlugin
|
||||
genericUtilPlugin,
|
||||
dialogUtilPlugin
|
||||
} from '@/utils/plugins'
|
||||
|
||||
function createMockRouter (newRoutes = []) {
|
||||
|
|
@ -88,6 +89,7 @@ function createFactory (component, options) {
|
|||
resourceTypePlugin,
|
||||
fileSizeUtilPlugin,
|
||||
genericUtilPlugin,
|
||||
dialogUtilPlugin,
|
||||
StoragePlugin
|
||||
],
|
||||
mocks
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ const vueConfig = {
|
|||
ws: false,
|
||||
changeOrigin: true,
|
||||
proxyTimeout: 10 * 60 * 1000, // 10 minutes
|
||||
cookieDomainRewrite: '*',
|
||||
cookieDomainRewrite: process.env.CS_COOKIE_HOST || 'localhost',
|
||||
cookiePathRewrite: {
|
||||
'/client': '/'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.security.KeyManagementException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
|
@ -36,7 +37,9 @@ import javax.net.ssl.SSLContext;
|
|||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.nio.TrustAllManager;
|
||||
import com.google.gson.JsonElement;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
|
|
@ -75,14 +78,15 @@ public class RedfishClient {
|
|||
private boolean ignoreSsl;
|
||||
private int redfishRequestMaxRetries;
|
||||
|
||||
private final static String SYSTEMS_URL_PATH = "redfish/v1/Systems/";
|
||||
private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "/Actions/ComputerSystem.Reset";
|
||||
private final static String SYSTEMS_URL_PATH = "redfish/v1/Systems";
|
||||
private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "Actions/ComputerSystem.Reset";
|
||||
private final static String REDFISH_RESET_TYPE = "ResetType";
|
||||
private final static String POWER_STATE = "PowerState";
|
||||
private final static String APPLICATION_JSON = "application/json";
|
||||
private final static String ACCEPT = "accept";
|
||||
private final static String ODATA_ID = "@odata.id";
|
||||
private final static String MEMBERS = "Members";
|
||||
private final static String LINKS = "Links";
|
||||
private final static String EXPECTED_HTTP_STATUS = "2XX";
|
||||
private final static int WAIT_FOR_REQUEST_RETRY = 2;
|
||||
|
||||
|
|
@ -265,12 +269,12 @@ public class RedfishClient {
|
|||
if (StringUtils.isBlank(resourceId)) {
|
||||
throw new RedfishException(String.format("Command '%s' requires a valid resource ID '%s'.", cmd, resourceId));
|
||||
}
|
||||
return String.format("%s%s", SYSTEMS_URL_PATH, resourceId);
|
||||
return String.format("%s/%s", SYSTEMS_URL_PATH, resourceId);
|
||||
case ComputerSystemReset:
|
||||
if (StringUtils.isBlank(resourceId)) {
|
||||
throw new RedfishException(String.format("Command '%s' requires a valid resource ID '%s'.", cmd, resourceId));
|
||||
}
|
||||
return String.format("%s%s%s", SYSTEMS_URL_PATH, resourceId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
return String.format("%s/%s/%s", SYSTEMS_URL_PATH, resourceId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
default:
|
||||
throw new RedfishException(String.format("Redfish client does not support command '%s'.", cmd));
|
||||
}
|
||||
|
|
@ -305,8 +309,8 @@ public class RedfishClient {
|
|||
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
|
||||
throw new RedfishException(String.format("Failed to get System power state for host '%s' with request '%s: %s'. The expected HTTP status code is '%s' but it got '%s'.",
|
||||
HttpGet.METHOD_NAME, url, hostAddress, EXPECTED_HTTP_STATUS, statusCode));
|
||||
throw new RedfishException(String.format("Failed to execute System power command for host by performing '%s' request on URL '%s' and host address '%s'. The expected HTTP status code is '%s' but it got '%s'.",
|
||||
HttpPost.METHOD_NAME, url, hostAddress, EXPECTED_HTTP_STATUS, statusCode));
|
||||
}
|
||||
logger.debug(String.format("Sending ComputerSystem.Reset Command '%s' to host '%s' with request '%s %s'", resetCommand, hostAddress, HttpPost.METHOD_NAME, url));
|
||||
}
|
||||
|
|
@ -340,16 +344,25 @@ public class RedfishClient {
|
|||
try {
|
||||
in = response.getEntity().getContent();
|
||||
BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
jsonString = streamReader.readLine();
|
||||
jsonString = streamReader.lines().collect(Collectors.joining());
|
||||
} catch (UnsupportedOperationException | IOException e) {
|
||||
throw new RedfishException("Failed to process system Response", e);
|
||||
}
|
||||
|
||||
// retrieving the system ID (e.g. 'System.Embedded.1') via JsonParser:
|
||||
// (...) Members":[{"@odata.id":"/redfish/v1/Systems/System.Embedded.1"}] (...)
|
||||
JsonArray jArray = new JsonParser().parse(jsonString).getAsJsonObject().get(MEMBERS).getAsJsonArray();
|
||||
JsonObject jsonnObject = jArray.get(0).getAsJsonObject();
|
||||
String jsonObjectAsString = jsonnObject.get(ODATA_ID).getAsString();
|
||||
JsonArray jArray = null;
|
||||
JsonElement jsonElement = new JsonParser().parse(jsonString);
|
||||
if (jsonElement.getAsJsonObject().get(MEMBERS) != null) {
|
||||
jArray = jsonElement.getAsJsonObject().get(MEMBERS).getAsJsonArray();
|
||||
} else if (jsonElement.getAsJsonObject().get(LINKS) != null){
|
||||
jArray = jsonElement.getAsJsonObject().get(LINKS).getAsJsonObject().get(MEMBERS).getAsJsonArray();
|
||||
}
|
||||
if (jArray == null || jArray.size() < 1) {
|
||||
throw new CloudRuntimeException("Members not found in the Redfish Systems JSON, unable to determine Redfish system ID");
|
||||
}
|
||||
JsonObject jsonObject = jArray.get(0).getAsJsonObject();
|
||||
String jsonObjectAsString = jsonObject.get(ODATA_ID).getAsString();
|
||||
String[] arrayOfStrings = StringUtils.split(jsonObjectAsString, '/');
|
||||
|
||||
return arrayOfStrings[arrayOfStrings.length - 1];
|
||||
|
|
@ -384,8 +397,7 @@ public class RedfishClient {
|
|||
try {
|
||||
in = response.getEntity().getContent();
|
||||
BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
|
||||
jsonString = streamReader.readLine();
|
||||
jsonString = streamReader.lines().collect(Collectors.joining());
|
||||
String powerState = new JsonParser().parse(jsonString).getAsJsonObject().get(POWER_STATE).getAsString();
|
||||
return RedfishPowerState.valueOf(powerState);
|
||||
} catch (UnsupportedOperationException | IOException e) {
|
||||
|
|
|
|||
|
|
@ -276,8 +276,8 @@ public class UriUtilsTest {
|
|||
|
||||
@Test
|
||||
public void validateUrl() {
|
||||
Pair<String, Integer> url1 = UriUtils.validateUrl("https://www.cloudstack.org");
|
||||
Assert.assertEquals(url1.first(), "www.cloudstack.org");
|
||||
Pair<String, Integer> url1 = UriUtils.validateUrl("https://cloudstack.apache.org/");
|
||||
Assert.assertEquals(url1.first(), "cloudstack.apache.org");
|
||||
|
||||
Pair<String, Integer> url2 = UriUtils.validateUrl("https://www.apache.org");
|
||||
Assert.assertEquals(url2.first(), "www.apache.org");
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ public class RedfishClientTest {
|
|||
private static final String PASSWORD = "password";
|
||||
private static final String oobAddress = "oob.host.address";
|
||||
private static final String systemId = "SystemID.1";
|
||||
private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "/Actions/ComputerSystem.Reset";
|
||||
private final static String COMPUTER_SYSTEM_RESET_URL_PATH = "Actions/ComputerSystem.Reset";
|
||||
private final static Integer REDFISHT_REQUEST_RETRIES = Integer.valueOf(2);
|
||||
private static final String url = "https://address.system.net/redfish/v1/Systems/";
|
||||
private static final String url = "https://address.system.net/redfish/v1/Systems";
|
||||
private static final HttpRequestBase httpReq = new HttpGet(url);
|
||||
|
||||
@Mock
|
||||
|
|
@ -87,7 +87,7 @@ public class RedfishClientTest {
|
|||
public void buildRequestUrlTestHttpsGetSystemId() {
|
||||
RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, true, false, REDFISHT_REQUEST_RETRIES);
|
||||
String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.GetSystemId, systemId);
|
||||
String expected = String.format("https://%s/redfish/v1/Systems/", oobAddress);
|
||||
String expected = String.format("https://%s/redfish/v1/Systems", oobAddress);
|
||||
Assert.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ public class RedfishClientTest {
|
|||
public void buildRequestUrlTestGetSystemId() {
|
||||
RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, false, false, REDFISHT_REQUEST_RETRIES);
|
||||
String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.GetSystemId, systemId);
|
||||
String expected = String.format("http://%s/redfish/v1/Systems/", oobAddress);
|
||||
String expected = String.format("http://%s/redfish/v1/Systems", oobAddress);
|
||||
Assert.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ public class RedfishClientTest {
|
|||
public void buildRequestUrlTestHttpsComputerSystemReset() {
|
||||
RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, true, false, REDFISHT_REQUEST_RETRIES);
|
||||
String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.ComputerSystemReset, systemId);
|
||||
String expected = String.format("https://%s/redfish/v1/Systems/%s%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
String expected = String.format("https://%s/redfish/v1/Systems/%s/%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
Assert.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ public class RedfishClientTest {
|
|||
public void buildRequestUrlTestComputerSystemReset() {
|
||||
RedfishClient redfishclient = new RedfishClient(USERNAME, PASSWORD, false, false, REDFISHT_REQUEST_RETRIES);
|
||||
String result = redfishclient.buildRequestUrl(oobAddress, RedfishClient.RedfishCmdType.ComputerSystemReset, systemId);
|
||||
String expected = String.format("http://%s/redfish/v1/Systems/%s%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
String expected = String.format("http://%s/redfish/v1/Systems/%s/%s", oobAddress, systemId, COMPUTER_SYSTEM_RESET_URL_PATH);
|
||||
Assert.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue