(mountPoint, templateName);
}
public static String getTemplateOnSecStorageFilePath(String secStorageMountPoint, String templateRelativeFolderPath, String templateName, String fileExtension) {
-
+ s_logger.debug(String.format("Trying to find template [%s] with file extension [%s] in secondary storage mount point [%s] using relative folder path [%s].",
+ templateName, fileExtension, secStorageMountPoint, templateRelativeFolderPath));
StringBuffer sb = new StringBuffer();
sb.append(secStorageMountPoint);
if (!secStorageMountPoint.endsWith("/")) {
@@ -699,17 +715,27 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
}
private String getOVFFilePath(String srcOVAFileName) {
+ s_logger.debug(String.format("Trying to get OVF file from OVA path [%s].", srcOVAFileName));
+
File file = new File(srcOVAFileName);
assert (_storage != null);
String[] files = _storage.listFiles(file.getParent());
- if (files != null) {
- for (String fileName : files) {
- if (fileName.toLowerCase().endsWith(".ovf")) {
- File ovfFile = new File(fileName);
- return file.getParent() + File.separator + ovfFile.getName();
- }
+
+ if (files == null) {
+ s_logger.warn(String.format("Cannot find any files in parent directory [%s] of OVA file [%s].", file.getParent(), srcOVAFileName));
+ return null;
+ }
+
+ s_logger.debug(String.format("Found [%s] files in parent directory of OVA file [%s]. Files found are [%s].", files.length + 1, file.getParent(), StringUtils.join(files, ", ")));
+ for (String fileName : files) {
+ if (fileName.toLowerCase().endsWith(".ovf")) {
+ File ovfFile = new File(fileName);
+ String ovfFilePath = file.getParent() + File.separator + ovfFile.getName();
+ s_logger.debug(String.format("Found OVF file [%s] from OVA file [%s].", ovfFilePath, srcOVAFileName));
+ return ovfFilePath;
}
}
+ s_logger.warn(String.format("Cannot find any OVF file in parent directory [%s] of OVA file [%s].", file.getParent(), srcOVAFileName));
return null;
}
@@ -2050,6 +2076,13 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
s_logger.warn(details);
return new Answer(cmd, false, details);
}
+
+ // delete the directory if it is empty
+ if (snapshotDir.isDirectory() && snapshotDir.list().length == 0 && !snapshotDir.delete()) {
+ details = String.format("Unable to delete directory [%s] at path [%s].", snapshotDir.getName(), snapshotPath);
+ s_logger.debug(details);
+ return new Answer(cmd, false, details);
+ }
return new Answer(cmd, true, null);
} else if (dstore instanceof S3TO) {
final S3TO s3 = (S3TO)dstore;
@@ -2616,13 +2649,13 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
return _parent;
}
try {
+ s_logger.debug(String.format("Trying to get root directory from secondary storage URL [%s] using NFS version [%s].", secUrl, nfsVersion));
URI uri = new URI(secUrl);
String dir = mountUri(uri, nfsVersion);
return _parent + "/" + dir;
} catch (Exception e) {
- String msg = "GetRootDir for " + secUrl + " failed due to " + e.toString();
- s_logger.error(msg, e);
- throw new CloudRuntimeException(msg);
+ String msg = String.format("Failed to get root directory from secondary storage URL [%s], using NFS version [%s], due to [%s].", secUrl, nfsVersion, e.getMessage());
+ throw new CloudRuntimeException(msg, e);
}
}
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
index 7c7f5e48d64..a8634a75ae3 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
@@ -668,8 +668,12 @@ class CsIP:
logging.info("Not making dns publicly available")
if self.config.has_metadata():
- app = CsApache(self)
- app.setup()
+ if method == "add":
+ app = CsApache(self)
+ app.setup()
+ elif method == "delete":
+ app = CsApache(self)
+ app.remove()
# If redundant then this is dealt with
# by the primary backup functions
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsApp.py b/systemvm/debian/opt/cloud/bin/cs/CsApp.py
index d8b3223f017..123171a09c0 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsApp.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsApp.py
@@ -34,7 +34,7 @@ class CsApache(CsApp):
""" Set up Apache """
def remove(self):
- file = "/etc/apache2/sites-enabled/vhost-%s.conf" % self.dev
+ file = "/etc/apache2/sites-enabled/vhost-%s.conf" % self.ip
if os.path.isfile(file):
os.remove(file)
CsHelper.service("apache2", "restart")
diff --git a/test/integration/smoke/test_ssvm.py b/test/integration/smoke/test_ssvm.py
index cdbc7d2f58e..ad03c3d46e1 100644
--- a/test/integration/smoke/test_ssvm.py
+++ b/test/integration/smoke/test_ssvm.py
@@ -121,7 +121,7 @@ class TestSSVMs(cloudstackTestCase):
# should return only ONE SSVM per zone
# 2. The returned SSVM should be in Running state
# 3. listSystemVM for secondarystoragevm should list publicip,
- # privateip and link-localip
+ # privateip, link-localip and service offering id/name
# 4. The gateway programmed on the ssvm by listSystemVm should be
# the same as the gateway returned by listVlanIpRanges
# 5. DNS entries must match those given for the zone
@@ -188,6 +188,18 @@ class TestSSVMs(cloudstackTestCase):
"Check whether SSVM has public IP field"
)
+ self.assertEqual(
+ hasattr(ssvm, 'serviceofferingid'),
+ True,
+ "Check whether SSVM has service offering id field"
+ )
+
+ self.assertEqual(
+ hasattr(ssvm, 'serviceofferingname'),
+ True,
+ "Check whether SSVM has service offering name field"
+ )
+
# Fetch corresponding ip ranges information from listVlanIpRanges
ipranges_response = list_vlan_ipranges(
self.apiclient,
@@ -261,8 +273,8 @@ class TestSSVMs(cloudstackTestCase):
# 1. listSystemVM (systemvmtype=consoleproxy) should return
# at least ONE CPVM per zone
# 2. The returned ConsoleProxyVM should be in Running state
- # 3. listSystemVM for console proxy should list publicip, privateip
- # and link-localip
+ # 3. listSystemVM for console proxy should list publicip, privateip,
+ # link-localip and service offering id/name
# 4. The gateway programmed on the console proxy should be the same
# as the gateway returned by listZones
# 5. DNS entries must match those given for the zone
@@ -327,6 +339,18 @@ class TestSSVMs(cloudstackTestCase):
True,
"Check whether CPVM has public IP field"
)
+
+ self.assertEqual(
+ hasattr(cpvm, 'serviceofferingid'),
+ True,
+ "Check whether CPVM has service offering id field"
+ )
+
+ self.assertEqual(
+ hasattr(cpvm, 'serviceofferingname'),
+ True,
+ "Check whether CPVM has service offering name field"
+ )
# Fetch corresponding ip ranges information from listVlanIpRanges
ipranges_response = list_vlan_ipranges(
self.apiclient,
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 63bef1240c8..88b94acf49b 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -517,6 +517,7 @@
"label.copy.clipboard": "Copy to clipboard",
"label.copy.consoleurl": "Copy console URL to clipboard",
"label.copyid": "Copy ID",
+"label.copy.password": "Copy password",
"label.core": "Core",
"label.core.zone.type": "Core zone type",
"label.counter": "Counter",
diff --git a/ui/src/components/page/GlobalLayout.vue b/ui/src/components/page/GlobalLayout.vue
index d807525068e..7a26599a2ba 100644
--- a/ui/src/components/page/GlobalLayout.vue
+++ b/ui/src/components/page/GlobalLayout.vue
@@ -199,7 +199,8 @@ export default {
created () {
this.menus = this.mainMenu.find((item) => item.path === '/').children
this.collapsed = !this.sidebarOpened
- setInterval(this.checkShutdown, 5000)
+ const readyForShutdownPollingJob = setInterval(this.checkShutdown, 5000)
+ this.$store.commit('SET_READY_FOR_SHUTDOWN_POLLING_JOB', readyForShutdownPollingJob)
},
mounted () {
const layoutMode = this.$config.theme['@layout-mode'] || 'light'
diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue
index 550bba74409..90952b0ea42 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -523,7 +523,7 @@
{{ $t('label.serviceofferingname') }}
- {{ resource.serviceofferingname || resource.serviceofferingid }}
+ {{ resource.serviceofferingname || resource.serviceofferingid }}
{{ resource.serviceofferingname || resource.serviceofferingid }}
{{ resource.serviceofferingname || resource.serviceofferingid }}
diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index 6602ecb884c..22ee2b007f5 100644
--- a/ui/src/config/section/compute.js
+++ b/ui/src/config/section/compute.js
@@ -370,7 +370,13 @@ export default {
message: 'message.action.instance.reset.password',
dataView: true,
show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled },
- response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `The password of VM ${result.virtualmachine.displayname} is ${result.virtualmachine.password}` : null }
+ response: (result) => {
+ return {
+ message: result.virtualmachine && result.virtualmachine.password ? `The password of VM ${result.virtualmachine.displayname} is ${result.virtualmachine.password}` : null,
+ copybuttontext: result.virtualmachine.password ? 'label.copy.password' : null,
+ copytext: result.virtualmachine.password ? result.virtualmachine.password : null
+ }
+ }
},
{
api: 'resetSSHKeyForVirtualMachine',
diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js
index b0c4ecdfccd..67b168be8c2 100644
--- a/ui/src/store/getters.js
+++ b/ui/src/store/getters.js
@@ -50,7 +50,8 @@ const getters = {
twoFaIssuer: state => state.user.twoFaIssuer,
loginFlag: state => state.user.loginFlag,
allProjects: (state) => state.app.allProjects,
- customHypervisorName: state => state.user.customHypervisorName
+ customHypervisorName: state => state.user.customHypervisorName,
+ readyForShutdownPollingJob: state => state.user.readyForShutdownPollingJob
}
export default getters
diff --git a/ui/src/store/modules/app.js b/ui/src/store/modules/app.js
index b3130b68d61..cf2b34e4b8e 100644
--- a/ui/src/store/modules/app.js
+++ b/ui/src/store/modules/app.js
@@ -130,6 +130,9 @@ const app = {
},
SET_SHUTDOWN_TRIGGERED: (state, shutdownTriggered) => {
state.shutdownTriggered = shutdownTriggered
+ },
+ SET_READY_FOR_SHUTDOWN_POLLING_JOB: (state, readyForShutdownPollingJob) => {
+ state.readyForShutdownPollingJob = readyForShutdownPollingJob
}
},
actions: {
@@ -192,6 +195,9 @@ const app = {
},
SetShutdownTriggered ({ commit }, bool) {
commit('SET_SHUTDOWN_TRIGGERED', bool)
+ },
+ SetReadyForShutdownPollingJob ({ commit }, job) {
+ commit('SET_READY_FOR_SHUTDOWN_POLLING_JOB', job)
}
}
}
diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js
index 6a3ba217baf..0e45ac7e676 100644
--- a/ui/src/store/modules/user.js
+++ b/ui/src/store/modules/user.js
@@ -65,7 +65,8 @@ const user = {
twoFaEnabled: false,
twoFaProvider: '',
twoFaIssuer: '',
- customHypervisorName: 'Custom'
+ customHypervisorName: 'Custom',
+ readyForShutdownPollingJob: ''
},
mutations: {
@@ -155,6 +156,9 @@ const user = {
},
SET_CUSTOM_HYPERVISOR_NAME (state, name) {
state.customHypervisorName = name
+ },
+ SET_READY_FOR_SHUTDOWN_POLLING_JOB: (state, job) => {
+ state.readyForShutdownPollingJob = job
}
},
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index ba1b87042b7..fa33be8d1b0 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -444,7 +444,8 @@