diff --git a/perfkitbenchmarker/azure/azure_virtual_machine.py b/perfkitbenchmarker/azure/azure_virtual_machine.py index b6e3bf2135..60fc43d1ba 100644 --- a/perfkitbenchmarker/azure/azure_virtual_machine.py +++ b/perfkitbenchmarker/azure/azure_virtual_machine.py @@ -34,6 +34,7 @@ from perfkitbenchmarker import resource from perfkitbenchmarker import virtual_machine from perfkitbenchmarker import vm_util +from perfkitbenchmarker import windows_virtual_machine from perfkitbenchmarker.azure import azure_disk from perfkitbenchmarker.azure import azure_network @@ -44,6 +45,8 @@ '14_04_1-LTS-amd64-server-20150123-en-us-30GB') CENTOS_IMAGE = ('0b11de9248dd4d87b18621318e037d37__RightImage-' 'CentOS-7.0-x64-v14.2.1') +WINDOWS_IMAGE = ('a699494373c04fc0bc8f2bb1389d6106__Windows-Server' + '-2012-R2-201505.01-en.us-127GB.vhd') class AzureService(resource.BaseResource): @@ -108,7 +111,6 @@ def __init__(self, vm_spec): disk_spec = disk.BaseDiskSpec(None, None, None) self.os_disk = azure_disk.AzureDisk(disk_spec, self.name) self.max_local_disks = 1 - self.local_disk_counter = 0 @classmethod def SetVmSpecDefaults(cls, vm_spec): @@ -126,22 +128,26 @@ def _CreateDependencies(self): def _DeleteDependencies(self): """Delete VM dependencies.""" - self.os_disk.Delete() + if self.os_disk.name: + self.os_disk.Delete() self.service.Delete() def _Create(self): create_cmd = [AZURE_PATH, 'vm', 'create', - '--ssh-cert=%s' % vm_util.GetCertPath(), - '--ssh=22', - '--no-ssh-password', '--affinity-group=%s' % self.network.affinity_group.name, '--virtual-network-name=%s' % self.network.vnet.name, '--vm-size=%s' % self.machine_type, self.name, self.image, self.user_name] + if self.password: + create_cmd.append(self.password) + else: + create_cmd.extend(['--ssh=%d' % self.ssh_port, + '--ssh-cert=%s' % vm_util.GetCertPath(), + '--no-ssh-password']) vm_util.IssueCommand(create_cmd) def _Delete(self): @@ -187,18 +193,26 @@ def CreateScratchDisk(self, disk_spec): Args: disk_spec: virtual_machine.BaseDiskSpec object of the disk. """ - if disk_spec.disk_type == disk.LOCAL: - self.local_disk_counter += disk_spec.num_striped_disks - if self.local_disk_counter > self.max_local_disks: - raise errors.Error('Not enough local disks.') - - # Instantiate the disk(s) that we want to create. - disks = [azure_disk.AzureDisk(disk_spec, self.name) - for _ in range(disk_spec.num_striped_disks)] + disks = [] + + for _ in xrange(disk_spec.num_striped_disks): + data_disk = azure_disk.AzureDisk(disk_spec, self.name) + if disk_spec.disk_type == disk.LOCAL: + # Local disk numbers start at 1 (0 is the system disk). + data_disk.disk_number = self.local_disk_counter + 1 + self.local_disk_counter += 1 + if self.local_disk_counter > self.max_local_disks: + raise errors.Error('Not enough local disks.') + else: + # Remote disk numbers start at 1 + max_local disks (0 is the system disk + # and local disks occupy [1, max_local_disks]). + data_disk.disk_number = (self.remote_disk_counter + + 1 + self.max_local_disks) + self.remote_disk_counter += 1 + disks.append(data_disk) self._CreateScratchDiskFromDisks(disk_spec, disks) - def GetLocalDisks(self): """Returns a list of local disks on the VM. @@ -217,3 +231,29 @@ class DebianBasedAzureVirtualMachine(AzureVirtualMachine, class RhelBasedAzureVirtualMachine(AzureVirtualMachine, linux_virtual_machine.RhelMixin): DEFAULT_IMAGE = CENTOS_IMAGE + + +class WindowsAzureVirtualMachine(AzureVirtualMachine, + windows_virtual_machine.WindowsMixin): + + DEFAULT_IMAGE = WINDOWS_IMAGE + + def __init__(self, vm_spec): + super(WindowsAzureVirtualMachine, self).__init__(vm_spec) + self.user_name = self.name + self.password = vm_util.GenerateRandomWindowsPassword() + + def _PostCreate(self): + super(WindowsAzureVirtualMachine, self)._PostCreate() + config_dict = {'commandToExecute': windows_virtual_machine.STARTUP_SCRIPT} + config = json.dumps(config_dict) + set_extension_command = [AZURE_PATH, + 'vm', + 'extension', + 'set', + self.name, + 'CustomScriptExtension', + 'Microsoft.Compute', + '1.4', + '--public-config=%s' % config] + vm_util.IssueRetryableCommand(set_extension_command) diff --git a/perfkitbenchmarker/benchmark_spec.py b/perfkitbenchmarker/benchmark_spec.py index ba007b906f..1f0ae0dd4f 100644 --- a/perfkitbenchmarker/benchmark_spec.py +++ b/perfkitbenchmarker/benchmark_spec.py @@ -63,7 +63,8 @@ AZURE: { VIRTUAL_MACHINE: { DEBIAN: azure_virtual_machine.DebianBasedAzureVirtualMachine, - RHEL: azure_virtual_machine.RhelBasedAzureVirtualMachine + RHEL: azure_virtual_machine.RhelBasedAzureVirtualMachine, + WINDOWS: azure_virtual_machine.WindowsAzureVirtualMachine }, FIREWALL: azure_network.AzureFirewall }, diff --git a/perfkitbenchmarker/vm_util.py b/perfkitbenchmarker/vm_util.py index aa7c2f39b7..2fd2d2fb01 100644 --- a/perfkitbenchmarker/vm_util.py +++ b/perfkitbenchmarker/vm_util.py @@ -22,6 +22,7 @@ import random import re import socket +import string import subprocess import tempfile import threading @@ -60,6 +61,7 @@ MAX_RETRIES = -1 WINDOWS = 'nt' +PASSWORD_LENGTH = 15 flags.DEFINE_integer('default_timeout', TIMEOUT, 'The default timeout for ' 'retryable commands in seconds.') @@ -678,3 +680,18 @@ def _RegisterDStatCollector(sender, parsed_flags): output_directory=output_directory) events.before_phase.connect(collector.Start, events.RUN_PHASE, weak=False) events.after_phase.connect(collector.Stop, events.RUN_PHASE, weak=False) + + +def GenerateRandomWindowsPassword(password_length=PASSWORD_LENGTH): + """Generates a password that meets Windows complexity requirements.""" + special_chars = '~!$%*_-+=\\[]:.?/' + password = [ + random.choice(string.ascii_letters + string.digits + special_chars) + for _ in range(password_length - 4)] + # Ensure that the password contains at least one of each 4 required + # character types. + password.append(random.choice(string.ascii_lowercase)) + password.append(random.choice(string.ascii_uppercase)) + password.append(random.choice(string.digits)) + password.append(random.choice(special_chars)) + return ''.join(password)