2929import java .util .Collections ;
3030import java .util .Comparator ;
3131import java .util .List ;
32+ import java .util .concurrent .ExecutorService ;
33+ import java .util .concurrent .Executors ;
34+ import java .util .concurrent .Future ;
3235
3336import org .apache .log4j .Logger ;
3437
3538import com .google .gson .Gson ;
3639import com .vmware .vim25 .ArrayOfManagedObjectReference ;
40+ import com .vmware .vim25 .ChoiceOption ;
3741import com .vmware .vim25 .CustomFieldStringValue ;
3842import com .vmware .vim25 .DistributedVirtualSwitchPortConnection ;
3943import com .vmware .vim25 .DynamicProperty ;
44+ import com .vmware .vim25 .ElementDescription ;
4045import com .vmware .vim25 .GuestInfo ;
4146import com .vmware .vim25 .GuestOsDescriptor ;
4247import com .vmware .vim25 .HttpNfcLeaseDeviceUrl ;
8085import com .vmware .vim25 .VirtualMachineConfigSpec ;
8186import com .vmware .vim25 .VirtualMachineConfigSummary ;
8287import com .vmware .vim25 .VirtualMachineFileInfo ;
88+ import com .vmware .vim25 .VirtualMachineMessage ;
8389import com .vmware .vim25 .VirtualMachineMovePriority ;
8490import com .vmware .vim25 .VirtualMachinePowerState ;
91+ import com .vmware .vim25 .VirtualMachineQuestionInfo ;
8592import com .vmware .vim25 .VirtualMachineRelocateDiskMoveOptions ;
8693import com .vmware .vim25 .VirtualMachineRelocateSpec ;
8794import com .vmware .vim25 .VirtualMachineRelocateSpecDiskLocator ;
98105import com .cloud .utils .ActionDelegate ;
99106import com .cloud .utils .Pair ;
100107import com .cloud .utils .Ternary ;
108+ import com .cloud .utils .concurrency .NamedThreadFactory ;
101109import com .cloud .utils .script .Script ;
102110
103111import java .util .Arrays ;
104112
105113public class VirtualMachineMO extends BaseMO {
106114 private static final Logger s_logger = Logger .getLogger (VirtualMachineMO .class );
115+ private static final ExecutorService _monitorServiceExecutor = Executors .newCachedThreadPool (new NamedThreadFactory ("VM-Question-Monitor" ));
107116 private ManagedObjectReference _vmEnvironmentBrowser = null ;
108117
109118 public VirtualMachineMO (VmwareContext context , ManagedObjectReference morVm ) {
@@ -170,6 +179,10 @@ public GuestInfo getVmGuestInfo() throws Exception {
170179 return (GuestInfo )getContext ().getVimClient ().getDynamicProperty (_mor , "guest" );
171180 }
172181
182+ public void answerVM (String questionId , String choice ) throws Exception {
183+ getContext ().getService ().answerVM (_mor , questionId , choice );
184+ }
185+
173186 public boolean isVMwareToolsRunning () throws Exception {
174187 GuestInfo guestInfo = getVmGuestInfo ();
175188 if (guestInfo != null ) {
@@ -1161,7 +1174,7 @@ public void attachIso(String isoDatastorePath, ManagedObjectReference morDs,
11611174 boolean connect , boolean connectAtBoot ) throws Exception {
11621175
11631176 if (s_logger .isTraceEnabled ())
1164- s_logger .trace ("vCenter API trace - detachIso (). target MOR: " + _mor .getValue () + ", isoDatastorePath: "
1177+ s_logger .trace ("vCenter API trace - attachIso (). target MOR: " + _mor .getValue () + ", isoDatastorePath: "
11651178 + isoDatastorePath + ", datastore: " + morDs .getValue () + ", connect: " + connect + ", connectAtBoot: " + connectAtBoot );
11661179
11671180 assert (isoDatastorePath != null );
@@ -1244,18 +1257,90 @@ public void detachIso(String isoDatastorePath) throws Exception {
12441257 //deviceConfigSpecArray[0] = deviceConfigSpec;
12451258 reConfigSpec .getDeviceChange ().add (deviceConfigSpec );
12461259
1247- ManagedObjectReference morTask = _context .getService ().reconfigVMTask (_mor , reConfigSpec );
1248- boolean result = _context .getVimClient ().waitForTask (morTask );
1260+ ManagedObjectReference morTask = _context .getService ().reconfigVMTask (_mor , reConfigSpec );
1261+
1262+ // Monitor VM questions
1263+ final Boolean [] flags = { false };
1264+ final VirtualMachineMO vmMo = this ;
1265+ Future <?> future = _monitorServiceExecutor .submit (new Runnable () {
1266+ @ Override
1267+ public void run () {
1268+ s_logger .info ("VM Question monitor started..." );
1269+
1270+ while (!flags [0 ]) {
1271+ try {
1272+ VirtualMachineRuntimeInfo runtimeInfo = vmMo .getRuntimeInfo ();
1273+ VirtualMachineQuestionInfo question = runtimeInfo .getQuestion ();
1274+ if (question != null ) {
1275+ if (s_logger .isTraceEnabled ()) {
1276+ s_logger .trace ("Question id: " + question .getId ());
1277+ s_logger .trace ("Question text: " + question .getText ());
1278+ }
1279+ if (question .getMessage () != null ) {
1280+ for (VirtualMachineMessage msg : question .getMessage ()) {
1281+ if (s_logger .isTraceEnabled ()) {
1282+ s_logger .trace ("msg id: " + msg .getId ());
1283+ s_logger .trace ("msg text: " + msg .getText ());
1284+ }
1285+ if ("msg.cdromdisconnect.locked" .equalsIgnoreCase (msg .getId ())) {
1286+ s_logger .info ("Found that VM has a pending question that we need to answer programmatically, question id: " + msg .getId ()
1287+ + ", for safe operation we will automatically decline it" );
1288+ vmMo .answerVM (question .getId (), "1" );
1289+ break ;
1290+ }
1291+ }
1292+ } else if (question .getText () != null ) {
1293+ String text = question .getText ();
1294+ String msgId ;
1295+ String msgText ;
1296+ if (s_logger .isDebugEnabled ()) {
1297+ s_logger .debug ("question text : " + text );
1298+ }
1299+ String [] tokens = text .split (":" );
1300+ msgId = tokens [0 ];
1301+ msgText = tokens [1 ];
1302+ if ("msg.cdromdisconnect.locked" .equalsIgnoreCase (msgId )) {
1303+ s_logger .info ("Found that VM has a pending question that we need to answer programmatically, question id: " + question .getId ()
1304+ + ". Message id : " + msgId + ". Message text : " + msgText
1305+ + ", for safe operation we will automatically decline it." );
1306+ vmMo .answerVM (question .getId (), "1" );
1307+ }
1308+ }
12491309
1250- if (!result ) {
1251- if (s_logger .isTraceEnabled ())
1252- s_logger .trace ("vCenter API trace - detachIso() done(failed)" );
1253- throw new Exception ("Failed to detachIso due to " + TaskMO .getTaskFailureInfo (_context , morTask ));
1310+ ChoiceOption choice = question .getChoice ();
1311+ if (choice != null ) {
1312+ for (ElementDescription info : choice .getChoiceInfo ()) {
1313+ if (s_logger .isTraceEnabled ()) {
1314+ s_logger .trace ("Choice option key: " + info .getKey ());
1315+ s_logger .trace ("Choice option label: " + info .getLabel ());
1316+ }
1317+ }
1318+ }
1319+ }
1320+ } catch (Throwable e ) {
1321+ s_logger .error ("Unexpected exception: " , e );
1322+ }
1323+ try {
1324+ Thread .sleep (1000 );
1325+ } catch (InterruptedException e ) {
1326+ }
1327+ }
1328+ s_logger .info ("VM Question monitor stopped" );
1329+ }
1330+ });
1331+ try {
1332+ boolean result = _context .getVimClient ().waitForTask (morTask );
1333+ if (!result ) {
1334+ if (s_logger .isDebugEnabled ())
1335+ s_logger .trace ("vCenter API trace - detachIso() done(failed)" );
1336+ throw new Exception ("Failed to detachIso due to " + TaskMO .getTaskFailureInfo (_context , morTask ));
1337+ }
1338+ _context .waitForTaskProgressDone (morTask );
1339+ s_logger .trace ("vCenter API trace - detachIso() done(successfully)" );
1340+ } finally {
1341+ flags [0 ] = true ;
1342+ future .cancel (true );
12541343 }
1255- _context .waitForTaskProgressDone (morTask );
1256-
1257- if (s_logger .isTraceEnabled ())
1258- s_logger .trace ("vCenter API trace - detachIso() done(successfully)" );
12591344 }
12601345
12611346 public Pair <VmdkFileDescriptor , byte []> getVmdkFileInfo (String vmdkDatastorePath ) throws Exception {
@@ -2061,7 +2146,7 @@ public List<String> detachAllDisksExcept(String vmdkBaseName, String deviceBusNa
20612146 }
20622147 }
20632148
2064- return detachedDiskFiles ;
2149+ return detachedDiskFiles ;
20652150 }
20662151
20672152 public List <VirtualDevice > getAllDeviceList () throws Exception {
@@ -2086,7 +2171,7 @@ public VirtualDisk getDiskDeviceByBusName(List<VirtualDevice> allDevices, String
20862171 for (VirtualDevice device : allDevices ) {
20872172 if (device instanceof VirtualDisk ) {
20882173 VirtualDisk disk = (VirtualDisk )device ;
2089- String diskBusName = getDeviceBusName (allDevices , ( VirtualDevice ) disk );
2174+ String diskBusName = getDeviceBusName (allDevices , disk );
20902175 if (busName .equalsIgnoreCase (diskBusName ))
20912176 return disk ;
20922177 }
@@ -2314,8 +2399,87 @@ public void mountToolsInstaller() throws Exception {
23142399 }
23152400
23162401 public void unmountToolsInstaller () throws Exception {
2317- _context .getService ().unmountToolsInstaller (_mor );
2318- }
2402+ int i = 1 ;
2403+ // Monitor VM questions
2404+ final Boolean [] flags = {false };
2405+ final VirtualMachineMO vmMo = this ;
2406+ Future <?> future = _monitorServiceExecutor .submit (new Runnable () {
2407+ @ Override
2408+ public void run () {
2409+ s_logger .info ("VM Question monitor started..." );
2410+
2411+ while (!flags [0 ]) {
2412+ try {
2413+ VirtualMachineRuntimeInfo runtimeInfo = vmMo .getRuntimeInfo ();
2414+ VirtualMachineQuestionInfo question = runtimeInfo .getQuestion ();
2415+ if (question != null ) {
2416+ if (s_logger .isTraceEnabled ()) {
2417+ s_logger .trace ("Question id: " + question .getId ());
2418+ s_logger .trace ("Question text: " + question .getText ());
2419+ }
2420+
2421+ if (question .getMessage () != null ) {
2422+ for (VirtualMachineMessage msg : question .getMessage ()) {
2423+ if (s_logger .isTraceEnabled ()) {
2424+ s_logger .trace ("msg id: " + msg .getId ());
2425+ s_logger .trace ("msg text: " + msg .getText ());
2426+ }
2427+ if ("msg.cdromdisconnect.locked" .equalsIgnoreCase (msg .getId ())) {
2428+ s_logger .info ("Found that VM has a pending question that we need to answer programmatically, question id: " + msg .getId ()
2429+ + ", for safe operation we will automatically decline it" );
2430+ vmMo .answerVM (question .getId (), "1" );
2431+ break ;
2432+ }
2433+ }
2434+ } else if (question .getText () != null ) {
2435+ String text = question .getText ();
2436+ String msgId ;
2437+ String msgText ;
2438+ if (s_logger .isDebugEnabled ()) {
2439+ s_logger .debug ("question text : " + text );
2440+ }
2441+ String [] tokens = text .split (":" );
2442+ msgId = tokens [0 ];
2443+ msgText = tokens [1 ];
2444+ if ("msg.cdromdisconnect.locked" .equalsIgnoreCase (msgId )) {
2445+ s_logger .info ("Found that VM has a pending question that we need to answer programmatically, question id: " + question .getId ()
2446+ + ". Message id : " + msgId + ". Message text : " + msgText
2447+ + ", for safe operation we will automatically decline it." );
2448+ vmMo .answerVM (question .getId (), "1" );
2449+ }
2450+ }
2451+
2452+ ChoiceOption choice = question .getChoice ();
2453+ if (choice != null ) {
2454+ for (ElementDescription info : choice .getChoiceInfo ()) {
2455+ if (s_logger .isTraceEnabled ()) {
2456+ s_logger .trace ("Choice option key: " + info .getKey ());
2457+ s_logger .trace ("Choice option label: " + info .getLabel ());
2458+ }
2459+ }
2460+ }
2461+ }
2462+ } catch (Throwable e ) {
2463+ s_logger .error ("Unexpected exception: " , e );
2464+ }
2465+
2466+ try {
2467+ Thread .sleep (1000 );
2468+ } catch (InterruptedException e ) {
2469+ }
2470+ }
2471+
2472+ s_logger .info ("VM Question monitor stopped" );
2473+ }
2474+ });
2475+
2476+ try {
2477+ _context .getService ().unmountToolsInstaller (_mor );
2478+ } finally {
2479+ flags [0 ] = true ;
2480+ future .cancel (true );
2481+ }
2482+ }
23192483
23202484 public void redoRegistration (ManagedObjectReference morHost ) throws Exception {
23212485 String vmName = getVmName ();
0 commit comments