mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge remote-tracking branch 'origin/4.15'
This commit is contained in:
commit
1e859a0e2c
2
debian/control
vendored
2
debian/control
vendored
@ -15,7 +15,7 @@ Description: A common package which contains files which are shared by several C
|
||||
|
||||
Package: cloudstack-management
|
||||
Architecture: all
|
||||
Depends: ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), sudo, python3-mysql.connector, augeas-tools, mysql-client | mariadb-client, adduser, bzip2, ipmitool, file, gawk, iproute2, qemu-utils, python3-dnspython, lsb-release, init-system-helpers (>= 1.14~), python3-setuptools
|
||||
Depends: ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), net-tools, sudo, python3-mysql.connector, augeas-tools, mysql-client | mariadb-client, adduser, bzip2, ipmitool, file, gawk, iproute2, qemu-utils, python3-dnspython, lsb-release, init-system-helpers (>= 1.14~), python3-setuptools
|
||||
Conflicts: cloud-server, cloud-client, cloud-client-ui
|
||||
Description: CloudStack server library
|
||||
The CloudStack management server
|
||||
|
||||
@ -3110,7 +3110,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
s_logger.warn("No recommended value found for dynamic max, setting static max and dynamic max equal");
|
||||
return dynamicMaxRam;
|
||||
}
|
||||
final long staticMax = Math.min(recommendedValue, 4l * dynamicMinRam); // XS
|
||||
final long staticMax = Math.min(recommendedValue, 4L * dynamicMinRam); // XS
|
||||
// constraint
|
||||
// for
|
||||
// stability
|
||||
|
||||
@ -27,8 +27,6 @@ import java.util.Set;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.vm.NicVO;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
@ -319,6 +317,7 @@ import com.cloud.vm.DomainRouterVO;
|
||||
import com.cloud.vm.InstanceGroup;
|
||||
import com.cloud.vm.InstanceGroupVO;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.NicVO;
|
||||
import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
@ -328,6 +327,7 @@ import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.VmStats;
|
||||
import com.cloud.vm.dao.ConsoleProxyDao;
|
||||
import com.cloud.vm.dao.DomainRouterDao;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
import com.cloud.vm.dao.NicSecondaryIpDao;
|
||||
import com.cloud.vm.dao.NicSecondaryIpVO;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
@ -1118,6 +1118,10 @@ public class ApiDBUtils {
|
||||
return s_serviceOfferingDao.findByIdIncludingRemoved(serviceOfferingId);
|
||||
}
|
||||
|
||||
public static ServiceOffering findServiceOfferingByUuid(String serviceOfferingUuid) {
|
||||
return s_serviceOfferingDao.findByUuidIncludingRemoved(serviceOfferingUuid);
|
||||
}
|
||||
|
||||
public static ServiceOfferingDetailsVO findServiceOfferingDetail(long serviceOfferingId, String key) {
|
||||
return s_serviceOfferingDetailsDao.findDetail(serviceOfferingId, key);
|
||||
}
|
||||
|
||||
@ -176,22 +176,8 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJo
|
||||
// populate owner.
|
||||
ApiResponseHelper.populateOwner(volResponse, volume);
|
||||
|
||||
// DiskOfferingVO diskOffering =
|
||||
// ApiDBUtils.findDiskOfferingById(volume.getDiskOfferingId());
|
||||
if (volume.getDiskOfferingId() > 0) {
|
||||
boolean isServiceOffering = false;
|
||||
if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
|
||||
isServiceOffering = true;
|
||||
} else {
|
||||
// can't rely on the fact that the volume is the datadisk as it might have been created as a root, and
|
||||
// then detached later
|
||||
long offeringId = volume.getDiskOfferingId();
|
||||
if (ApiDBUtils.findDiskOfferingById(offeringId) == null) {
|
||||
isServiceOffering = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isServiceOffering) {
|
||||
if (ApiDBUtils.findServiceOfferingByUuid(volume.getDiskOfferingUuid()) != null) {
|
||||
volResponse.setServiceOfferingId(volume.getDiskOfferingUuid());
|
||||
volResponse.setServiceOfferingName(volume.getDiskOfferingName());
|
||||
volResponse.setServiceOfferingDisplayText(volume.getDiskOfferingDisplayText());
|
||||
|
||||
@ -1973,14 +1973,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
_itMgr.reConfigureVm(vmInstance.getUuid(), currentServiceOffering, newServiceOffering, customParameters, existingHostHasCapacity);
|
||||
success = true;
|
||||
return success;
|
||||
} catch (InsufficientCapacityException e) {
|
||||
s_logger.warn("Received exception while scaling ", e);
|
||||
} catch (ResourceUnavailableException e) {
|
||||
s_logger.warn("Received exception while scaling ", e);
|
||||
} catch (ConcurrentOperationException e) {
|
||||
} catch (InsufficientCapacityException | ResourceUnavailableException | ConcurrentOperationException e) {
|
||||
s_logger.warn("Received exception while scaling ", e);
|
||||
} catch (Exception e) {
|
||||
s_logger.warn("Received exception while scaling ", e);
|
||||
s_logger.warn("Scaling failed with exception: ", e);
|
||||
} finally {
|
||||
if (!success) {
|
||||
// Decrement CPU and Memory count accordingly.
|
||||
|
||||
@ -22,12 +22,17 @@ from marvin.cloudstackTestCase import cloudstackTestCase
|
||||
from marvin.cloudstackAPI import scaleVirtualMachine
|
||||
from marvin.lib.utils import cleanup_resources
|
||||
from marvin.lib.base import (Account,
|
||||
Host,
|
||||
VirtualMachine,
|
||||
ServiceOffering)
|
||||
ServiceOffering,
|
||||
Template,
|
||||
Configurations)
|
||||
from marvin.lib.common import (get_zone,
|
||||
get_template,
|
||||
get_domain)
|
||||
from nose.plugins.attrib import attr
|
||||
from marvin.sshClient import SshClient
|
||||
import time
|
||||
|
||||
_multiprocess_shared_ = True
|
||||
|
||||
@ -39,6 +44,8 @@ class TestScaleVm(cloudstackTestCase):
|
||||
testClient = super(TestScaleVm, cls).getClsTestClient()
|
||||
cls.apiclient = testClient.getApiClient()
|
||||
cls.services = testClient.getParsedTestDataConfig()
|
||||
cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][
|
||||
0].__dict__
|
||||
cls._cleanup = []
|
||||
cls.unsupportedHypervisor = False
|
||||
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
||||
@ -48,21 +55,31 @@ class TestScaleVm(cloudstackTestCase):
|
||||
|
||||
# Get Zone, Domain and templates
|
||||
domain = get_domain(cls.apiclient)
|
||||
zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||
cls.services['mode'] = zone.networktype
|
||||
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||
cls.services['mode'] = cls.zone.networktype
|
||||
|
||||
template = get_template(
|
||||
cls.apiclient,
|
||||
zone.id,
|
||||
cls.services["ostype"]
|
||||
)
|
||||
if template == FAILED:
|
||||
assert False, "get_template() failed to return template\
|
||||
with description %s" % cls.services["ostype"]
|
||||
if cls.hypervisor.lower() == 'simulator':
|
||||
cls.template = get_template(
|
||||
cls.apiclient,
|
||||
cls.zone.id,
|
||||
cls.services["ostype"]
|
||||
)
|
||||
if cls.template == FAILED:
|
||||
assert False, "get_template() failed to return template\
|
||||
with description %s" % cls.services["ostype"]
|
||||
else:
|
||||
cls.template = Template.register(
|
||||
cls.apiclient,
|
||||
cls.services["CentOS7template"],
|
||||
zoneid=cls.zone.id
|
||||
)
|
||||
cls._cleanup.append(cls.template)
|
||||
cls.template.download(cls.apiclient)
|
||||
time.sleep(60)
|
||||
|
||||
# Set Zones and disk offerings ??
|
||||
cls.services["small"]["zoneid"] = zone.id
|
||||
cls.services["small"]["template"] = template.id
|
||||
# Set Zones and disk offerings
|
||||
cls.services["small"]["zoneid"] = cls.zone.id
|
||||
cls.services["small"]["template"] = cls.template.id
|
||||
|
||||
# Create account, service offerings, vm.
|
||||
cls.account = Account.create(
|
||||
@ -70,16 +87,25 @@ class TestScaleVm(cloudstackTestCase):
|
||||
cls.services["account"],
|
||||
domainid=domain.id
|
||||
)
|
||||
cls._cleanup.append(cls.account)
|
||||
|
||||
cls.small_offering = ServiceOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["service_offerings"]["small"]
|
||||
)
|
||||
cls._cleanup.append(cls.small_offering)
|
||||
|
||||
cls.big_offering = ServiceOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["service_offerings"]["big"]
|
||||
)
|
||||
cls._cleanup.append(cls.big_offering)
|
||||
|
||||
Configurations.update(
|
||||
cls.apiclient,
|
||||
name="enable.dynamic.scale.vm",
|
||||
value="true"
|
||||
)
|
||||
|
||||
# create a virtual machine
|
||||
cls.virtual_machine = VirtualMachine.create(
|
||||
@ -90,17 +116,10 @@ class TestScaleVm(cloudstackTestCase):
|
||||
serviceofferingid=cls.small_offering.id,
|
||||
mode=cls.services["mode"]
|
||||
)
|
||||
cls._cleanup = [
|
||||
cls.small_offering,
|
||||
cls.account
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.apiclient = super(
|
||||
TestScaleVm,
|
||||
cls).getClsTestClient().getApiClient()
|
||||
cleanup_resources(cls.apiclient, cls._cleanup)
|
||||
super(TestScaleVm,cls).tearDownClass()
|
||||
return
|
||||
|
||||
def setUp(self):
|
||||
@ -113,10 +132,27 @@ class TestScaleVm(cloudstackTestCase):
|
||||
%s" % self.hypervisor)
|
||||
|
||||
def tearDown(self):
|
||||
Configurations.update(
|
||||
self.apiclient,
|
||||
name="enable.dynamic.scale.vm",
|
||||
value="false"
|
||||
)
|
||||
# Clean up, terminate the created ISOs
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
super(TestScaleVm,self).tearDown()
|
||||
return
|
||||
|
||||
def get_ssh_client(self, ip, username, password, retries=10):
|
||||
""" Setup ssh client connection and return connection """
|
||||
try:
|
||||
ssh_client = SshClient(ip, 22, username, password, retries)
|
||||
except Exception as e:
|
||||
raise self.skipTest("Unable to create ssh connection: " % e)
|
||||
|
||||
self.assertIsNotNone(
|
||||
ssh_client, "Failed to setup ssh connection to ip=%s" % ip)
|
||||
|
||||
return ssh_client
|
||||
|
||||
@attr(hypervisor="xenserver")
|
||||
@attr(tags=["advanced", "basic"], required_hardware="false")
|
||||
def test_01_scale_vm(self):
|
||||
@ -145,6 +181,25 @@ class TestScaleVm(cloudstackTestCase):
|
||||
if not "running" in result:
|
||||
self.skipTest("Skipping scale VM operation because\
|
||||
VMware tools are not installed on the VM")
|
||||
if self.hypervisor.lower() != 'simulator':
|
||||
hostid = self.virtual_machine.hostid
|
||||
host = Host.list(
|
||||
self.apiclient,
|
||||
zoneid=self.zone.id,
|
||||
hostid=hostid,
|
||||
type='Routing'
|
||||
)[0]
|
||||
|
||||
try:
|
||||
username = self.hostConfig["username"]
|
||||
password = self.hostConfig["password"]
|
||||
ssh_client = self.get_ssh_client(host.ipaddress, username, password)
|
||||
res = ssh_client.execute("hostnamectl | grep 'Operating System' | cut -d':' -f2")
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if 'XenServer' in res[0]:
|
||||
self.skipTest("Skipping test for XenServer as it's License does not allow scaling")
|
||||
|
||||
self.virtual_machine.update(
|
||||
self.apiclient,
|
||||
|
||||
@ -50,6 +50,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<a-list-item slot="renderItem" slot-scope="item" v-else-if="item === 'ip6address' && ipV6Address.length > 0">
|
||||
<div>
|
||||
<strong>{{ $t('label.' + String(item).toLowerCase()) }}</strong>
|
||||
<br/>
|
||||
<div>{{ ipV6Address }}</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<HostInfo :resource="resource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
||||
<DedicateData :resource="resource" v-if="dedicatedSectionActive" />
|
||||
<VmwareData :resource="resource" v-if="$route.meta.name === 'zone' && 'listVmwareDcs' in $store.getters.apis" />
|
||||
@ -88,6 +95,15 @@ export default {
|
||||
mounted () {
|
||||
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
|
||||
},
|
||||
computed: {
|
||||
ipV6Address () {
|
||||
if (this.resource.nic && this.resource.nic.length > 0) {
|
||||
return this.resource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ')
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
|
||||
},
|
||||
|
||||
@ -313,6 +313,16 @@
|
||||
<span v-else>{{ ipaddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="ipV6Address && ipV6Address !== null">
|
||||
<div class="resource-detail-item__label">{{ $t('label.ip6address') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<a-icon
|
||||
type="environment"
|
||||
@click="$message.success(`${$t('label.copied.clipboard')} : ${ ipV6Address }`)"
|
||||
v-clipboard:copy="ipV6Address" />
|
||||
{{ ipV6Address }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.projectid || resource.projectname">
|
||||
<div class="resource-detail-item__label">{{ $t('label.project') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
@ -778,6 +788,13 @@ export default {
|
||||
name () {
|
||||
return this.resource.displayname || this.resource.displaytext || this.resource.name || this.resource.username ||
|
||||
this.resource.ipaddress || this.resource.virtualmachinename || this.resource.templatetype
|
||||
},
|
||||
ipV6Address () {
|
||||
if (this.resource.nic && this.resource.nic.length > 0) {
|
||||
return this.resource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ')
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -114,6 +114,9 @@
|
||||
<a-tag>source-nat</a-tag>
|
||||
</span>
|
||||
</span>
|
||||
<span slot="ip6address" slot-scope="text, record" href="javascript:;">
|
||||
<span>{{ ipV6Address(text, record) }}</span>
|
||||
</span>
|
||||
<a slot="publicip" slot-scope="text, record" href="javascript:;">
|
||||
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
||||
</a>
|
||||
@ -564,6 +567,13 @@ export default {
|
||||
},
|
||||
editTariffValue (record) {
|
||||
this.parentEditTariffAction(true, record)
|
||||
},
|
||||
ipV6Address (text, record) {
|
||||
if (!record || !record.nic || record.nic.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return record.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') || text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ export default {
|
||||
return fields
|
||||
},
|
||||
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'],
|
||||
details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'],
|
||||
details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'],
|
||||
tabs: [{
|
||||
component: () => import('@/views/compute/InstanceTab.vue')
|
||||
}],
|
||||
|
||||
@ -47,6 +47,14 @@
|
||||
</div>
|
||||
<div>{{ network.vlan }}</div>
|
||||
</div>
|
||||
<div class="list__col">
|
||||
<div class="list__label">
|
||||
{{ $t('label.traffictype') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ network.traffictype }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="list__col">
|
||||
<div class="list__label">
|
||||
{{ $t('label.broadcastdomainrange') }}
|
||||
@ -101,9 +109,25 @@ export default {
|
||||
this.fetchLoading = true
|
||||
api('listPhysicalNetworks', { zoneid: this.resource.id }).then(json => {
|
||||
this.networks = json.listphysicalnetworksresponse.physicalnetwork || []
|
||||
this.fetchTrafficLabels()
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
})
|
||||
},
|
||||
fetchTrafficLabels () {
|
||||
const promises = []
|
||||
for (const network of this.networks) {
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
api('listTrafficTypes', { physicalnetworkid: network.id }).then(json => {
|
||||
network.traffictype = json.listtraffictypesresponse.traffictype.filter(e => { return e.traffictype }).map(e => { return e.traffictype }).join(', ')
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
reject(error)
|
||||
})
|
||||
}))
|
||||
}
|
||||
Promise.all(promises).finally(() => {
|
||||
this.fetchLoading = false
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user