mirror of https://github.com/apache/cloudstack.git
Merge remote-tracking branch 'origin/4.11' into 4.12
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
commit
00ff536f81
|
|
@ -52,7 +52,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||
UploadInProgress("Volume upload is in progress"),
|
||||
UploadError("Volume upload encountered some error"),
|
||||
UploadAbandoned("Volume upload is abandoned since the upload was never initiated within a specificed time"),
|
||||
Attaching("The volume is attaching to a VM");
|
||||
Attaching("The volume is attaching to a VM from Ready state.");
|
||||
|
||||
String _description;
|
||||
|
||||
|
|
@ -72,6 +72,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||
static {
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.CreateRequested, Creating, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.DestroyRequested, Destroy, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.OperationFailed, Allocated, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.OperationSucceeded, Allocated, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Creating, Event.OperationRetry, Creating, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Creating, Event.OperationFailed, Allocated, null));
|
||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Creating, Event.OperationSucceeded, Ready, null));
|
||||
|
|
|
|||
|
|
@ -266,13 +266,14 @@ public class ResourceCountDaoImpl extends GenericDaoBase<ResourceCountVO, Long>
|
|||
}
|
||||
|
||||
private long executeSqlCountComputingResourcesForAccount(long accountId, String sqlCountComputingResourcesAllocatedToAccount) {
|
||||
try (TransactionLegacy tx = TransactionLegacy.currentTxn()) {
|
||||
TransactionLegacy tx = TransactionLegacy.currentTxn();
|
||||
try {
|
||||
PreparedStatement pstmt = tx.prepareAutoCloseStatement(sqlCountComputingResourcesAllocatedToAccount);
|
||||
pstmt.setLong(1, accountId);
|
||||
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (!rs.next()) {
|
||||
throw new CloudRuntimeException(String.format("An unexpected case happened while counting allocated computing resources for account: " + accountId));
|
||||
return 0L;
|
||||
}
|
||||
return rs.getLong("total");
|
||||
} catch (SQLException e) {
|
||||
|
|
|
|||
|
|
@ -217,6 +217,9 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
@Override
|
||||
public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) {
|
||||
DataStoreDriver dataStoreDriver = dataStore != null ? dataStore.getDriver() : null;
|
||||
if (dataStoreDriver == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataStoreDriver instanceof PrimaryDataStoreDriver) {
|
||||
((PrimaryDataStoreDriver)dataStoreDriver).revokeAccess(dataObject, host, dataStore);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
|
||||
private String _modifyVlanPath;
|
||||
private String _versionstringpath;
|
||||
private String _patchViaSocketPath;
|
||||
private String _patchScriptPath;
|
||||
private String _createvmPath;
|
||||
private String _manageSnapshotPath;
|
||||
private String _resizeVolumePath;
|
||||
|
|
@ -693,9 +693,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
throw new ConfigurationException("Unable to find versions.sh");
|
||||
}
|
||||
|
||||
_patchViaSocketPath = Script.findScript(kvmScriptsDir + "/patch/", "patchviasocket.py");
|
||||
if (_patchViaSocketPath == null) {
|
||||
throw new ConfigurationException("Unable to find patchviasocket.py");
|
||||
_patchScriptPath = Script.findScript(kvmScriptsDir, "patch.sh");
|
||||
if (_patchScriptPath == null) {
|
||||
throw new ConfigurationException("Unable to find patch.sh");
|
||||
}
|
||||
|
||||
_heartBeatPath = Script.findScript(kvmScriptsDir, "kvmheartbeat.sh");
|
||||
|
|
@ -1376,13 +1376,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
}
|
||||
|
||||
public boolean passCmdLine(final String vmName, final String cmdLine) throws InternalErrorException {
|
||||
final Script command = new Script(_patchViaSocketPath, 5 * 1000, s_logger);
|
||||
final Script command = new Script(_patchScriptPath, 30 * 1000, s_logger);
|
||||
String result;
|
||||
command.add("-n", vmName);
|
||||
command.add("-p", cmdLine.replaceAll(" ", "%"));
|
||||
command.add("-c", cmdLine);
|
||||
result = command.execute();
|
||||
if (result != null) {
|
||||
s_logger.error("passcmd failed:" + result);
|
||||
s_logger.error("Passing cmdline failed:" + result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -2171,12 +2171,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
final SerialDef serial = new SerialDef("pty", null, (short)0);
|
||||
devices.addDevice(serial);
|
||||
|
||||
/* Add a VirtIO channel for SystemVMs for communication and provisioning */
|
||||
if (vmTO.getType() != VirtualMachine.Type.User) {
|
||||
File virtIoChannel = Paths.get(_qemuSocketsPath.getPath(), vmTO.getName() + ".agent").toFile();
|
||||
devices.addDevice(new ChannelDef(vmTO.getName() + ".vport", ChannelDef.ChannelType.UNIX, virtIoChannel));
|
||||
}
|
||||
|
||||
if (_rngEnable) {
|
||||
final RngDef rngDevice = new RngDef(_rngPath, _rngBackendModel, _rngRateBytes, _rngRatePeriod);
|
||||
devices.addDevice(rngDevice);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
|
|||
|
||||
// pass cmdline info to system vms
|
||||
if (vmSpec.getType() != VirtualMachine.Type.User) {
|
||||
//wait and try passCmdLine for 5 minutes at most for CLOUDSTACK-2823
|
||||
String controlIp = null;
|
||||
for (final NicTO nic : nics) {
|
||||
if (nic.getType() == TrafficType.Control) {
|
||||
|
|
@ -116,9 +115,11 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
|
|||
break;
|
||||
}
|
||||
}
|
||||
for (int count = 0; count < 30; count++) {
|
||||
// try to patch and SSH into the systemvm for up to 5 minutes
|
||||
for (int count = 0; count < 10; count++) {
|
||||
// wait and try passCmdLine for 30 seconds at most for CLOUDSTACK-2823
|
||||
libvirtComputingResource.passCmdLine(vmName, vmSpec.getBootArgs());
|
||||
//check router is up?
|
||||
// check router is up?
|
||||
final VirtualRoutingResource virtRouterResource = libvirtComputingResource.getVirtRouterResource();
|
||||
final boolean result = virtRouterResource.connect(controlIp, 1, 5000);
|
||||
if (result) {
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ package com.cloud.hypervisor.kvm.resource;
|
|||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class LibvirtDomainXMLParserTest extends TestCase {
|
||||
|
||||
public void testDomainXMLParser() {
|
||||
|
|
@ -45,9 +45,6 @@ public class LibvirtDomainXMLParserTest extends TestCase {
|
|||
InterfaceDef.GuestNetType ifType = InterfaceDef.GuestNetType.BRIDGE;
|
||||
|
||||
ChannelDef.ChannelType channelType = ChannelDef.ChannelType.UNIX;
|
||||
ChannelDef.ChannelState channelState = ChannelDef.ChannelState.DISCONNECTED;
|
||||
String ssvmAgentPath = "/var/lib/libvirt/qemu/s-2970-VM.agent";
|
||||
String ssvmAgentName = "s-2970-VM.vport";
|
||||
String guestAgentPath = "/var/lib/libvirt/qemu/guest-agent.org.qemu.guest_agent.0";
|
||||
String guestAgentName = "org.qemu.guest_agent.0";
|
||||
|
||||
|
|
@ -155,12 +152,6 @@ public class LibvirtDomainXMLParserTest extends TestCase {
|
|||
"<target type='serial' port='0'/>" +
|
||||
"<alias name='serial0'/>" +
|
||||
"</console>" +
|
||||
"<channel type='unix'>" +
|
||||
"<source mode='bind' path='/var/lib/libvirt/qemu/s-2970-VM.agent'/>" +
|
||||
"<target type='virtio' name='s-2970-VM.vport' state='disconnected'/>" +
|
||||
"<alias name='channel0'/>" +
|
||||
"<address type='virtio-serial' controller='0' bus='0' port='1'/>" +
|
||||
"</channel>" +
|
||||
"<input type='tablet' bus='usb'>" +
|
||||
"<alias name='input0'/>" +
|
||||
"</input>" +
|
||||
|
|
@ -215,14 +206,9 @@ public class LibvirtDomainXMLParserTest extends TestCase {
|
|||
assertEquals(channelType, channels.get(i).getChannelType());
|
||||
}
|
||||
|
||||
/* SSVM provisioning port/channel */
|
||||
assertEquals(channelState, channels.get(0).getChannelState());
|
||||
assertEquals(new File(ssvmAgentPath), channels.get(0).getPath());
|
||||
assertEquals(ssvmAgentName, channels.get(0).getName());
|
||||
|
||||
/* Qemu Guest Agent port/channel */
|
||||
assertEquals(new File(guestAgentPath), channels.get(1).getPath());
|
||||
assertEquals(guestAgentName, channels.get(1).getName());
|
||||
assertEquals(new File(guestAgentPath), channels.get(0).getPath());
|
||||
assertEquals(guestAgentName, channels.get(0).getName());
|
||||
|
||||
List<InterfaceDef> ifs = parser.getInterfaces();
|
||||
for (int i = 0; i < ifs.size(); i++) {
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ public class LibvirtVMDefTest extends TestCase {
|
|||
public void testChannelDef() {
|
||||
ChannelDef.ChannelType type = ChannelDef.ChannelType.UNIX;
|
||||
ChannelDef.ChannelState state = ChannelDef.ChannelState.CONNECTED;
|
||||
String name = "v-136-VM.vport";
|
||||
String name = "v-136-VM.org.qemu.guest_agent.0";
|
||||
File path = new File("/var/lib/libvirt/qemu/" + name);
|
||||
|
||||
ChannelDef channelDef = new ChannelDef(name, type, state, path);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
#!/bin/bash
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
set -e
|
||||
|
||||
# Get the VM name and cmdline
|
||||
while getopts "n:c:h" opt; do
|
||||
case ${opt} in
|
||||
n )
|
||||
name=$OPTARG
|
||||
;;
|
||||
c )
|
||||
cmdline=$(echo $OPTARG | base64 -w 0)
|
||||
;;
|
||||
h )
|
||||
echo "Usage: $0 -n [VM name] -c [command line]"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SSHKEY_FILE="/root/.ssh/id_rsa.pub.cloud"
|
||||
if [ ! -e $SSHKEY_FILE ]; then
|
||||
echo "SSH public key file $SSHKEY_FILE not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! which virsh > /dev/null; then
|
||||
echo "Libvirt CLI 'virsh' not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read the SSH public key
|
||||
sshkey=$(cat $SSHKEY_FILE | base64 -w 0)
|
||||
|
||||
# Method to send and write payload inside the VM
|
||||
send_file() {
|
||||
local name=${1}
|
||||
local path=${2}
|
||||
local content=${@:3}
|
||||
local fd=$(virsh qemu-agent-command $name "{\"execute\":\"guest-file-open\", \"arguments\":{\"path\":\"$path\",\"mode\":\"w+\"}}" | sed 's/[^:]*:\([^}]*\).*/\1/')
|
||||
virsh qemu-agent-command $name "{\"execute\":\"guest-file-write\", \"arguments\":{\"handle\":$fd,\"buf-b64\":\"$content\"}}" > /dev/null
|
||||
virsh qemu-agent-command $name "{\"execute\":\"guest-file-close\", \"arguments\":{\"handle\":$fd}}" > /dev/null
|
||||
}
|
||||
|
||||
# Wait for the guest agent to come online
|
||||
while ! virsh qemu-agent-command $name '{"execute":"guest-ping"}' >/dev/null 2>&1
|
||||
do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Test guest agent sanity
|
||||
while [ "$(virsh qemu-agent-command $name '{"execute":"guest-sync","arguments":{"id":1234567890}}' 2>/dev/null)" != '{"return":1234567890}' ]
|
||||
do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Write ssh public key
|
||||
send_file $name "/root/.ssh/authorized_keys" $sshkey
|
||||
|
||||
# Fix ssh public key permission
|
||||
virsh qemu-agent-command $name '{"execute":"guest-exec","arguments":{"path":"chmod","arg":["go-rwx","/root/.ssh/authorized_keys"]}}' > /dev/null
|
||||
|
||||
# Write cmdline payload
|
||||
send_file $name "/var/cache/cloud/cmdline" $cmdline
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#
|
||||
# This script connects to the system vm socket and writes the
|
||||
# authorized_keys and cmdline data to it. The system VM then
|
||||
# reads it from /dev/vport0p1 in cloud_early_config
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import socket
|
||||
|
||||
SOCK_FILE = "/var/lib/libvirt/qemu/{name}.agent"
|
||||
PUB_KEY_FILE = "/root/.ssh/id_rsa.pub.cloud"
|
||||
MESSAGE = "pubkey:{key}\ncmdline:{cmdline}\n"
|
||||
|
||||
|
||||
def send_to_socket(sock_file, key_file, cmdline):
|
||||
if not os.path.exists(key_file):
|
||||
print("ERROR: ssh public key not found on host at {0}".format(key_file))
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(key_file, "r") as f:
|
||||
pub_key = f.read()
|
||||
except IOError as e:
|
||||
print("ERROR: unable to open {0} - {1}".format(key_file, e.strerror))
|
||||
return 1
|
||||
|
||||
# Keep old substitution from perl code:
|
||||
cmdline = cmdline.replace("%", " ")
|
||||
|
||||
msg = MESSAGE.format(key=pub_key, cmdline=cmdline)
|
||||
|
||||
if not os.path.exists(sock_file):
|
||||
print("ERROR: {0} socket not found".format(sock_file))
|
||||
return 1
|
||||
|
||||
try:
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
s.connect(sock_file)
|
||||
s.sendall(msg)
|
||||
s.close()
|
||||
except IOError as e:
|
||||
print("ERROR: unable to connect to {0} - {1}".format(sock_file, e.strerror))
|
||||
return 1
|
||||
|
||||
return 0 # Success
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Send configuration to system VM socket")
|
||||
parser.add_argument("-n", "--name", required=True, help="Name of VM")
|
||||
parser.add_argument("-p", "--cmdline", required=True, help="Command line")
|
||||
|
||||
arguments = parser.parse_args()
|
||||
|
||||
socket_file = SOCK_FILE.format(name=arguments.name)
|
||||
|
||||
exit(send_to_socket(socket_file, PUB_KEY_FILE, arguments.cmdline))
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
import patchviasocket
|
||||
|
||||
import getpass
|
||||
import os
|
||||
import socket
|
||||
import tempfile
|
||||
import time
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
KEY_DATA = "I luv\nCloudStack\n"
|
||||
CMD_DATA = "/run/this-for-me --please=TRUE! very%quickly"
|
||||
NON_EXISTING_FILE = "must-not-exist"
|
||||
|
||||
|
||||
def write_key_file():
|
||||
_, tmpfile = tempfile.mkstemp(".sck")
|
||||
with open(tmpfile, "w") as f:
|
||||
f.write(KEY_DATA)
|
||||
return tmpfile
|
||||
|
||||
|
||||
class SocketThread(threading.Thread):
|
||||
def __init__(self):
|
||||
super(SocketThread, self).__init__()
|
||||
self._data = ""
|
||||
self._folder = tempfile.mkdtemp(".sck")
|
||||
self._file = os.path.join(self._folder, "socket")
|
||||
self._ready = False
|
||||
|
||||
def data(self):
|
||||
return self._data
|
||||
|
||||
def file(self):
|
||||
return self._file
|
||||
|
||||
def wait_until_ready(self):
|
||||
while not self._ready:
|
||||
time.sleep(0.050)
|
||||
|
||||
def run(self):
|
||||
TIMEOUT = 0.314 # Very short time for tests that don't write to socket.
|
||||
MAX_SIZE = 10 * 1024
|
||||
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.bind(self._file)
|
||||
s.listen(1)
|
||||
s.settimeout(TIMEOUT)
|
||||
try:
|
||||
self._ready = True
|
||||
client, address = s.accept()
|
||||
self._data = client.recv(MAX_SIZE)
|
||||
client.close()
|
||||
except socket.timeout:
|
||||
pass
|
||||
finally:
|
||||
s.close()
|
||||
os.remove(self._file)
|
||||
os.rmdir(self._folder)
|
||||
|
||||
|
||||
class TestPatchViaSocket(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._key_file = write_key_file()
|
||||
|
||||
self._unreadable = write_key_file()
|
||||
os.chmod(self._unreadable, 0)
|
||||
|
||||
self.assertFalse(os.path.exists(NON_EXISTING_FILE))
|
||||
self.assertNotEqual("root", getpass.getuser(), "must be non-root user (to test access denied errors)")
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(self._key_file)
|
||||
os.remove(self._unreadable)
|
||||
|
||||
def test_write_to_socket(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(0, patchviasocket.send_to_socket(reader.file(), self._key_file, CMD_DATA))
|
||||
reader.join()
|
||||
data = reader.data()
|
||||
self.assertIn(KEY_DATA, data)
|
||||
self.assertIn(CMD_DATA.replace("%", " "), data)
|
||||
self.assertNotIn("LUV", data)
|
||||
self.assertNotIn("very%quickly", data) # Testing substitution
|
||||
|
||||
def test_host_key_error(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(1, patchviasocket.send_to_socket(reader.file(), NON_EXISTING_FILE, CMD_DATA))
|
||||
reader.join() # timeout
|
||||
|
||||
def test_host_key_access_denied(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(1, patchviasocket.send_to_socket(reader.file(), self._unreadable, CMD_DATA))
|
||||
reader.join() # timeout
|
||||
|
||||
def test_nonexistant_socket_error(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(1, patchviasocket.send_to_socket(NON_EXISTING_FILE, self._key_file, CMD_DATA))
|
||||
reader.join() # timeout
|
||||
|
||||
def test_invalid_socket_error(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(1, patchviasocket.send_to_socket(self._key_file, self._key_file, CMD_DATA))
|
||||
reader.join() # timeout
|
||||
|
||||
def test_access_denied_socket_error(self):
|
||||
reader = SocketThread()
|
||||
reader.start()
|
||||
reader.wait_until_ready()
|
||||
self.assertEquals(1, patchviasocket.send_to_socket(self._unreadable, self._key_file, CMD_DATA))
|
||||
reader.join() # timeout
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -1892,6 +1892,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
}
|
||||
|
||||
if (volumePool == null) {
|
||||
sendCommand = false;
|
||||
}
|
||||
|
||||
Answer answer = null;
|
||||
|
||||
if (sendCommand) {
|
||||
|
|
@ -1925,12 +1929,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
_volsDao.detachVolume(volume.getId());
|
||||
|
||||
// volume.getPoolId() should be null if the VM we are detaching the disk from has never been started before
|
||||
DataStore dataStore = volume.getPoolId() != null ? dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary) : null;
|
||||
|
||||
volService.revokeAccess(volFactory.getVolume(volume.getId()), host, dataStore);
|
||||
|
||||
handleTargetsForVMware(hostId, volumePool.getHostAddress(), volumePool.getPort(), volume.get_iScsiName());
|
||||
|
||||
if (volume.getPoolId() != null) {
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary);
|
||||
volService.revokeAccess(volFactory.getVolume(volume.getId()), host, dataStore);
|
||||
}
|
||||
if (volumePool != null && hostId != null) {
|
||||
handleTargetsForVMware(hostId, volumePool.getHostAddress(), volumePool.getPort(), volume.get_iScsiName());
|
||||
}
|
||||
return _volsDao.findById(volumeId);
|
||||
} else {
|
||||
|
||||
|
|
@ -2749,24 +2754,25 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
return !storeForExistingStoreScope.isSameScope(storeForNewStoreScope);
|
||||
}
|
||||
|
||||
private synchronized void checkAndSetAttaching(Long volumeId, Long hostId) {
|
||||
private synchronized void checkAndSetAttaching(Long volumeId) {
|
||||
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
|
||||
|
||||
if (volumeToAttach.isAttachedVM()) {
|
||||
throw new CloudRuntimeException("volume: " + volumeToAttach.getName() + " is already attached to a VM: " + volumeToAttach.getAttachedVmName());
|
||||
}
|
||||
if (volumeToAttach.getState().equals(Volume.State.Ready)) {
|
||||
volumeToAttach.stateTransit(Volume.Event.AttachRequested);
|
||||
} else {
|
||||
String error = null;
|
||||
if (hostId == null) {
|
||||
error = "Please try attach operation after starting VM once";
|
||||
} else {
|
||||
error = "Volume: " + volumeToAttach.getName() + " is in " + volumeToAttach.getState() + ". It should be in Ready state";
|
||||
}
|
||||
s_logger.error(error);
|
||||
throw new CloudRuntimeException(error);
|
||||
|
||||
if (Volume.State.Allocated.equals(volumeToAttach.getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Volume.State.Ready.equals(volumeToAttach.getState())) {
|
||||
volumeToAttach.stateTransit(Volume.Event.AttachRequested);
|
||||
return;
|
||||
}
|
||||
|
||||
final String error = "Volume: " + volumeToAttach.getName() + " is in " + volumeToAttach.getState() + ". It should be in Ready or Allocated state";
|
||||
s_logger.error(error);
|
||||
throw new CloudRuntimeException(error);
|
||||
}
|
||||
|
||||
private void verifyManagedStorage(Long storagePoolId, Long hostId) {
|
||||
|
|
@ -2837,7 +2843,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
// volumeToAttachStoragePool should be null if the VM we are attaching the disk to has never been started before
|
||||
DataStore dataStore = volumeToAttachStoragePool != null ? dataStoreMgr.getDataStore(volumeToAttachStoragePool.getId(), DataStoreRole.Primary) : null;
|
||||
|
||||
checkAndSetAttaching(volumeToAttach.getId(), hostId);
|
||||
checkAndSetAttaching(volumeToAttach.getId());
|
||||
|
||||
boolean attached = false;
|
||||
try {
|
||||
|
|
@ -2926,9 +2932,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
volumeToAttach = _volsDao.findById(volumeToAttach.getId());
|
||||
|
||||
if (vm.getHypervisorType() == HypervisorType.KVM && volumeToAttachStoragePool.isManaged() && volumeToAttach.getPath() == null) {
|
||||
if (vm.getHypervisorType() == HypervisorType.KVM &&
|
||||
volumeToAttachStoragePool != null && volumeToAttachStoragePool.isManaged() &&
|
||||
volumeToAttach.getPath() == null && volumeToAttach.get_iScsiName() != null) {
|
||||
volumeToAttach.setPath(volumeToAttach.get_iScsiName());
|
||||
|
||||
_volsDao.update(volumeToAttach.getId(), volumeToAttach);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,41 +90,29 @@ get_boot_params() {
|
|||
sed -i "s/%/ /g" $CMDLINE
|
||||
;;
|
||||
kvm)
|
||||
VPORT=$(find /dev/virtio-ports -type l -name '*.vport' 2>/dev/null|head -1)
|
||||
|
||||
if [ -z "$VPORT" ]; then
|
||||
log_it "No suitable VirtIO port was found in /dev/virtio-ports" && exit 2
|
||||
# Use any old cmdline as backup only
|
||||
if [ -s $CMDLINE ]; then
|
||||
mv $CMDLINE $CMDLINE.old
|
||||
log_it "Found a non-empty old cmdline file"
|
||||
fi
|
||||
|
||||
if [ ! -e "$VPORT" ]; then
|
||||
log_it "${VPORT} not loaded, perhaps guest kernel is too old." && exit 2
|
||||
fi
|
||||
|
||||
local factor=2
|
||||
local progress=1
|
||||
for i in {1..5}
|
||||
do
|
||||
while read line; do
|
||||
if [[ $line == cmdline:* ]]; then
|
||||
cmd=${line//cmdline:/}
|
||||
echo $cmd > $CMDLINE
|
||||
elif [[ $line == pubkey:* ]]; then
|
||||
pubkey=${line//pubkey:/}
|
||||
echo $pubkey > /var/cache/cloud/authorized_keys
|
||||
echo $pubkey > /root/.ssh/authorized_keys
|
||||
fi
|
||||
done < $VPORT
|
||||
# In case of reboot we do not send the boot args again.
|
||||
# So, no need to wait for them, as the boot args are already set at startup
|
||||
if [ -s $CMDLINE ]
|
||||
then
|
||||
log_it "Found a non empty cmdline file. Will now exit the loop and proceed with configuration."
|
||||
break;
|
||||
systemctl enable --now qemu-guest-agent
|
||||
# Wait for $CMDLINE file to be written by the qemu-guest-agent
|
||||
for i in {1..60}; do
|
||||
if [ -s $CMDLINE ]; then
|
||||
log_it "Received a new non-empty cmdline file from qemu-guest-agent"
|
||||
break
|
||||
fi
|
||||
sleep ${progress}s
|
||||
progress=$[ progress * factor ]
|
||||
sleep 1
|
||||
done
|
||||
chmod go-rwx /root/.ssh/authorized_keys
|
||||
# Use any old cmdline only when new cmdline is not received
|
||||
if [ -e $CMDLINE.old ]; then
|
||||
if [ -s $CMDLINE ]; then
|
||||
rm $CMDLINE.old
|
||||
else
|
||||
mv $CMDLINE.old $CMDLINE
|
||||
log_it "Using old cmdline file, VM was perhaps rebooted."
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
vmware)
|
||||
vmtoolsd --cmd 'machine.id.get' > $CMDLINE
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2RIE3hgSAD8zULuyE7KDW9EKh2oVbNGY7iSL/VI5xHLISKh4e8ksTshWjlGBtrUCnuzR7y2BUxZ65RI8XkB1fEDxcOU4/0lVPvJYDSsGveXoOgpLwOtKRoGLgjFUGzBQlj2s6YaYQxoNTqtBVkDIH6ekPNq0Q38hRrFcsVIk1sFo5ejuvFxt2wx6APcFIQtHSNezEDO0GVUScDU1N1YEMMv1PU3M/SrcezkXrGl/efF3kWtY9L5xm7sojHMCCqsI38r8ogof67F7JdWRXM6Nl3VzkdCBzWGcyAl+cYfjzgOiBGXyAyYBk8qqzJjKwUOtdjfRvCyowA/0xBwMW1T7PQ==
|
||||
|
|
@ -88,12 +88,6 @@
|
|||
<include>agent.zip</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>debian/root/.ssh</directory>
|
||||
<includes>
|
||||
<include>authorized_keys</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
@ -166,7 +160,6 @@
|
|||
<argument>systemvm.iso</argument>
|
||||
<argument>agent.zip</argument>
|
||||
<argument>cloud-scripts.tgz</argument>
|
||||
<argument>authorized_keys</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
|||
|
|
@ -399,32 +399,32 @@ class TestVolumes(cloudstackTestCase):
|
|||
# 3. disk should be attached to instance successfully
|
||||
|
||||
self.debug(
|
||||
"Attaching volume (ID: %s) to VM (ID: %s)" % (
|
||||
self.volume.id,
|
||||
self.virtual_machine.id
|
||||
))
|
||||
"Attaching volume (ID: %s) to VM (ID: %s)" % (
|
||||
self.volume.id,
|
||||
self.virtual_machine.id
|
||||
))
|
||||
self.virtual_machine.attach_volume(self.apiClient, self.volume)
|
||||
self.attached = True
|
||||
list_volume_response = Volume.list(
|
||||
self.apiClient,
|
||||
id=self.volume.id
|
||||
)
|
||||
self.apiClient,
|
||||
id=self.volume.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_volume_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
isinstance(list_volume_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
self.assertNotEqual(
|
||||
list_volume_response,
|
||||
None,
|
||||
"Check if volume exists in ListVolumes"
|
||||
)
|
||||
list_volume_response,
|
||||
None,
|
||||
"Check if volume exists in ListVolumes"
|
||||
)
|
||||
volume = list_volume_response[0]
|
||||
self.assertNotEqual(
|
||||
volume.virtualmachineid,
|
||||
None,
|
||||
"Check if volume state (attached) is reflected"
|
||||
)
|
||||
volume.virtualmachineid,
|
||||
None,
|
||||
"Check if volume state (attached) is reflected"
|
||||
)
|
||||
try:
|
||||
#Format the attached volume to a known fs
|
||||
format_volume_to_ext3(self.virtual_machine.get_ssh_client())
|
||||
|
|
@ -432,7 +432,7 @@ class TestVolumes(cloudstackTestCase):
|
|||
except Exception as e:
|
||||
|
||||
self.fail("SSH failed for VM: %s - %s" %
|
||||
(self.virtual_machine.ipaddress, e))
|
||||
(self.virtual_machine.ipaddress, e))
|
||||
return
|
||||
|
||||
@attr(tags = ["advanced", "advancedns", "smoke", "basic"], required_hardware="false")
|
||||
|
|
@ -858,6 +858,60 @@ class TestVolumes(cloudstackTestCase):
|
|||
self.assertTrue(hasattr(root_volume, "podname"))
|
||||
self.assertEqual(root_volume.podname, list_pods.name)
|
||||
|
||||
@attr(tags = ["advanced", "advancedns", "smoke", "basic"], required_hardware="true")
|
||||
def test_11_attach_volume_with_unstarted_vm(self):
|
||||
"""Attach a created Volume to a unstarted VM
|
||||
"""
|
||||
# Validate the following
|
||||
# 1. Attach to a vm in startvm=false state works and vm can be started afterwards.
|
||||
# 2. shows list of volumes
|
||||
# 3. "Attach Disk" pop-up box will display with list of instances
|
||||
# 4. disk should be attached to instance successfully
|
||||
|
||||
test_vm = VirtualMachine.create(
|
||||
self.apiclient,
|
||||
self.services,
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
mode=self.services["mode"],
|
||||
startvm=False
|
||||
)
|
||||
|
||||
self.debug(
|
||||
"Attaching volume (ID: %s) to VM (ID: %s)" % (
|
||||
self.volume.id,
|
||||
test_vm.id
|
||||
))
|
||||
test_vm.attach_volume(self.apiClient, self.volume)
|
||||
test_vm.start(self.apiClient)
|
||||
|
||||
list_volume_response = Volume.list(
|
||||
self.apiClient,
|
||||
id=self.volume.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_volume_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
self.assertNotEqual(
|
||||
list_volume_response,
|
||||
None,
|
||||
"Check if volume exists in ListVolumes"
|
||||
)
|
||||
volume = list_volume_response[0]
|
||||
self.assertNotEqual(
|
||||
volume.virtualmachineid,
|
||||
None,
|
||||
"Check if volume state (attached) is reflected"
|
||||
)
|
||||
|
||||
test_vm.detach_volume(self.apiClient, self.volume)
|
||||
self.cleanup.append(test_vm)
|
||||
|
||||
return
|
||||
|
||||
def wait_for_attributes_and_return_root_vol(self):
|
||||
def checkVolumeResponse():
|
||||
list_volume_response = Volume.list(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
CLOUDSTACK_RELEASE=4.11.2
|
||||
CLOUDSTACK_RELEASE=4.11.3
|
||||
|
||||
function configure_apache2() {
|
||||
# Enable ssl, rewrite and auth
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@
|
|||
"disk_interface": "virtio",
|
||||
"net_device": "virtio-net",
|
||||
|
||||
"iso_url": "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-9.6.0-amd64-netinst.iso",
|
||||
"iso_checksum": "fcd77acbd46f33e0a266faf284acc1179ab0a3719e4b8abebac555307aa978aa242d7052c8d41e1a5fc6d1b30bc6ca6d62269e71526b71c9d5199b13339f0e25",
|
||||
"iso_url": "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-9.9.0-amd64-netinst.iso",
|
||||
"iso_checksum": "42d9818abc4a08681dc0638f07e7aeb35d0c44646ab1e5b05a31a71d76c99da52b6192db9a3e852171ac78c2ba6b110b337c0b562c7be3d32e86a105023a6a0c",
|
||||
"iso_checksum_type": "sha512",
|
||||
|
||||
"vm_name": "systemvmtemplate",
|
||||
|
|
|
|||
Loading…
Reference in New Issue