1""" 2 :codeauthor: Rahul Handay <rahulha@saltstack.com> 3""" 4 5 6import types 7from datetime import datetime 8 9import salt.modules.win_system as win_system 10import salt.utils.platform 11import salt.utils.stringutils 12from tests.support.mixins import LoaderModuleMockMixin 13from tests.support.mock import MagicMock, Mock, patch 14from tests.support.unit import TestCase, skipIf 15 16try: 17 import wmi 18 19 HAS_WMI = True 20except ImportError: 21 HAS_WMI = False 22 23 24class MockWMI_ComputerSystem: 25 """ 26 Mock WMI Win32_ComputerSystem Class 27 """ 28 29 BootupState = "Normal boot" 30 Caption = "SALT SERVER" 31 ChassisBootupState = 3 32 ChassisSKUNumber = "3.14159" 33 DNSHostname = "SALT SERVER" 34 Domain = "WORKGROUP" 35 DomainRole = 2 36 Manufacturer = "Dell Inc." 37 Model = "Dell 2980" 38 NetworkServerModeEnabled = True 39 PartOfDomain = False 40 PCSystemType = 4 41 PowerState = 0 42 Status = "OK" 43 SystemType = "x64-based PC" 44 TotalPhysicalMemory = 17078214656 45 ThermalState = 3 46 Workgroup = "WORKGROUP" 47 48 def __init__(self): 49 pass 50 51 @staticmethod 52 def Rename(Name): 53 return Name == Name 54 55 @staticmethod 56 def JoinDomainOrWorkgroup(Name): 57 return [0] 58 59 @staticmethod 60 def UnjoinDomainOrWorkgroup(Password, UserName, FUnjoinOptions): 61 return [0] 62 63 64class MockWMI_OperatingSystem: 65 """ 66 Mock WMI Win32_OperatingSystem Class 67 """ 68 69 Description = "Because salt goes EVERYWHERE" 70 InstallDate = "20110211131800" 71 LastBootUpTime = "19620612120000" 72 Manufacturer = "Python" 73 Caption = "Salty" 74 NumberOfUsers = 7530000000 75 Organization = "SaltStack" 76 OSArchitecture = "Windows" 77 Primary = True 78 ProductType = 3 79 RegisteredUser = "thatch@saltstack.com" 80 SystemDirectory = "C:\\Windows\\System32" 81 SystemDrive = "C:\\" 82 Version = "10.0.17763" 83 WindowsDirectory = "C:\\Windows" 84 85 def __init__(self): 86 pass 87 88 89class MockWMI_ComputerSystemProduct: 90 def __init__(self): 91 self.SKUNumber = None 92 93 94class MockWMI_Processor: 95 """ 96 Mock WMI Win32_Processor Class 97 """ 98 99 Manufacturer = "Intel" 100 MaxClockSpeed = 2301 101 NumberOfLogicalProcessors = 8 102 NumberOfCores = 4 103 NumberOfEnabledCore = 4 104 105 def __init__(self): 106 pass 107 108 109class MockWMI_BIOS: 110 """ 111 Mock WMI Win32_BIOS Class 112 """ 113 114 SerialNumber = "SALTY2011" 115 Manufacturer = "Dell Inc." 116 Version = "DELL - 10283849" 117 Caption = "A12" 118 BIOSVersion = [Version, Caption, "ASUS - 3948D"] 119 Description = Caption 120 121 def __init__(self): 122 pass 123 124 125@skipIf(not HAS_WMI, "WMI only available on Windows") 126@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") 127class WinSystemTestCase(TestCase, LoaderModuleMockMixin): 128 """ 129 Test cases for salt.modules.win_system 130 """ 131 132 def setup_loader_modules(self): 133 modules_globals = {} 134 # wmi and pythoncom modules are platform specific... 135 mock_pythoncom = types.ModuleType(salt.utils.stringutils.to_str("pythoncom")) 136 sys_modules_patcher = patch.dict("sys.modules", {"pythoncom": mock_pythoncom}) 137 sys_modules_patcher.start() 138 self.addCleanup(sys_modules_patcher.stop) 139 self.WMI = Mock() 140 self.addCleanup(delattr, self, "WMI") 141 modules_globals["wmi"] = wmi 142 143 if win_system.HAS_WIN32NET_MODS is False: 144 win32api = types.ModuleType("win32api") 145 now = datetime.now() 146 win32api.GetLocalTime = MagicMock( 147 return_value=[ 148 now.year, 149 now.month, 150 now.weekday(), 151 now.day, 152 now.hour, 153 now.minute, 154 now.second, 155 now.microsecond, 156 ] 157 ) 158 modules_globals["win32api"] = win32api 159 win32net = types.ModuleType("win32net") 160 win32net.NetServerGetInfo = MagicMock() 161 win32net.NetServerSetInfo = MagicMock() 162 modules_globals["win32net"] = win32net 163 164 return {win_system: modules_globals} 165 166 def test_halt(self): 167 """ 168 Test to halt a running system 169 """ 170 mock = MagicMock(return_value="salt") 171 with patch.object(win_system, "shutdown", mock): 172 self.assertEqual(win_system.halt(), "salt") 173 174 def test_init(self): 175 """ 176 Test to change the system runlevel on sysV compatible systems 177 """ 178 self.assertEqual(win_system.init(3), "Not implemented on Windows at this time.") 179 180 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 181 def test_poweroff(self): 182 """ 183 Test to poweroff a running system 184 """ 185 mock = MagicMock(return_value="salt") 186 with patch.object(win_system, "shutdown", mock): 187 self.assertEqual(win_system.poweroff(), "salt") 188 189 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 190 def test_reboot(self): 191 """ 192 Test to reboot the system 193 """ 194 with patch("salt.modules.win_system.shutdown", MagicMock(return_value=True)): 195 self.assertEqual(win_system.reboot(), True) 196 197 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 198 def test_reboot_with_timeout_in_minutes(self): 199 """ 200 Test to reboot the system with a timeout 201 """ 202 with patch( 203 "salt.modules.win_system.shutdown", MagicMock(return_value=True) 204 ) as shutdown: 205 self.assertEqual(win_system.reboot(5, in_seconds=False), True) 206 shutdown.assert_called_with( 207 timeout=5, in_seconds=False, reboot=True, only_on_pending_reboot=False 208 ) 209 210 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 211 def test_reboot_with_timeout_in_seconds(self): 212 """ 213 Test to reboot the system with a timeout 214 """ 215 with patch( 216 "salt.modules.win_system.shutdown", MagicMock(return_value=True) 217 ) as shutdown: 218 self.assertEqual(win_system.reboot(5, in_seconds=True), True) 219 shutdown.assert_called_with( 220 timeout=5, in_seconds=True, reboot=True, only_on_pending_reboot=False 221 ) 222 223 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 224 def test_reboot_with_wait(self): 225 """ 226 Test to reboot the system with a timeout and 227 wait for it to finish 228 """ 229 with patch( 230 "salt.modules.win_system.shutdown", MagicMock(return_value=True) 231 ), patch("salt.modules.win_system.time.sleep", MagicMock()) as time: 232 self.assertEqual(win_system.reboot(wait_for_reboot=True), True) 233 time.assert_called_with(330) 234 235 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 236 def test_shutdown(self): 237 """ 238 Test to shutdown a running system 239 """ 240 with patch( 241 "salt.modules.win_system.win32api.InitiateSystemShutdown", MagicMock() 242 ): 243 self.assertEqual(win_system.shutdown(), True) 244 245 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 246 def test_shutdown_hard(self): 247 """ 248 Test to shutdown a running system with no timeout or warning 249 """ 250 with patch( 251 "salt.modules.win_system.shutdown", MagicMock(return_value=True) 252 ) as shutdown: 253 self.assertEqual(win_system.shutdown_hard(), True) 254 shutdown.assert_called_with(timeout=0) 255 256 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 257 def test_set_computer_name(self): 258 """ 259 Test to set the Windows computer name 260 """ 261 with patch( 262 "salt.modules.win_system.windll.kernel32.SetComputerNameExW", 263 MagicMock(return_value=True), 264 ), patch.object( 265 win_system, "get_computer_name", MagicMock(return_value="salt") 266 ), patch.object( 267 win_system, 268 "get_pending_computer_name", 269 MagicMock(return_value="salt_new"), 270 ): 271 self.assertDictEqual( 272 win_system.set_computer_name("salt_new"), 273 {"Computer Name": {"Current": "salt", "Pending": "salt_new"}}, 274 ) 275 # Test set_computer_name failure 276 with patch( 277 "salt.modules.win_system.windll.kernel32.SetComputerNameExW", 278 MagicMock(return_value=False), 279 ): 280 self.assertFalse(win_system.set_computer_name("salt")) 281 282 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 283 def test_set_computer_desc(self): 284 """ 285 Test to set the Windows computer description 286 """ 287 mock = MagicMock() 288 mock_get_info = MagicMock(return_value={"comment": ""}) 289 mock_get_desc = MagicMock(return_value="Salt's comp") 290 with patch( 291 "salt.modules.win_system.win32net.NetServerGetInfo", mock_get_info 292 ), patch( 293 "salt.modules.win_system.win32net.NetServerSetInfo", mock 294 ), patch.object( 295 win_system, "get_computer_desc", mock_get_desc 296 ): 297 self.assertDictEqual( 298 win_system.set_computer_desc("Salt's comp"), 299 {"Computer Description": "Salt's comp"}, 300 ) 301 302 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 303 def test_get_computer_desc(self): 304 """ 305 Test to get the Windows computer description 306 """ 307 with patch( 308 "salt.modules.win_system.get_system_info", 309 MagicMock( 310 side_effect=[{"description": "salt description"}, {"description": None}] 311 ), 312 ): 313 self.assertEqual(win_system.get_computer_desc(), "salt description") 314 self.assertFalse(win_system.get_computer_desc()) 315 316 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 317 def test_join_domain(self): 318 """ 319 Test to join a computer to an Active Directory domain 320 """ 321 with patch( 322 "salt.modules.win_system._join_domain", MagicMock(return_value=0) 323 ), patch( 324 "salt.modules.win_system.get_domain_workgroup", 325 MagicMock(return_value={"Workgroup": "Workgroup"}), 326 ): 327 self.assertDictEqual( 328 win_system.join_domain("saltstack", "salt", "salt@123"), 329 {"Domain": "saltstack", "Restart": False}, 330 ) 331 332 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 333 def test_join_domain_already_joined(self): 334 """ 335 Test to join a computer to an Active Directory domain when it is 336 already joined 337 """ 338 with patch( 339 "salt.modules.win_system._join_domain", MagicMock(return_value=0) 340 ), patch( 341 "salt.modules.win_system.get_domain_workgroup", 342 MagicMock(return_value={"Domain": "saltstack"}), 343 ): 344 self.assertEqual( 345 win_system.join_domain("saltstack", "salt", "salt@123"), 346 "Already joined to saltstack", 347 ) 348 349 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 350 def test_unjoin_domain(self): 351 """ 352 Test unjoining a computer from an Active Directory domain 353 """ 354 with patch("salt.utils.winapi.Com", MagicMock()), patch.object( 355 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 356 ), patch.object(wmi, "WMI", Mock(return_value=self.WMI)), patch( 357 "salt.modules.win_system.get_domain_workgroup", 358 MagicMock(return_value={"Domain": "contoso.com"}), 359 ): 360 self.assertDictEqual( 361 win_system.unjoin_domain(), 362 {"Workgroup": "WORKGROUP", "Restart": False}, 363 ) 364 365 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 366 def test_unjoin_domain_already_unjoined(self): 367 """ 368 Test unjoining a computer from an Active Directory domain 369 """ 370 with patch("salt.utils.winapi.Com", MagicMock()), patch.object( 371 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 372 ), patch.object(wmi, "WMI", Mock(return_value=self.WMI)), patch( 373 "salt.modules.win_system.get_domain_workgroup", 374 MagicMock(return_value={"Workgroup": "WORKGROUP"}), 375 ): 376 self.assertEqual(win_system.unjoin_domain(), "Already joined to WORKGROUP") 377 378 def test_get_system_time(self): 379 """ 380 Test to get system time 381 """ 382 tm = datetime.strftime(datetime.now(), "%I:%M:%S %p") 383 win_tm = win_system.get_system_time() 384 try: 385 self.assertEqual(win_tm, tm) 386 except AssertionError: 387 # handle race condition 388 import re 389 390 self.assertTrue(re.search(r"^\d{2}:\d{2} \w{2}$", win_tm)) 391 392 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 393 def test_set_system_time(self): 394 """ 395 Test to set system time 396 """ 397 with patch( 398 "salt.modules.win_system.set_system_date_time", 399 MagicMock(side_effect=[False, True]), 400 ): 401 self.assertFalse(win_system.set_system_time("11:31:15 AM")) 402 self.assertTrue(win_system.set_system_time("11:31:15 AM")) 403 404 def test_get_system_date(self): 405 """ 406 Test to get system date 407 """ 408 date = datetime.strftime(datetime.now(), "%m/%d/%Y") 409 self.assertEqual(win_system.get_system_date(), date) 410 411 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 412 def test_set_system_date(self): 413 """ 414 Test to set system date 415 """ 416 with patch( 417 "salt.modules.win_system.set_system_date_time", 418 MagicMock(side_effect=[False, True]), 419 ): 420 self.assertFalse(win_system.set_system_date("03-28-13")) 421 self.assertTrue(win_system.set_system_date("03-28-13")) 422 423 def test_start_time_service(self): 424 """ 425 Test to start the Windows time service 426 """ 427 mock = MagicMock(return_value=True) 428 with patch.dict(win_system.__salt__, {"service.start": mock}): 429 self.assertTrue(win_system.start_time_service()) 430 431 def test_stop_time_service(self): 432 """ 433 Test to stop the windows time service 434 """ 435 mock = MagicMock(return_value=True) 436 with patch.dict(win_system.__salt__, {"service.stop": mock}): 437 self.assertTrue(win_system.stop_time_service()) 438 439 def test_set_hostname(self): 440 """ 441 Test setting a new hostname 442 """ 443 with patch("salt.utils.winapi.Com", MagicMock()), patch.object( 444 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 445 ), patch.object(wmi, "WMI", Mock(return_value=self.WMI)): 446 self.assertTrue(win_system.set_hostname("NEW")) 447 448 def test_get_domain_workgroup(self): 449 """ 450 Test get_domain_workgroup 451 """ 452 with patch.object(wmi, "WMI", Mock(return_value=self.WMI)), patch( 453 "salt.utils.winapi.Com", MagicMock() 454 ), patch.object( 455 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 456 ): 457 self.assertDictEqual( 458 win_system.get_domain_workgroup(), {"Workgroup": "WORKGROUP"} 459 ) 460 461 def test_set_domain_workgroup(self): 462 """ 463 Test set_domain_workgroup 464 """ 465 with patch.object(wmi, "WMI", Mock(return_value=self.WMI)), patch( 466 "salt.utils.winapi.Com", MagicMock() 467 ), patch.object( 468 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 469 ): 470 self.assertTrue(win_system.set_domain_workgroup("test")) 471 472 def test_get_hostname(self): 473 """ 474 Test getting a new hostname 475 """ 476 cmd_run_mock = MagicMock(return_value="MINION") 477 with patch.dict(win_system.__salt__, {"cmd.run": cmd_run_mock}): 478 ret = win_system.get_hostname() 479 self.assertEqual(ret, "MINION") 480 cmd_run_mock.assert_called_once_with(cmd="hostname") 481 482 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 483 def test_get_system_info(self): 484 fields = [ 485 "bios_caption", 486 "bios_description", 487 "bios_details", 488 "bios_manufacturer", 489 "bios_version", 490 "bootup_state", 491 "caption", 492 "chassis_bootup_state", 493 "chassis_sku_number", 494 "description", 495 "dns_hostname", 496 "domain", 497 "domain_role", 498 "hardware_manufacturer", 499 "hardware_model", 500 "hardware_serial", 501 "install_date", 502 "last_boot", 503 "name", 504 "network_server_mode_enabled", 505 "organization", 506 "os_architecture", 507 "os_manufacturer", 508 "os_name", 509 "os_type", 510 "os_version", 511 "part_of_domain", 512 "pc_system_type", 513 "power_state", 514 "primary", 515 "processor_cores", 516 "processor_cores_enabled", 517 "processor_manufacturer", 518 "processor_max_clock_speed", 519 "processors", 520 "processors_logical", 521 "registered_user", 522 "status", 523 "system_directory", 524 "system_drive", 525 "system_type", 526 "thermal_state", 527 "total_physical_memory", 528 "total_physical_memory_raw", 529 "users", 530 "windows_directory", 531 "workgroup", 532 ] 533 with patch("salt.utils.win_system.get_computer_name", MagicMock()), patch( 534 "salt.utils.winapi.Com", MagicMock() 535 ), patch.object( 536 self.WMI, "Win32_OperatingSystem", return_value=[MockWMI_OperatingSystem()] 537 ), patch.object( 538 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 539 ), patch.object( 540 self.WMI, 541 "Win32_ComputerSystemProduct", 542 return_value=[MockWMI_ComputerSystemProduct()], 543 ), patch.object( 544 self.WMI, 545 "Win32_Processor", 546 return_value=[MockWMI_Processor(), MockWMI_Processor()], 547 ), patch.object( 548 self.WMI, "Win32_BIOS", return_value=[MockWMI_BIOS()] 549 ), patch.object( 550 wmi, "WMI", Mock(return_value=self.WMI) 551 ): 552 ret = win_system.get_system_info() 553 # Make sure all the fields are in the return 554 for field in fields: 555 self.assertIn(field, ret) 556 # os_type 557 os_types = ["Work Station", "Domain Controller", "Server"] 558 self.assertIn(ret["os_type"], os_types) 559 domain_roles = [ 560 "Standalone Workstation", 561 "Member Workstation", 562 "Standalone Server", 563 "Member Server", 564 "Backup Domain Controller", 565 "Primary Domain Controller", 566 ] 567 self.assertIn(ret["domain_role"], domain_roles) 568 system_types = [ 569 "Unspecified", 570 "Desktop", 571 "Mobile", 572 "Workstation", 573 "Enterprise Server", 574 "SOHO Server", 575 "Appliance PC", 576 "Performance Server", 577 "Slate", 578 "Maximum", 579 ] 580 self.assertIn(ret["pc_system_type"], system_types) 581 warning_states = [ 582 "Other", 583 "Unknown", 584 "Safe", 585 "Warning", 586 "Critical", 587 "Non-recoverable", 588 ] 589 self.assertIn(ret["chassis_bootup_state"], warning_states) 590 self.assertIn(ret["thermal_state"], warning_states) 591 592 @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") 593 def test_get_system_info_no_number_of_enabled_core(self): 594 """ 595 Tests get_system_info when there is no `NumberOfEnabledCore` property in 596 the WMI Class Win32_Processor. Older versions of Windows are missing 597 this property 598 """ 599 # Create a mock processor class that does not have the 600 # NumberOfCoresEnabled property 601 class MockWMIProcessor: 602 """ 603 Mock WMI Win32_Processor Class 604 """ 605 606 def __init__(self): 607 self.Manufacturer = "Intel" 608 self.MaxClockSpeed = 2301 609 self.NumberOfLogicalProcessors = 8 610 self.NumberOfCores = 4 611 612 with patch("salt.utils.win_system.get_computer_name", MagicMock()), patch( 613 "salt.utils.winapi.Com", MagicMock() 614 ), patch.object( 615 self.WMI, "Win32_OperatingSystem", return_value=[MockWMI_OperatingSystem()] 616 ), patch.object( 617 self.WMI, "Win32_ComputerSystem", return_value=[MockWMI_ComputerSystem()] 618 ), patch.object( 619 self.WMI, 620 "Win32_ComputerSystemProduct", 621 return_value=[MockWMI_ComputerSystemProduct()], 622 ), patch.object( 623 self.WMI, 624 "Win32_Processor", 625 return_value=[MockWMIProcessor(), MockWMIProcessor()], 626 ), patch.object( 627 self.WMI, "Win32_BIOS", return_value=[MockWMI_BIOS()] 628 ), patch.object( 629 wmi, "WMI", Mock(return_value=self.WMI) 630 ): 631 ret = win_system.get_system_info() 632 self.assertIn("processors", ret) 633 self.assertIn("processors_logical", ret) 634 self.assertIn("processor_cores", ret) 635 self.assertIn("processor_manufacturer", ret) 636 self.assertIn("processor_max_clock_speed", ret) 637 self.assertNotIn("processor_cores_enabled", ret) 638