forked from apache/cloudstack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
WmiCallsV2.cs
executable file
·500 lines (458 loc) · 21.5 KB
/
WmiCallsV2.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2;
using log4net;
using System.Globalization;
using System.Management;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using CloudStack.Plugin.WmiWrappers.ROOT.CIMV2;
using System.IO;
namespace HypervResource
{
public class WmiCallsV2
{
public static String CloudStackUserDataKey = "cloudstack-vm-userdata";
public static void Initialize()
{
// Trigger assembly load into curren appdomain
}
private static ILog logger = LogManager.GetLogger(typeof(WmiCallsV2));
/// <summary>
/// Returns ComputerSystem lacking any NICs and VOLUMEs
/// </summary>
public static ComputerSystem AddUserData(ComputerSystem vm, string userData)
{
// Obtain controller for Hyper-V virtualisation subsystem
VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
// Create object to hold the data.
KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance();
kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey;
kvpItem.LateBoundObject["Data"] = userData;
kvpItem.LateBoundObject["Source"] = 0;
logger.Debug("VM " + vm.Name + " gets userdata " + userData);
// Update the resource settings for the VM.
System.Management.ManagementBaseObject kvpMgmtObj = kvpItem.LateBoundObject;
System.Management.ManagementPath jobPath;
String kvpStr = kvpMgmtObj.GetText(System.Management.TextFormat.CimDtd20);
uint ret_val = vmMgmtSvc.AddKvpItems(new String[] { kvpStr }, vm.Path, out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return vm;
}
/// <summary>
/// Returns ComputerSystem lacking any NICs and VOLUMEs
/// </summary>
public static void DeleteHostKvpItem(ComputerSystem vm, string key)
{
// Obtain controller for Hyper-V virtualisation subsystem
VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
// Create object to hold the data.
KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance();
kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey;
kvpItem.LateBoundObject["Data"] = "dummy";
kvpItem.LateBoundObject["Source"] = 0;
logger.Debug("VM " + vm.Name + " will have KVP key " + key + " removed.");
String kvpStr = kvpItem.LateBoundObject.GetText(TextFormat.CimDtd20);
// Update the resource settings for the VM.
ManagementPath jobPath;
uint ret_val = vmMgmtSvc.RemoveKvpItems(new String[] { kvpStr }, vm.Path, out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
public static VirtualSystemManagementService GetVirtualisationSystemManagementService()
{
// VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set
// of local instances, which should be size 1.
var virtSysMgmtSvcCollection = VirtualSystemManagementService.GetInstances();
foreach (VirtualSystemManagementService item in virtSysMgmtSvcCollection)
{
return item;
}
var errMsg = string.Format("No Hyper-V subsystem on server");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
/// <summary>
/// Similar to http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
/// </summary>
/// <param name="jobPath"></param>
/// <returns></returns>
private static void JobCompleted(ManagementPath jobPath)
{
ConcreteJob jobObj = null;
for(;;)
{
jobObj = new ConcreteJob(jobPath);
if (jobObj.JobState != JobState.Starting && jobObj.JobState != JobState.Running)
{
break;
}
logger.InfoFormat("In progress... {0}% completed.", jobObj.PercentComplete);
System.Threading.Thread.Sleep(1000);
}
if (jobObj.JobState != JobState.Completed)
{
var errMsg = string.Format(
"Hyper-V Job failed, Error Code:{0}, Description: {1}",
jobObj.ErrorCode,
jobObj.ErrorDescription);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.DebugFormat("WMI job succeeded: {0}, Elapsed={1}", jobObj.Description, jobObj.ElapsedTime);
}
public static ComputerSystem GetComputerSystem(string displayName)
{
var wmiQuery = String.Format("ElementName=\"{0}\"", displayName);
ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(wmiQuery);
// Return the first one
foreach (ComputerSystem vm in vmCollection)
{
return vm;
}
return null;
}
public static List<string> GetVmElementNames()
{
List<string> result = new List<string>();
ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances();
// Return the first one
foreach (ComputerSystem vm in vmCollection)
{
if (vm.Caption.StartsWith("Hosting Computer System") )
{
continue;
}
result.Add(vm.ElementName);
}
return result;
}
public static VirtualSystemSettingData GetVmSettings(ComputerSystem vm)
{
// An ASSOCIATOR object provides the cross reference from the ComputerSettings and the
// VirtualSystemSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vm.Path.Path, VirtualSystemSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery);
var wmiObjCollection = new VirtualSystemSettingData.VirtualSystemSettingDataCollection(wmiObjectSearch.Get());
// When snapshots are taken into account, there can be multiple settings objects
// take the first one that isn't a snapshot
foreach (VirtualSystemSettingData wmiObj in wmiObjCollection)
{
if (wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Realized" ||
wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Planned")
{
return wmiObj;
}
}
var errMsg = string.Format("No VirtualSystemSettingData for VM {0}, path {1}", vm.ElementName, vm.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static KvpExchangeComponentSettingData GetKvpSettings(VirtualSystemSettingData vmSettings)
{
// An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
// KvpExchangeComponentSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, KvpExchangeComponentSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
var wmiObjCollection = new KvpExchangeComponentSettingData.KvpExchangeComponentSettingDataCollection(wmiObjectSearch.Get());
foreach (KvpExchangeComponentSettingData wmiObj in wmiObjCollection)
{
return wmiObj;
}
var errMsg = string.Format("No KvpExchangeComponentSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
public class WmiException : Exception
{
public WmiException()
{
}
public WmiException(string message)
: base(message)
{
}
public WmiException(string message, Exception inner)
: base(message, inner)
{
}
}
/// <summary>
/// Covers V2 API, see
/// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
/// </summary>
public static class ReturnCode
{
public const UInt32 Completed = 0;
public const UInt32 Started = 4096;
public const UInt32 Failed = 32768;
public const UInt32 AccessDenied = 32769;
public const UInt32 NotSupported = 32770;
public const UInt32 Unknown = 32771;
public const UInt32 Timeout = 32772;
public const UInt32 InvalidParameter = 32773;
public const UInt32 SystemInUse = 32774;
public const UInt32 InvalidState = 32775;
public const UInt32 IncorrectDataType = 32776;
public const UInt32 SystemNotAvailable = 32777;
public const UInt32 OutofMemory = 32778;
public static string ToString(UInt32 value)
{
string result = "Unknown return code";
switch (value)
{
case Completed: result = "Completed"; break;
case Started: result = "Started"; break;
case Failed: result = "Failed"; break;
case AccessDenied: result = "AccessDenied"; break;
case NotSupported: result = "NotSupported"; break;
case Unknown: result = "Unknown"; break;
case Timeout: result = "Timeout"; break;
case InvalidParameter: result = "InvalidParameter"; break;
case SystemInUse: result = "SystemInUse"; break;
case InvalidState: result = "InvalidState"; break;
case IncorrectDataType: result = "IncorrectDataType"; break;
case SystemNotAvailable: result = "SystemNotAvailable"; break;
case OutofMemory: result = "OutofMemory"; break;
}
return result;
}
}
/// <summary>
/// Covers V2 API, see
/// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
/// </summary>
public static class JobState
{
public const UInt16 New = 2;
public const UInt16 Starting = 3;
public const UInt16 Running = 4;
public const UInt16 Suspended = 5;
public const UInt16 ShuttingDown = 6;
public const UInt16 Completed = 7;
public const UInt16 Terminated = 8;
public const UInt16 Killed = 9;
public const UInt16 Exception = 10;
public const UInt16 Service = 11;
public static string ToString(UInt16 value)
{
string result = "Unknown JobState code";
switch (value)
{
case New: result = "New"; break;
case Starting: result = "Starting"; break;
case Running: result = "Running"; break;
case Suspended: result = "Suspended"; break;
case ShuttingDown: result = "ShuttingDown"; break;
case Completed: result = "Completed"; break;
case Terminated: result = "Terminated"; break;
case Killed: result = "Killed"; break;
case Exception: result = "Exception"; break;
case Service: result = "Service"; break;
}
return result;
}
}
/// <summary>
/// V2 API (see http://msdn.microsoft.com/en-us/library/hh850279(v=vs.85).aspx)
/// has removed 'Paused' and 'Suspended' as compared to the
/// V1 API (see http://msdn.microsoft.com/en-us/library/cc723874%28v=vs.85%29.aspx)
/// However, Paused and Suspended appear on the VM state transition table
/// (see http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods)
/// </summary>
public class RequiredState
{
public const UInt16 Enabled = 2; // Turns the VM on.
public const UInt16 Disabled = 3; // Turns the VM off.
public const UInt16 ShutDown = 4;
public const UInt16 Offline = 6;
public const UInt16 Test = 7;
public const UInt16 Defer = 8;
public const UInt16 Quiesce = 9;
public const UInt16 Reboot = 10; // A hard reset of the VM.
public const UInt16 Reset = 11; // For future use.
public const UInt16 Paused = 32768; // Pauses the VM.
public const UInt16 Suspended = 32769; // Saves the state of the VM.
public static string ToString(UInt16 value)
{
string result = "Unknown RequiredState code";
switch (value)
{
case Enabled: result = "Enabled"; break;
case Disabled: result = "Disabled"; break;
case ShutDown: result = "ShutDown"; break;
case Offline: result = "Offline"; break;
case Test: result = "Test"; break;
case Defer: result = "Defer"; break;
case Quiesce: result = "Quiesce"; break;
case Reboot: result = "Reboot"; break;
case Reset: result = "Reset"; break;
}
return result;
}
}
/// <summary>
/// V2 API specifies the states below in its state transition graph at
/// http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods
/// However, the CIM standard has additional possibilities based on the description
/// of EnabledState.
/// The previous V1 API is described by
/// http://msdn.microsoft.com/en-us/library/cc136822%28v=vs.85%29.aspx
/// </summary>
public class EnabledState
{
/// <summary>
/// The state of the VM could not be determined.
/// </summary>
public const UInt16 Unknown = 0;
/// <summary>
/// The VM is running.
/// </summary>
public const UInt16 Enabled = 2;
/// <summary>
/// The VM is turned off.
/// </summary>
public const UInt16 Disabled = 3;
/// <summary>
/// The VM is paused.
/// </summary>
public const UInt16 Paused = 32768;
/// <summary>
/// The VM is in a saved state.
/// </summary>
public const UInt16 Suspended = 32769;
/// <summary>
/// The VM is starting. This is a transitional state between 3 (Disabled)
/// or 32769 (Suspended) and 2 (Enabled) initiated by a call to the
/// RequestStateChange method with a RequestedState parameter of 2 (Enabled).
/// </summary>
public const UInt16 Starting = 32770;
/// <summary>
/// Starting with Windows Server 2008 R2 this value is not supported.
/// If the VM is performing a snapshot operation, the element at index 1
/// of the OperationalStatus property array will contain 32768 (Creating Snapshot),
/// 32769 (Applying Snapshot), or 32770 (Deleting Snapshot).
/// </summary>
public const UInt16 Snapshotting = 32771;
/// <summary>
/// The VM is saving its state. This is a transitional state between 2 (Enabled)
/// and 32769 (Suspended) initiated by a call to the RequestStateChange method
/// with a RequestedState parameter of 32769 (Suspended).
/// </summary>
public const UInt16 Saving = 32773;
/// <summary>
/// The VM is turning off. This is a transitional state between 2 (Enabled)
/// and 3 (Disabled) initiated by a call to the RequestStateChange method
/// with a RequestedState parameter of 3 (Disabled) or a guest operating system
/// initiated power off.
/// </summary>
public const UInt16 Stopping = 32774;
/// <summary>
/// The VM is pausing. This is a transitional state between 2 (Enabled) and 32768 (Paused) initiated by a call to the RequestStateChange method with a RequestedState parameter of 32768 (Paused).
/// </summary>
public const UInt16 Pausing = 32776;
/// <summary>
/// The VM is resuming from a paused state. This is a transitional state between 32768 (Paused) and 2 (Enabled).
/// </summary>
public const UInt16 Resuming = 32777;
public static string ToString(UInt16 value)
{
string result = "Unknown";
switch (value)
{
case Enabled: result = "Enabled"; break;
case Disabled: result = "Disabled"; break;
case Paused: result = "Paused"; break;
case Suspended: result = "Suspended"; break;
case Starting: result = "Starting"; break;
case Snapshotting: result = "Snapshotting"; break; // NOT used
case Saving: result = "Saving"; break;
case Stopping: result = "Stopping"; break;
case Pausing: result = "Pausing"; break;
case Resuming: result = "Resuming"; break;
}
return result;
}
public static string ToCloudStackState(UInt16 value)
{
string result = "Unknown";
switch (value)
{
case Enabled: result = "Running"; break;
case Disabled: result = "Stopped"; break;
case Paused: result = "Unknown"; break;
case Suspended: result = "Unknown"; break;
case Starting: result = "Starting"; break;
case Snapshotting: result = "Unknown"; break; // NOT used
case Saving: result = "Saving"; break;
case Stopping: result = "Stopping"; break;
case Pausing: result = "Unknown"; break;
case Resuming: result = "Starting"; break;
}
return result;
}
}
}