├── DeployVM-Config └── DeployVM.vmx ├── DeployVM-Scripts ├── change_default_vm_storage_policy.py ├── change_webclient_theme.ps1 ├── deploy.ps1 ├── rc.local ├── setup_vsan_cluster.py └── vcsa.json.template ├── KS.CFG ├── README.md ├── create_sddc_deployment_on_usb_for_linux.sh ├── create_sddc_deployment_on_usb_for_osx.sh └── usb-to-sddc-0.png /DeployVM-Config/DeployVM.vmx: -------------------------------------------------------------------------------- 1 | .encoding = "UTF-8" 2 | config.version = "8" 3 | virtualHW.version = "13" 4 | nvram = "DeployVM.nvram" 5 | pciBridge0.present = "TRUE" 6 | svga.present = "TRUE" 7 | pciBridge4.present = "TRUE" 8 | pciBridge4.virtualDev = "pcieRootPort" 9 | pciBridge4.functions = "8" 10 | pciBridge5.present = "TRUE" 11 | pciBridge5.virtualDev = "pcieRootPort" 12 | pciBridge5.functions = "8" 13 | pciBridge6.present = "TRUE" 14 | pciBridge6.virtualDev = "pcieRootPort" 15 | pciBridge6.functions = "8" 16 | pciBridge7.present = "TRUE" 17 | pciBridge7.virtualDev = "pcieRootPort" 18 | pciBridge7.functions = "8" 19 | vmci0.present = "TRUE" 20 | hpet0.present = "TRUE" 21 | memSize = "2048" 22 | sched.cpu.units = "mhz" 23 | sched.cpu.affinity = "all" 24 | powerType.powerOff = "default" 25 | powerType.suspend = "default" 26 | powerType.reset = "default" 27 | scsi0.virtualDev = "pvscsi" 28 | scsi0.present = "TRUE" 29 | ethernet0.virtualDev = "vmxnet3" 30 | ethernet0.addressType = "vpx" 31 | ethernet0.generatedAddress = "00:50:56:b4:ce:9c" 32 | ethernet0.uptCompatibility = "TRUE" 33 | ethernet0.present = "TRUE" 34 | ethernet0.networkName = "VM Network" 35 | displayName = "DeployVM" 36 | guestOS = "vmware-photon-64" 37 | toolScripts.afterPowerOn = "TRUE" 38 | toolScripts.afterResume = "TRUE" 39 | toolScripts.beforeSuspend = "TRUE" 40 | toolScripts.beforePowerOff = "TRUE" 41 | sched.cpu.min = "0" 42 | sched.cpu.shares = "normal" 43 | sched.mem.min = "0" 44 | sched.mem.minSize = "0" 45 | sched.mem.shares = "normal" 46 | numa.autosize.vcpu.maxPerVirtualNode = "1" 47 | numa.autosize.cookie = "10001" 48 | pciBridge0.pciSlotNumber = "17" 49 | pciBridge4.pciSlotNumber = "21" 50 | pciBridge5.pciSlotNumber = "22" 51 | pciBridge6.pciSlotNumber = "23" 52 | pciBridge7.pciSlotNumber = "24" 53 | scsi0.pciSlotNumber = "160" 54 | ethernet0.pciSlotNumber = "192" 55 | vmci0.pciSlotNumber = "32" 56 | sata0.pciSlotNumber = "33" 57 | scsi0.sasWWID = "50 05 05 64 74 43 1e c0" 58 | vmci0.id = "229114788" 59 | monitor.phys_bits_used = "43" 60 | vmotion.checkpointFBSize = "4194304" 61 | vmotion.checkpointSVGAPrimarySize = "4194304" 62 | cleanShutdown = "TRUE" 63 | softPowerOff = "TRUE" 64 | svga.guestBackedPrimaryAware = "TRUE" 65 | tools.syncTime = "FALSE" 66 | scsi0:0.deviceType = "scsi-hardDisk" 67 | scsi0:0.fileName = "DeployVM.vmdk" 68 | sched.scsi0:0.shares = "normal" 69 | sched.scsi0:0.throughputCap = "off" 70 | scsi0:0.present = "TRUE" 71 | scsi0:0.redo = "" 72 | ide0:0.deviceType = "cdrom-image" 73 | ide0:0.present = "TRUE" 74 | ide0:0.fileName = "/vmfs/volumes/BLAH/DeployVM/VCSA.iso" 75 | -------------------------------------------------------------------------------- /DeployVM-Scripts/change_default_vm_storage_policy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pyVmomi import vim, pbm, VmomiSupport 3 | from pyVim.connect import SmartConnect, Disconnect 4 | 5 | import atexit 6 | import argparse 7 | import ast 8 | import getpass 9 | import sys 10 | import ssl 11 | 12 | """ 13 | Example of using Storage Policy Based Management (SPBM) API 14 | to update an existing VM Storage Policy. 15 | 16 | Required Prviledge: Profile-driven storage update 17 | """ 18 | 19 | __author__ = 'William Lam' 20 | 21 | 22 | # retrieve SPBM API endpoint 23 | def GetPbmConnection(vpxdStub): 24 | import Cookie 25 | import pyVmomi 26 | sessionCookie = vpxdStub.cookie.split('"')[1] 27 | httpContext = VmomiSupport.GetHttpContext() 28 | cookie = Cookie.SimpleCookie() 29 | cookie["vmware_soap_session"] = sessionCookie 30 | httpContext["cookies"] = cookie 31 | VmomiSupport.GetRequestContext()["vcSessionCookie"] = sessionCookie 32 | hostname = vpxdStub.host.split(":")[0] 33 | pbmStub = pyVmomi.SoapStubAdapter( 34 | host=hostname, 35 | version="pbm.version.version1", 36 | path="/pbm/sdk", 37 | poolSize=0, 38 | sslContext=ssl._create_unverified_context()) 39 | pbmSi = pbm.ServiceInstance("ServiceInstance", pbmStub) 40 | pbmContent = pbmSi.RetrieveContent() 41 | 42 | return (pbmSi, pbmContent) 43 | 44 | 45 | # Create required SPBM Capability object from python dict 46 | def _dictToCapability(d): 47 | return [ 48 | pbm.capability.CapabilityInstance( 49 | id=pbm.capability.CapabilityMetadata.UniqueId( 50 | namespace=k.split('.')[0], 51 | id=k.split('.')[1] 52 | ), 53 | constraint=[ 54 | pbm.capability.ConstraintInstance( 55 | propertyInstance=[ 56 | pbm.capability.PropertyInstance( 57 | id=k.split('.')[1], 58 | value=v 59 | ) 60 | ] 61 | ) 62 | ] 63 | ) 64 | for k, v in d.iteritems() 65 | ] 66 | 67 | 68 | # Update existing VM Storage Policy 69 | def UpdateProfile(pm, profile, rules): 70 | pm.PbmUpdate( 71 | profileId=profile.profileId, 72 | updateSpec=pbm.profile.CapabilityBasedProfileUpdateSpec( 73 | description=None, 74 | constraints=pbm.profile.SubProfileCapabilityConstraints( 75 | subProfiles=[ 76 | pbm.profile.SubProfileCapabilityConstraints.SubProfile( 77 | name="Object", 78 | capability=_dictToCapability(rules) 79 | ) 80 | ] 81 | ) 82 | ) 83 | ) 84 | 85 | 86 | def GetArgs(): 87 | """ 88 | Supports the command-line arguments listed below. 89 | """ 90 | parser = argparse.ArgumentParser( 91 | description='Process args for VSAN SDK sample application') 92 | parser.add_argument('-s', '--host', required=True, action='store', 93 | help='Remote host to connect to') 94 | parser.add_argument('-o', '--port', type=int, default=443, action='store', 95 | help='Port to connect on') 96 | parser.add_argument('-u', '--user', required=True, action='store', 97 | help='User name to use when connecting to host') 98 | parser.add_argument('-p', '--password', required=False, action='store', 99 | help='Password to use when connecting to host') 100 | parser.add_argument('-n', '--policy-name', required=True, action='store', 101 | help='VM Storage Policy ID') 102 | parser.add_argument('-r', '--policy-rule', required=True, action='store', 103 | help="VM Storage Policy Rule encoded as dictionary" 104 | "example:" 105 | " \"{\'VSAN.hostFailuresToTolerate\':1," 106 | " \'VSAN.stripeWidth\':2," 107 | " \'VSAN.forceProvisioning\':False}\"") 108 | args = parser.parse_args() 109 | return args 110 | 111 | 112 | # Start program 113 | def main(): 114 | args = GetArgs() 115 | if args.password: 116 | password = args.password 117 | else: 118 | password = getpass.getpass(prompt='Enter password for host %s and ' 119 | 'user %s: ' % (args.host, args.user)) 120 | 121 | si = SmartConnect(host=args.host, 122 | user=args.user, 123 | pwd=password, 124 | port=int(args.port), 125 | sslContext=ssl._create_unverified_context()) 126 | 127 | atexit.register(Disconnect, si) 128 | 129 | # Connect to SPBM Endpoint 130 | pbmSi, pbmContent = GetPbmConnection(si._stub) 131 | 132 | pm = pbmContent.profileManager 133 | profileIds = pm.PbmQueryProfile( 134 | resourceType=pbm.profile.ResourceType(resourceType="STORAGE"), 135 | profileCategory="REQUIREMENT" 136 | ) 137 | 138 | profiles = [] 139 | if len(profileIds) > 0: 140 | profiles = pm.PbmRetrieveContent(profileIds=profileIds) 141 | 142 | # Attempt to find profile name given by user 143 | for profile in profiles: 144 | if profile.name == args.policy_name: 145 | vmProfile = profile 146 | break 147 | 148 | if vmProfile: 149 | # Convert string to dict 150 | vmPolicyRules = ast.literal_eval(args.policy_rule) 151 | 152 | print("Updating VM Storage Policy %s with %s ..." % ( 153 | args.policy_name, args.policy_rule)) 154 | UpdateProfile(pm, vmProfile, vmPolicyRules) 155 | else: 156 | print("Unable to find VM Storage Policy %s " % args.policy_name) 157 | 158 | 159 | # Start program 160 | if __name__ == "__main__": 161 | main() 162 | -------------------------------------------------------------------------------- /DeployVM-Scripts/change_webclient_theme.ps1: -------------------------------------------------------------------------------- 1 | Function Set-VCSATheme { 2 | [CmdletBinding()] 3 | Param( 4 | [Parameter(Mandatory=$true)]$vcsaOSuser, 5 | [Parameter(Mandatory=$true)]$vcsaOSpass, 6 | [Parameter(Mandatory=$true)]$VCSAVM 7 | ) 8 | DynamicParam { 9 | # Set the dynamic parameters' name 10 | $ParameterName = 'Theme' 11 | 12 | # Create the dictionary 13 | $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 14 | 15 | # Create the collection of attributes 16 | $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] 17 | 18 | # Create and set the parameters' attributes 19 | $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute 20 | $ParameterAttribute.Mandatory = $true 21 | $ParameterAttribute.Position = 1 22 | 23 | # Add the attributes to the attributes collection 24 | $AttributeCollection.Add($ParameterAttribute) 25 | 26 | # Generate and set the ValidateSet 27 | $folder = Get-item -path "/root/customize-vsphere-web-client-6.5" 28 | $themefolder = "$($(Get-Childitem -Directory $($folder.fullname)).fullname)" 29 | [System.Collections.ArrayList]$arrSet = Get-ChildItem -Path $themefolder -Directory | Select-Object -ExpandProperty Name 30 | $RestoreOption = $arrSet.Add("Default") 31 | $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) 32 | 33 | # Add the ValidateSet to the attributes collection 34 | $AttributeCollection.Add($ValidateSetAttribute) 35 | 36 | # Create and return the dynamic parameter 37 | $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection) 38 | $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter) 39 | return $RuntimeParameterDictionary 40 | } 41 | 42 | begin { 43 | # Bind the parameter to a friendly variable 44 | $theme = $PsBoundParameters[$ParameterName] 45 | } 46 | 47 | process { 48 | $VCVM = Get-VM $VCSAVM 49 | if ($theme -eq "Default"){ 50 | Write-Verbose "Restoring original theme" 51 | $backupLoginFile = invoke-vmscript -vm $VCVM -GuestUser $vcsaOSuser -GuestPassword $vcsaOSpass -ScriptText "cp /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css.themebak /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css" -ScriptType Bash 52 | Write-Verbose $backupLoginFile 53 | $backupUnpentry = invoke-vmscript -vm $VCVM -GuestUser $vcsaOSuser -GuestPassword $vcsaOSpass -ScriptText "cp /usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp.themebak /usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp" -ScriptType Bash 54 | Write-Verbose $backupunpentry 55 | Write-Host "Theme restored to original" -ForegroundColor Green 56 | return 57 | } 58 | $folder = Get-item -path "/root/customize-vsphere-web-client-6.5" 59 | $themefolder = "$($(Get-Childitem -Directory $($folder.fullname)).fullname)" 60 | if (-Not (Test-Path $themefolder)) { 61 | Write-Warning "$VCVer Theme folder not found at $themefolder, run Get-VCSATheme -VCVer $vCVer to download the latest themes" 62 | Return 63 | } 64 | $UseTheme = $themefolder + "/$theme" 65 | 66 | $bakresponse = invoke-vmscript -vm $VCVM -GuestUser $vcsaOSuser -GuestPassword $vcsaOSpass -ScriptText "ls /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css.themebak" -ScriptType Bash 67 | if ($bakresponse -match "No such file or directory") { 68 | Write-Verbose "No Theme backup found, backing up original theme" 69 | $backupLoginFile = invoke-vmscript -vm $VCVM -GuestUser $vcsaOSuser -GuestPassword $vcsaOSpass -ScriptText "cp /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css.themebak" -ScriptType Bash 70 | Write-Verbose $backupLoginFile 71 | $backupUnpentry = invoke-vmscript -vm $VCVM -GuestUser $vcsaOSuser -GuestPassword $vcsaOSpass -ScriptText "cp /usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp /usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp.themebak" -ScriptType Bash 72 | Write-Verbose $backupunpentry 73 | Write-Verbose "Theme backup created, applying new theme" 74 | } Else { 75 | Write-Verbose "Theme backup already found, applying new theme" 76 | } 77 | Get-Item "$Usetheme/*.png", "$usetheme/*.jpg", "$usetheme/*.gif" | Where { $_.name -notlike "sample*"} | Foreach { 78 | write-verbose "Copying $($_.fullname) to /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/img/$($_.name)" 79 | $_ | Copy-VMGuestFile -LocalToGuest -VM $VCVM -Destination "/usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/img/$($_.name)" -GuestUser $VCSAOSUser -GuestPassword $VCSAOSPass -Force 80 | } 81 | write-verbose "Copying $Usetheme/login.css to /usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css" 82 | Get-Item "$Usetheme/login.css" | Copy-VMGuestFile -LocalToGuest -VM $VCVM -Destination "/usr/lib/vmware-sso/vmware-sts/webapps/websso/resources/css/login.css" -GuestUser $VCSAOSUser -GuestPassword $VCSAOSPass -Force 83 | write-verbose "Copying $Usetheme/unpentry.jsp to /usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp" 84 | Get-Item "$Usetheme/unpentry.jsp" | Copy-VMGuestFile -LocalToGuest -VM $VCVM -Destination "/usr/lib/vmware-sso/vmware-sts/webapps/websso/WEB-INF/views/unpentry.jsp" -GuestUser $VCSAOSUser -GuestPassword $VCSAOSPass -Force 85 | Write-Host "Theme Uploaded!" -ForegroundColor Green 86 | } 87 | } 88 | 89 | $DeployLogFile = "/root/script.log" 90 | 91 | . /root/config.ps1 92 | 93 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-File -Append -LiteralPath $DeployLogFile 94 | 95 | Connect-VIServer -Server $VI_SERVER -User $VI_USERNAME -password $VI_PASSWORD | Out-File -Append -LiteralPath $DeployLogFile 96 | 97 | Set-VCSATheme -Theme $VCSA_WEBCLIENT_THEME_NAME ` 98 | -VCSAVM "Embedded-vCenter-Server-Appliance" ` 99 | -VCSAOSUser "root" ` 100 | -VCSAOSPass $VCSA_ROOT_PASSWORD ` 101 | -Verbose 102 | 103 | Disconnect-VIServer * -confirm:$false | Out-File -Append -LiteralPath $DeployLogFile 104 | -------------------------------------------------------------------------------- /DeployVM-Scripts/deploy.ps1: -------------------------------------------------------------------------------- 1 | $DeployLogFile = "/root/script.log" 2 | 3 | $StartTime = Get-Date 4 | 5 | . /root/config.ps1 6 | 7 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-File -Append -LiteralPath $DeployLogFile 8 | 9 | Connect-VIServer -Server $VI_SERVER -User $VI_USERNAME -password $VI_PASSWORD | Out-File -Append -LiteralPath $DeployLogFile 10 | 11 | "Adding ESXi Host $ESX_SERVER to vCenter ..." | Out-File -Append -LiteralPath $DeployLogFile 12 | Add-VMHost -Location (Get-Cluster -Name $VI_CLUSTER) -User root -Password $ESX_PASSWORD -Name $ESX_SERVER -Force | Out-File -Append -LiteralPath $DeployLogFile 13 | 14 | "Configuring Host syslog to VC ..." | Out-File -Append -LiteralPath $DeployLogFile 15 | Get-VMHost | Set-VMHostSysLogServer -SysLogServer $VI_SERVER | Out-File -Append -LiteralPath $DeployLogFile 16 | 17 | "Enabling VM Autostart for the VCSA VM ..." | Out-File -Append -LiteralPath $DeployLogFile 18 | $VCVM = Get-VM -Name "Embedded-vCenter-Server-Appliance" 19 | $vmstartpolicy = Get-VMStartPolicy -VM $VCVM 20 | Set-VMHostStartPolicy (Get-VMHost $ESX_SERVER | Get-VMHostStartPolicy) -Enabled:$true | Out-File -Append -LiteralPath $DeployLogFile 21 | Set-VMStartPolicy -StartPolicy $vmstartpolicy -StartAction PowerOn -StartDelay 0 | Out-File -Append -LiteralPath $DeployLogFile 22 | 23 | "Acknowledging Alarms on the Cluster ..." | Out-File -Append -LiteralPath $DeployLogFile 24 | $alarmMgr = Get-View AlarmManager 25 | Get-Cluster | Where-Object {$_.ExtensionData.TriggeredAlarmState} | ForEach-Object{ 26 | $cluster = $_ 27 | $entity_moref = $cluster.ExtensionData.MoRef 28 | 29 | $cluster.ExtensionData.TriggeredAlarmState | ForEach-Object{ 30 | $alarm_moref = $_.Alarm.value 31 | 32 | "Ack'ing $alarm_moref ..." | Out-File -Append -LiteralPath $DeployLogFile 33 | $alarmMgr.AcknowledgeAlarm($alarm_moref,$entity_moref) | Out-File -Append -LiteralPath $DeployLogFile 34 | } 35 | } 36 | 37 | Disconnect-VIServer * -confirm:$false | Out-File -Append -LiteralPath $DeployLogFile 38 | 39 | $EndTime = Get-Date 40 | $duration = [math]::Round((New-TimeSpan -Start $StartTime -End $EndTime).TotalMinutes,2) 41 | 42 | "================================" | Out-File -Append -LiteralPath $DeployLogFile 43 | "vSphere Lab Deployment Complete!" | Out-File -Append -LiteralPath $DeployLogFile 44 | "StartTime: $StartTime" | Out-File -Append -LiteralPath $DeployLogFile 45 | " EndTime: $EndTime" | Out-File -Append -LiteralPath $DeployLogFile 46 | " Duration: $duration minutes" | Out-File -Append -LiteralPath $DeployLogFile 47 | "" | Out-File -Append -LiteralPath $DeployLogFile 48 | "Access the vSphere Web Client at https://$VI_SERVER/vsphere-client/" | Out-File -Append -LiteralPath $DeployLogFile 49 | "Access the HTML5 vSphere Web Client at https://$VI_SERVER/ui/" | Out-File -Append -LiteralPath $DeployLogFile 50 | "Browse the vSphere REST APIs using the API Explorer here: https://$VI_SERVER/apiexplorer/" | Out-File -Append -LiteralPath $DeployLogFile 51 | "================================" | Out-File -Append -LiteralPath $DeployLogFile 52 | -------------------------------------------------------------------------------- /DeployVM-Scripts/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | export HOME=/root 4 | 5 | if [ -e /root/ran_customized_photon ]; then 6 | exit 7 | else 8 | PHOTON_IP=$(vmtoolsd --cmd "info-get guestinfo.photon_ip") 9 | PHOTON_CIDR=$(vmtoolsd --cmd "info-get guestinfo.photon_cidr") 10 | PHOTON_GATEWAY=$(vmtoolsd --cmd "info-get guestinfo.photon_gateway") 11 | PHOTON_DNS=$(vmtoolsd --cmd "info-get guestinfo.photon_dns") 12 | cat > /etc/systemd/network/10-dhcp-en.network << __CUSTOMIZE_PHOTON__ 13 | [Match] 14 | Name=e* 15 | 16 | [Network] 17 | Address=${PHOTON_IP}/${PHOTON_CIDR} 18 | Gateway=${PHOTON_GATEWAY} 19 | DNS=${PHOTON_DNS} 20 | Domain=code.vmware.com 21 | __CUSTOMIZE_PHOTON__ 22 | systemctl restart systemd-networkd 23 | touch /root/ran_customized_photon 24 | fi 25 | 26 | if [ -e /root/ran_vcsa_deployed ]; then 27 | exit 28 | fi 29 | 30 | LOGFILE=/root/script.log 31 | 32 | echo "Sleeping 60 seconds for system to be initialized ..." 33 | sleep 60 34 | 35 | echo "Running startup script ..." > ${LOGFILE} 36 | 37 | START_TIME=$(date) 38 | 39 | echo "Extracting guestinfo properties and updating /root/vcsa.json ..." 40 | ESXI_HOSTNAME=$(vmtoolsd --cmd "info-get guestinfo.esxi_hostname") 41 | ESXI_PASSWORD=$(vmtoolsd --cmd "info-get guestinfo.esxi_password") 42 | VCSA_IP=$(vmtoolsd --cmd "info-get guestinfo.vcsa_ip") 43 | VCSA_DNS=$(vmtoolsd --cmd "info-get guestinfo.vcsa_dns") 44 | VCSA_PREFIX=$(vmtoolsd --cmd "info-get guestinfo.vcsa_prefix") 45 | VCSA_GATEWAY=$(vmtoolsd --cmd "info-get guestinfo.vcsa_gateway") 46 | VCSA_HOSTNAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_hostname") 47 | VCSA_SSO_DOMAIN_NAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_sso_domain_name") 48 | VCSA_SSO_SITE_NAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_sso_site_name") 49 | VCSA_ROOT_PASSWORD=$(vmtoolsd --cmd "info-get guestinfo.vcsa_root_password") 50 | VCSA_SSO_PASSWORD=$(vmtoolsd --cmd "info-get guestinfo.vcsa_sso_password") 51 | VCSA_SSH_ENABLED=$(vmtoolsd --cmd "info-get guestinfo.vcsa_ssh_enabled") 52 | VCSA_CEIP_ENABLED=$(vmtoolsd --cmd "info-get guestinfo.vcsa_ceip_enabled") 53 | VCSA_DATACENTER_NAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_datacenter_name") 54 | VCSA_CLUSTER_NAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_cluster_name") 55 | VCSA_WEBCLIENT_THEME_NAME=$(vmtoolsd --cmd "info-get guestinfo.vcsa_webclient_theme_name") 56 | 57 | cp /root/vcsa.json.template /root/vcsa.json 58 | sed -i "s/VAR-ESXI-HOSTNAME/${ESXI_HOSTNAME}/g" /root/vcsa.json 59 | sed -i "s/VAR-ESXI-PASSWORD/${ESXI_PASSWORD}/g" /root/vcsa.json 60 | sed -i "s/VAR-VCSA-IP/${VCSA_IP}/g" /root/vcsa.json 61 | sed -i "s/VAR-VCSA-DNS/${VCSA_DNS}/g" /root/vcsa.json 62 | sed -i "s/VAR-VCSA-PREFIX/${VCSA_PREFIX}/g" /root/vcsa.json 63 | sed -i "s/VAR-VCSA-GATEWAY/${VCSA_GATEWAY}/g" /root/vcsa.json 64 | sed -i "s/VAR-VCSA-HOSTNAME/${VCSA_HOSTNAME}/g" /root/vcsa.json 65 | sed -i "s/VAR-VCSA-SSO-DOMAIN-NAME/${VCSA_SSO_DOMAIN_NAME}/g" /root/vcsa.json 66 | sed -i "s/VAR-VCSA-SSO-SITE-NAME/${VCSA_SSO_SITE_NAME}/g" /root/vcsa.json 67 | sed -i "s/VAR-VCSA-SSH-ENABLED/${VCSA_SSH_ENABLED}/g" /root/vcsa.json 68 | sed -i "s/VAR-VCSA-CEIP-ENABLED/${VCSA_CEIP_ENABLED}/g" /root/vcsa.json 69 | sed -i "s/VAR-VCSA-ROOT-PASSWORD/${VCSA_ROOT_PASSWORD}/g" /root/vcsa.json 70 | sed -i "s/VAR-VCSA-SSO-PASSWORD/${VCSA_SSO_PASSWORD}/g" /root/vcsa.json 71 | 72 | echo "Mounting cdrom ..." >> ${LOGFILE} 73 | mkdir -p /mnt/cdrom 74 | mount /dev/cdrom /mnt/cdrom 75 | 76 | echo "Changing into /mnt/cdrom/vcsa-cli-installer/lin64 ..." >> ${LOGFILE} 77 | cd /mnt/cdrom/vcsa-cli-installer/lin64 78 | 79 | echo "Starting deployment ..." >> ${LOGFILE} 80 | ./vcsa-deploy install --accept-eula --acknowledge-ceip --no-esx-ssl-verify /root/vcsa.json >> ${LOGFILE} 81 | cd /root 82 | 83 | echo "Unmounting cdrom ..." >> ${LOGFILE} 84 | umount /mnt/cdrom 85 | 86 | echo "Creating configuration file passed from Deploy VM ..." 87 | VCSA_USERNAME="administrator@${VCSA_SSO_DOMAIN_NAME}" 88 | cat > /root/config.ps1 << __PCLI_VC_CREDENTIAL__ 89 | \$VI_SERVER = "${VCSA_IP}" 90 | \$VI_USERNAME = "${VCSA_USERNAME}" 91 | \$VI_PASSWORD = "${VCSA_SSO_PASSWORD}" 92 | \$VI_DATACENTER = "${VCSA_DATACENTER_NAME}" 93 | \$VI_CLUSTER = "${VCSA_CLUSTER_NAME}" 94 | \$ESX_SERVER = "${ESXI_HOSTNAME}" 95 | \$ESX_PASSWORD = "${ESXI_PASSWORD}" 96 | \$VCSA_ROOT_PASSWORD = "${VCSA_ROOT_PASSWORD}" 97 | \$VCSA_WEBCLIENT_THEME_NAME = "${VCSA_WEBCLIENT_THEME_NAME}" 98 | __PCLI_VC_CREDENTIAL__ 99 | 100 | echo "Creating vSphere Datacenter and Cluster via Pyvmomi ..." 101 | /usr/bin/python /root/vsan-sdk-python/samplecode/setup_vsan_cluster.py -s "${VCSA_IP}" -u "${VCSA_USERNAME}" -p "${VCSA_SSO_PASSWORD}" -d "${VCSA_DATACENTER_NAME}" -c "${VCSA_CLUSTER_NAME}" >> ${LOGFILE} 102 | 103 | echo "Starting Docker Daemon and running PowerCLI Configuration Script ..." 104 | systemctl start docker 105 | docker run --rm -v /root:/root vmware/powerclicore:ubuntu14.04 powershell /root/deploy.ps1 106 | 107 | if [ "${VCSA_WEBCLIENT_THEME_NAME}" != "" ]; then 108 | echo "Configuring vSphere Web Client UI theme to ${VCSA_WEBCLIENT_THEME_NAME} ..." 109 | docker run --rm -v /root:/root vmware/powerclicore:ubuntu14.04 powershell /root/change_webclient_theme.ps1 110 | fi 111 | 112 | echo "Changing default vSAN VM Storage Policy for single ESXi host ..." 113 | /usr/bin/python /root/vsan-sdk-python/samplecode/change_default_vm_storage_policy.py -s "${VCSA_IP}" -u "${VCSA_USERNAME}" -p "${VCSA_SSO_PASSWORD}" >> ${LOGFILE} 114 | 115 | touch /root/ran_vcsa_deployed 116 | 117 | END_TIME=$(date) 118 | 119 | echo "Start Time: ${START_TIME}" >> ${LOGFILE} 120 | echo "End Time: ${END_TIME}" >> ${LOGFILE} 121 | -------------------------------------------------------------------------------- /DeployVM-Scripts/setup_vsan_cluster.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Setup vSAN Cluster as Storage cmdlets haven't been ported to PowerCLI Core 6 | * Create vSphere Datacenter 7 | * Create vSphere Cluster: Enable DRS/vSAN 8 | * Enable Dedupe/Compression on vSAN Cluster 9 | """ 10 | 11 | __author__ = 'William Lam' 12 | from pyVmomi import vim 13 | from pyVim.connect import SmartConnect, Disconnect 14 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 15 | 16 | import atexit 17 | import argparse 18 | import getpass 19 | import requests 20 | import sys 21 | import ssl 22 | #import the VSAN API python bindings 23 | import vsanmgmtObjects 24 | import vsanapiutils 25 | 26 | def GetArgs(): 27 | """ 28 | Supports the command-line arguments listed below. 29 | """ 30 | parser = argparse.ArgumentParser( 31 | description='Process args for VSAN SDK sample application') 32 | parser.add_argument('-s', '--host', required=True, action='store', 33 | help='Remote host to connect to') 34 | parser.add_argument('-o', '--port', type=int, default=443, action='store', 35 | help='Port to connect on') 36 | parser.add_argument('-u', '--user', required=True, action='store', 37 | help='User name to use when connecting to host') 38 | parser.add_argument('-p', '--password', required=False, action='store', 39 | help='Password to use when connecting to host') 40 | parser.add_argument('-d', '--datacenter', required=True, dest='datacenterName', 41 | default='Datacenter') 42 | parser.add_argument('-c', '--cluster', required=True, dest='clusterName', 43 | default='VSAN-Cluster') 44 | args = parser.parse_args() 45 | return args 46 | 47 | def get_obj(content, vimtype, name, folder=None): 48 | obj = None 49 | if not folder: 50 | folder = content.rootFolder 51 | container = content.viewManager.CreateContainerView(folder, vimtype, True) 52 | for item in container.view: 53 | if item.name == name: 54 | obj = item 55 | break 56 | return obj 57 | 58 | #Start program 59 | def main(): 60 | args = GetArgs() 61 | if args.password: 62 | password = args.password 63 | else: 64 | password = getpass.getpass(prompt='Enter password for host %s and ' 65 | 'user %s: ' % (args.host,args.user)) 66 | 67 | #For python 2.7.9 and later, the defaul SSL conext has more strict 68 | #connection handshaking rule. We may need turn of the hostname checking 69 | #and client side cert verification 70 | context = None 71 | if sys.version_info[:3] > (2,7,8): 72 | context = ssl.create_default_context() 73 | context.check_hostname = False 74 | context.verify_mode = ssl.CERT_NONE 75 | 76 | # Disabling the annoying InsecureRequestWarning message 77 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 78 | 79 | si = SmartConnect(host=args.host, 80 | user=args.user, 81 | pwd=password, 82 | port=int(args.port), 83 | sslContext=context) 84 | 85 | atexit.register(Disconnect, si) 86 | 87 | #for detecting whether the host is VC or ESXi 88 | aboutInfo = si.content.about 89 | 90 | if aboutInfo.apiType == 'VirtualCenter': 91 | majorApiVersion = aboutInfo.apiVersion.split('.')[0] 92 | if int(majorApiVersion) < 6: 93 | print('The Virtual Center with version %s (lower than 6.0) is not supported.' 94 | % aboutInfo.apiVersion) 95 | return -1 96 | 97 | # Create vSphere Datacenter 98 | folder = si.content.rootFolder 99 | 100 | dc_moref = get_obj(si.content, [vim.Datacenter], args.datacenterName) 101 | if not dc_moref: 102 | print("Creating vSphere Datacenter: %s" % args.datacenterName) 103 | dc_moref = folder.CreateDatacenter(name=args.datacenterName) 104 | 105 | # Create vSphere Cluster 106 | host_folder = dc_moref.hostFolder 107 | cluster_spec = vim.cluster.ConfigSpecEx() 108 | drs_config = vim.cluster.DrsConfigInfo() 109 | drs_config.enabled = True 110 | cluster_spec.drsConfig = drs_config 111 | vsan_config = vim.vsan.cluster.ConfigInfo() 112 | vsan_config.enabled = True 113 | cluster_spec.vsanConfig = vsan_config 114 | print("Creating vSphere Cluster: %s" % args.clusterName) 115 | cluster = host_folder.CreateClusterEx(name=args.clusterName, spec=cluster_spec) 116 | 117 | #Here is an example of how to access VC side VSAN Health Service API 118 | vcMos = vsanapiutils.GetVsanVcMos(si._stub, context=context) 119 | 120 | # Get VSAN Cluster Config System 121 | vccs = vcMos['vsan-cluster-config-system'] 122 | 123 | #cluster = getClusterInstance(args.clusterName, si) 124 | 125 | if cluster is None: 126 | print("Cluster %s is not found for %s" % (args.clusterName, args.host)) 127 | return -1 128 | 129 | vsanCluster = vccs.VsanClusterGetConfig(cluster=cluster) 130 | 131 | # Check to see if Dedupe & Compression is already enabled, if not, then we'll enable it 132 | if(vsanCluster.dataEfficiencyConfig.compressionEnabled == False or vsanCluster.dataEfficiencyConfig.dedupEnabled == False): 133 | print ("Enabling Compression/Dedupe capability on vSphere Cluster: %s" % args.clusterName) 134 | # Create new VSAN Reconfig Spec, both Compression/Dedupe must be enabled together 135 | vsanSpec = vim.VimVsanReconfigSpec( 136 | dataEfficiencyConfig=vim.VsanDataEfficiencyConfig( 137 | compressionEnabled=True, 138 | dedupEnabled=True 139 | ), 140 | modify=True 141 | ) 142 | vsanTask = vccs.VsanClusterReconfig(cluster=cluster,vsanReconfigSpec=vsanSpec) 143 | vcTask = vsanapiutils.ConvertVsanTaskToVcTask(vsanTask,si._stub) 144 | vsanapiutils.WaitForTasks([vcTask],si) 145 | else: 146 | print ("Compression/Dedupe is already enabled on vSphere Cluster: %s" % args.clusterName) 147 | 148 | # Start program 149 | if __name__ == "__main__": 150 | main() 151 | -------------------------------------------------------------------------------- /DeployVM-Scripts/vcsa.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "__version": "2.3.0", 3 | "__comments": "Sample template to deploy a vCenter Server Appliance with an embedded Platform Services Controller on an ESXi host.", 4 | "new.vcsa": { 5 | "esxi": { 6 | "hostname": "VAR-ESXI-HOSTNAME", 7 | "username": "root", 8 | "password": "VAR-ESXI-PASSWORD", 9 | "deployment.network": "VM Network", 10 | "datastore": "vsanDatastore" 11 | }, 12 | "appliance": { 13 | "thin.disk.mode": true, 14 | "deployment.option": "tiny", 15 | "name": "Embedded-vCenter-Server-Appliance" 16 | }, 17 | "network": { 18 | "ip.family": "ipv4", 19 | "mode": "static", 20 | "ip": "VAR-VCSA-IP", 21 | "dns.servers": [ 22 | "VAR-VCSA-DNS" 23 | ], 24 | "prefix": "VAR-VCSA-PREFIX", 25 | "gateway": "VAR-VCSA-GATEWAY", 26 | "system.name": "VAR-VCSA-HOSTNAME" 27 | }, 28 | "os": { 29 | "password": "VAR-VCSA-ROOT-PASSWORD", 30 | "ssh.enable": VAR-VCSA-SSH-ENABLED, 31 | "time.tools-sync": true 32 | }, 33 | "sso": { 34 | "password": "VAR-VCSA-SSO-PASSWORD", 35 | "domain-name": "VAR-VCSA-SSO-DOMAIN-NAME", 36 | "site-name": "VAR-VCSA-SSO-SITE-NAME" 37 | }, 38 | "ovftool.arguments" : { 39 | "prop:guestinfo.cis.appliance.root.shell" : "/bin/bash" 40 | } 41 | }, 42 | "ceip": { 43 | "settings": { 44 | "ceip.enabled": VAR-VCSA-CEIP-ENABLED 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /KS.CFG: -------------------------------------------------------------------------------- 1 | vmaccepteula 2 | install --firstdisk=usb --overwritevmfs --novmfsondisk 3 | reboot 4 | 5 | %include /tmp/rootpass 6 | %include /tmp/networkconfig 7 | 8 | %pre --interpreter=busybox 9 | 10 | # ---> START EDIT HERE <--- # 11 | VSAN_DISK_TYPE="AF" 12 | PHOTON_IP="192.168.1.10" 13 | PHOTON_CIDR="24" 14 | PHOTON_GATEWAY="192.168.1.1" 15 | PHOTON_DNS="192.168.1.1" 16 | ESXI_IP="192.168.1.100" 17 | ESXI_PASSWORD="VMware1!" 18 | ESXI_NETMASK="255.255.255.0" 19 | ESXI_GATEWAY="192.168.1.1" 20 | ESXI_HOSTNAME="nuc.primp-industries.com" 21 | ESXI_DNS="192.168.1.1" 22 | VCSA_IP="192.168.1.200" 23 | VCSA_HOSTNAME="192.168.1.200" 24 | VCSA_PREFIX="24" 25 | VCSA_GATEWAY="192.168.1.1" 26 | VCSA_DNS="192.168.1.1" 27 | VCSA_SSO_DOMAIN_NAME="vsphere.local" 28 | VCSA_SSO_SITE_NAME="virtuallyGhetto" 29 | VCSA_ROOT_PASSWORD="VMware1!" 30 | VCSA_SSO_PASSWORD="VMware1!" 31 | VCSA_SSH_ENABLED="true" 32 | VCSA_CEIP_ENABLED="true" 33 | VCSA_DATACENTER_NAME="VSAN-Datacenter" 34 | VCSA_CLUSTER_NAME="VSAN-Cluster" 35 | VCSA_WEBCLIENT_THEME_NAME="CormacHogan" 36 | # ---> STOP EDIT HERE <--- # 37 | 38 | echo "network --bootproto=static --ip=${ESXI_IP} --netmask=${ESXI_NETMASK} --gateway=${ESXI_GATEWAY} --hostname=${ESXI_HOSTNAME} --nameserver=${ESXI_DNS} --addvmportgroup=1" > /tmp/networkconfig 39 | echo "rootpw ${ESXI_PASSWORD}" > /tmp/rootpass 40 | echo "${VSAN_DISK_TYPE}" > /tmp/vsandisktype 41 | 42 | cat > /tmp/deployvmsettings << __DEPLOYVM__ 43 | PHOTON_IP=${PHOTON_IP} 44 | PHOTON_CIDR=${PHOTON_CIDR} 45 | PHOTON_GATEWAY=${PHOTON_GATEWAY} 46 | PHOTON_DNS=${PHOTON_DNS} 47 | ESXI_IP=${ESXI_IP} 48 | ESXI_PASSWORD=${ESXI_PASSWORD} 49 | VCSA_IP=${VCSA_IP} 50 | VCSA_HOSTNAME=${VCSA_HOSTNAME} 51 | VCSA_PREFIX=${VCSA_PREFIX} 52 | VCSA_GATEWAY=${VCSA_GATEWAY} 53 | VCSA_DNS=${VCSA_DNS} 54 | VCSA_SSO_DOMAIN_NAME=${VCSA_SSO_DOMAIN_NAME} 55 | VCSA_SSO_SITE_NAME=${VCSA_SSO_SITE_NAME} 56 | VCSA_ROOT_PASSWORD=${VCSA_ROOT_PASSWORD} 57 | VCSA_SSO_PASSWORD=${VCSA_SSO_PASSWORD} 58 | VCSA_SSH_ENABLED=${VCSA_SSH_ENABLED} 59 | VCSA_CEIP_ENABLED=${VCSA_CEIP_ENABLED} 60 | VCSA_DATACENTER_NAME=${VCSA_DATACENTER_NAME} 61 | VCSA_CLUSTER_NAME=${VCSA_CLUSTER_NAME} 62 | VCSA_WEBCLIENT_THEME_NAME=${VCSA_WEBCLIENT_THEME_NAME} 63 | __DEPLOYVM__ 64 | 65 | %pre --interpreter=python 66 | 67 | import subprocess, os, uuid, syslog 68 | 69 | vsan_syslog_key = "VSAN-KS" 70 | debug = False 71 | 72 | with open ("/tmp/vsandisktype", "r") as myfile: 73 | vsan_disk_type=myfile.read().replace('\n', '') 74 | 75 | def execute(cmd,shell): 76 | proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE) 77 | return proc.communicate()[0].decode() 78 | 79 | # Enable vSAN traffic 80 | def enableVsanTraffic(): 81 | vsan_traffic_cmd = "localcli vsan network ip add -i vmk0" 82 | syslog.syslog(vsan_syslog_key + " Enabling vSAN traffic on vmk0: " + vsan_traffic_cmd) 83 | if debug == False: 84 | os.system(vsan_traffic_cmd) 85 | 86 | # Enable Dedupe/Compression 87 | def enableDedupeCompression(): 88 | space_eff_cmd = "localcli system settings advanced set -o /VSAN/StorageEfficiency -i 1" 89 | syslog.syslog(vsan_syslog_key + " Enabling vSAN Dedupe/Compression command: " + space_eff_cmd) 90 | if debug == False: 91 | os.system(space_eff_cmd) 92 | 93 | # Build VSAN Disk Group command based on vdq -q output 94 | def createVsanAFDiskGroup(): 95 | output = execute(['/sbin/vdq','-q'],False) 96 | 97 | capacityDisk = None 98 | cachingDisk = None 99 | largestDisk = None 100 | largestDiskSize = None 101 | usbDevice = None 102 | 103 | results = eval(output) 104 | for disk in results: 105 | diskId = disk['Name'] 106 | 107 | if disk['State'] == 'Eligible for use by VSAN': 108 | syslog.syslog(vsan_syslog_key + " Found Disk: " + diskId) 109 | 110 | diskcapacity_cmd = "localcli storage core device capacity list -d " + diskId + " | tail -1 | awk '{print $5}'" 111 | syslog.syslog(vsan_syslog_key + " Running disk capacity command: " + diskcapacity_cmd) 112 | ps = subprocess.Popen(diskcapacity_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 113 | strdiskSize = ps.communicate()[0].decode().strip() 114 | diskSize = int(strdiskSize) 115 | 116 | if largestDisk == None: 117 | largestDisk = diskId 118 | largestDiskSize = diskSize 119 | else: 120 | if diskSize > largestDiskSize: 121 | capacityDisk = diskId 122 | cachingDisk = largestDisk 123 | else: 124 | capacityDisk = largestDisk 125 | cachingDisk = diskId 126 | 127 | syslog.syslog(vsan_syslog_key + " Largest Capacity Disk so far: " + largestDisk + " (" + str(largestDiskSize) + ")") 128 | 129 | if disk['State'] == 'Ineligible for use by VSAN' and disk['VSANUUID'] == "": 130 | usbDevice = diskId 131 | fo = open("/tmp/usbDevice.txt", "w") 132 | fo.write(usbDevice) 133 | fo.close() 134 | 135 | if cachingDisk != None and capacityDisk != None: 136 | # Tag Capacity Disk 137 | disktag_cmd = "localcli vsan storage tag add -d " + capacityDisk + " -t capacityFlash" 138 | syslog.syslog(vsan_syslog_key + " Running disk capacity tagging command: " + disktag_cmd) 139 | if debug == False: 140 | os.system(disktag_cmd) 141 | 142 | # Create VSAN Disk Group 143 | diskgroup_cmd = "localcli vsan storage add -s " + cachingDisk + " -d " + capacityDisk 144 | syslog.syslog(vsan_syslog_key + " Running disk group create command: " + diskgroup_cmd) 145 | if debug == False: 146 | os.system(diskgroup_cmd) 147 | 148 | def createVsanHybridDiskGroup(): 149 | output = execute(['/sbin/vdq','-q'],False) 150 | 151 | capacityDisk = None 152 | cachingDisk = None 153 | largestDisk = None 154 | largestDiskSize = None 155 | usbDevice = None 156 | 157 | results = eval(output) 158 | for disk in results: 159 | diskId = disk['Name'] 160 | 161 | if disk['State'] == 'Eligible for use by VSAN': 162 | syslog.syslog(vsan_syslog_key + " Found Disk: " + diskId) 163 | 164 | diskcapacity_cmd = "localcli storage core device capacity list -d " + diskId + " | tail -1 | awk '{print $5}'" 165 | syslog.syslog(vsan_syslog_key + " Running disk capacity command: " + diskcapacity_cmd) 166 | ps = subprocess.Popen(diskcapacity_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 167 | 168 | if disk['IsSSD'] == '1': 169 | syslog.syslog(vsan_syslog_key + " Found SSD " + diskId) 170 | cachingDisk = diskId 171 | 172 | if disk['IsSSD'] == '0': 173 | syslog.syslog(vsan_syslog_key + " Found HDD " + diskId) 174 | capacityDisk = diskId 175 | 176 | if disk['State'] == 'Ineligible for use by VSAN' and disk['VSANUUID'] == "": 177 | usbDevice = diskId 178 | fo = open("/tmp/usbDevice.txt", "w") 179 | fo.write(usbDevice) 180 | fo.close() 181 | 182 | if cachingDisk != None and capacityDisk != None: 183 | # Create VSAN Disk Group 184 | diskgroup_cmd = "localcli vsan storage add -s " + cachingDisk + " -d " + capacityDisk 185 | syslog.syslog(vsan_syslog_key + " Running disk group create command: " + diskgroup_cmd) 186 | if debug == False: 187 | os.system(diskgroup_cmd) 188 | 189 | def createVsanCluster(): 190 | cluster_cmd = "localcli vsan cluster new" 191 | syslog.syslog(vsan_syslog_key + " Running VSAN cluster create command: " + cluster_cmd) 192 | if debug == False: 193 | os.system(cluster_cmd) 194 | 195 | enableVsanTraffic() 196 | if vsan_disk_type == "HYBRID": 197 | createVsanHybridDiskGroup() 198 | else: 199 | createVsanAFDiskGroup() 200 | enableDedupeCompression() 201 | createVsanCluster() 202 | 203 | %pre --interpreter=busybox 204 | 205 | /bin/vmkload_mod vmfs3 206 | 207 | # Set FTT=0 + Force Provisioning for single node 208 | localcli vsan policy setdefault -c vdisk -p "((\"hostFailuresToTolerate\" i1) (\"forceProvisioning\" i1))" 209 | localcli vsan policy setdefault -c vmnamespace -p "((\"hostFailuresToTolerate\" i1) (\"forceProvisioning\" i1))" 210 | 211 | # Copy Deploy VM to vSAN Datastore 212 | USB_KEY=$(cat /tmp/usbDevice.txt) 213 | DEPLOY_VM_ZIP=DeployVM.zip 214 | VCSA_ISO=VCSA.iso 215 | 216 | # required in case where osfs-mkdir may fail 217 | sleep 120 218 | 219 | /usr/lib/vmware/osfs/bin/osfs-mkdir /vmfs/volumes/vsanDatastore/DeployVM 220 | /bin/mcopy -i "/dev/disks/${USB_KEY}:2" ::/${DEPLOY_VM_ZIP} /vmfs/volumes/vsanDatastore/DeployVM/${DEPLOY_VM_ZIP} 221 | /bin/mcopy -i "/dev/disks/${USB_KEY}:2" ::/VCSA-part-aa /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-aa 222 | /bin/mcopy -i "/dev/disks/${USB_KEY}:2" ::/VCSA-part-ab /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-ab 223 | /bin/mcopy -i "/dev/disks/${USB_KEY}:2" ::/VCSA-part-ac /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-ac 224 | /bin/mcopy -i "/dev/disks/${USB_KEY}:2" ::/VCSA-part-ad /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-ad 225 | cat /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-a* > /vmfs/volumes/vsanDatastore/DeployVM/${VCSA_ISO} 226 | unzip /vmfs/volumes/vsanDatastore/DeployVM/${DEPLOY_VM_ZIP} -d /vmfs/volumes/vsanDatastore/DeployVM 227 | rm -f /vmfs/volumes/vsanDatastore/DeployVM/VCSA-part-a* 228 | rm -f /vmfs/volumes/vsanDatastore/DeployVM/${DEPLOY_VM_ZIP} 229 | 230 | # Custom values passed to DeployVM 231 | . /tmp/deployvmsettings 232 | 233 | # Add guestinfo properties to Deploy VM for VCSA deployment 234 | DEPLOYVM_VMX_PATH=/vmfs/volumes/vsanDatastore/DeployVM/DeployVM.vmx 235 | 236 | echo "guestinfo.photon_ip = \"${PHOTON_IP}\"" >> ${DEPLOYVM_VMX_PATH} 237 | echo "guestinfo.photon_cidr = \"${PHOTON_CIDR}\"" >> ${DEPLOYVM_VMX_PATH} 238 | echo "guestinfo.photon_gateway = \"${PHOTON_GATEWAY}\"" >> ${DEPLOYVM_VMX_PATH} 239 | echo "guestinfo.photon_dns = \"${PHOTON_DNS}\"" >> ${DEPLOYVM_VMX_PATH} 240 | echo "guestinfo.esxi_hostname = \"${ESXI_IP}\"" >> ${DEPLOYVM_VMX_PATH} 241 | echo "guestinfo.esxi_password = \"${ESXI_PASSWORD}\"" >> ${DEPLOYVM_VMX_PATH} 242 | echo "guestinfo.vcsa_ip = \"${VCSA_IP}\"" >> ${DEPLOYVM_VMX_PATH} 243 | echo "guestinfo.vcsa_dns = \"${VCSA_DNS}\"" >> ${DEPLOYVM_VMX_PATH} 244 | echo "guestinfo.vcsa_prefix = \"${VCSA_PREFIX}\"" >> ${DEPLOYVM_VMX_PATH} 245 | echo "guestinfo.vcsa_gateway = \"${VCSA_GATEWAY}\"" >> ${DEPLOYVM_VMX_PATH} 246 | echo "guestinfo.vcsa_hostname = \" ${VCSA_HOSTNAME}\"" >> ${DEPLOYVM_VMX_PATH} 247 | echo "guestinfo.vcsa_sso_domain_name = \"${VCSA_SSO_DOMAIN_NAME}\"" >> ${DEPLOYVM_VMX_PATH} 248 | echo "guestinfo.vcsa_sso_site_name = \"${VCSA_SSO_SITE_NAME}\"" >> ${DEPLOYVM_VMX_PATH} 249 | echo "guestinfo.vcsa_root_password = \"${VCSA_ROOT_PASSWORD}\"" >> ${DEPLOYVM_VMX_PATH} 250 | echo "guestinfo.vcsa_sso_password = \"${VCSA_SSO_PASSWORD}\"" >> ${DEPLOYVM_VMX_PATH} 251 | echo "guestinfo.vcsa_ssh_enabled = \"${VCSA_SSH_ENABLED}\"" >> ${DEPLOYVM_VMX_PATH} 252 | echo "guestinfo.vcsa_ceip_enabled = \"${VCSA_CEIP_ENABLED}\"" >> ${DEPLOYVM_VMX_PATH} 253 | echo "guestinfo.vcsa_datacenter_name = \"${VCSA_DATACENTER_NAME}\"" >> ${DEPLOYVM_VMX_PATH} 254 | echo "guestinfo.vcsa_cluster_name = \"${VCSA_CLUSTER_NAME}\"" >> ${DEPLOYVM_VMX_PATH} 255 | echo "guestinfo.vcsa_webclient_theme_name = \"${VCSA_WEBCLIENT_THEME_NAME}\"" >> ${DEPLOYVM_VMX_PATH} 256 | 257 | # Update the Deploy VM VMX file w/VSAN path 258 | VSAN_VOLUME=$(ls /vmfs/volumes/ | grep "vsan:") 259 | sed -i "s#BLAH#${VSAN_VOLUME}#g" ${DEPLOYVM_VMX_PATH} 260 | 261 | %post --interpreter=busybox 262 | 263 | # Disable AHCI Native Driver (known to have issues for NUCs) 264 | localcli system module set --enabled=false --module=vmw_ahci 265 | 266 | # Disable device monitoring for Home Labs 267 | localcli system settings advanced set -o /LSOM/VSANDeviceMonitoring -i 0 268 | 269 | # Enable Thin swap 270 | localcli system settings advanced set -o /VSAN/SwapThickProvisionDisabled -i 1 271 | 272 | # Required if you plan to run Nested ESXi on top of vSAN Datatsore 273 | localcli system settings advanced set -o /VSAN/FakeSCSIReservations -i 1 274 | 275 | %firstboot --interpreter=busybox 276 | 277 | DEPLOYVM_VMX_PATH=/vmfs/volumes/vsanDatastore/DeployVM/DeployVM.vmx 278 | 279 | # Ensure hostd is ready 280 | while ! vim-cmd hostsvc/runtimeinfo; do 281 | sleep 10 282 | done 283 | 284 | # Set FTT=0 + Force Provisioning for single node 285 | esxcli vsan policy setdefault -c vdisk -p "((\"hostFailuresToTolerate\" i1) (\"forceProvisioning\" i1))" 286 | esxcli vsan policy setdefault -c vmnamespace -p "((\"hostFailuresToTolerate\" i1) (\"forceProvisioning\" i1))" 287 | 288 | # enable & start SSH 289 | vim-cmd hostsvc/enable_ssh 290 | vim-cmd hostsvc/start_ssh 291 | 292 | # enable & start ESXi Shell 293 | vim-cmd hostsvc/enable_esx_shell 294 | vim-cmd hostsvc/start_esx_shell 295 | 296 | # Suppress ESXi Shell warning 297 | esxcli system settings advanced set -o /UserVars/SuppressShellWarning -i 1 298 | 299 | # Register VM 300 | vim-cmd solo/registervm ${DEPLOYVM_VMX_PATH} 301 | 302 | # power on VM 303 | vim-cmd vmsvc/power.on 1 304 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USB to SDDC 2 | 3 | ![](usb-to-sddc-0.png) 4 | 5 | ## Table of Contents 6 | 7 | * [Description](#description) 8 | * [Additional Resources](#additional-resources) 9 | * [Environment Requirements](#environment-requirements) 10 | * [Software Requirements](#software-requirements) 11 | * [Usage](#usage) 12 | * [Expected Timings](#expected-timings) 13 | * [Troubleshooting](#troubleshooting) 14 | 15 | ## Description 16 | 17 | Would it not be cool if you could simply plug in a USB key which had a specific configuration defined and have ESXi, vCenter Server Appliance and vSAN automatically install and configure itself without any additional user interaction? 18 | 19 | This is what Project USB to SDDC is all about! 20 | 21 | ## Additional Resources 22 | 23 | For more details about this solution, please take a look at the following blog posts: 24 | 25 | * [Project USB to SDDC - Part 1](http://www.virtuallyghetto.com/2017/04/project-usb-to-sddc-part-1.html) 26 | * [Project USB to SDDC - Part 2](http://www.virtuallyghetto.com/2017/04/project-usb-to-sddc-part-2.html) 27 | * [Project USB to SDDC - Part 3](http://www.virtuallyghetto.com/2017/05/project-usb-to-sddc-part-3.html) 28 | 29 | 30 | ## Environment Requirements 31 | 32 | * USB key that is at least 6GB in capacity 33 | * Access to either macOS or Linux system as the script that creates the USB key is only supported on these two platforms 34 | * No additional USB keys must be plugged into the hardware system other than the primary installer USB key 35 | * Hardware system must have at least 2 disk drives which can either be **1xHDD** and **1xSSD** for running Hybrid vSAN OR **2xSSD** for running All-Flash vSAN 36 | * Both Intel NUC 6th Gen and Supermicro E200-8D and E300-8D have been tested with this solution. It should work with other hardware systems that meet the minimum requirements but YMMV 37 | 38 | ## Software Requirements 39 | * [ESXi 6.5a - VMware-VMvisor-Installer-201701001-4887370.x86_64.iso](https://my.vmware.com/web/vmware/details?downloadGroup=ESXI650A&productId=614&rPId=15340) 40 | * [VCSA 6.5b - VMware-VCSA-all-6.5.0-5178943.iso](https://my.vmware.com/web/vmware/details?productId=614&rPId=15340&downloadGroup=VC650B) 41 | * [DeployVM.zip](https://download3.vmware.com/software/vmw-tools/wlam/DeployVM.zip) 42 | * [UNetbootin](https://unetbootin.github.io/linux_download.html) (Required for Mac OS X users) 43 | 44 | **Note:** Other ESXi / VCSA 6.5.x versions can also be substituted 45 | 46 | ## Usage 47 | 48 | **Step 1** - Clone the Github repository to your local system by running the following command: 49 | 50 | ``` 51 | git clone https://github.com/lamw/usb-to-sddc.git 52 | ``` 53 | 54 | If you do not have git installed on your computer or if you prefer to just download the scripts manually, you can do so by downloading the following file below: 55 | 56 | [https://github.com/lamw/usb-to-sddc/archive/master.zip](https://github.com/lamw/usb-to-sddc/archive/master.zip) 57 | 58 | **Step 2** - Change into the usb-to-sddc directory or extract the contents if you downloaded the zip file instead. 59 | 60 | **Step 3** - Download all the files listed in the **Software Requirements** above to your local desktop. 61 | 62 | **Step 4** - Open the `KS.CFG` using your favorite text editor such as vi, Visual Studio Code, etc. Search for the following tag in the file **# ---> START EDIT HERE <--- #** which should be located on [Line 10](KS.CFG#L10). 63 | 64 | There are 25 variables as shown below which can be adjusted to customize your deployment: 65 | 66 | ```bash 67 | VSAN_DISK_TYPE="AF" 68 | PHOTON_IP="192.168.1.10" 69 | PHOTON_CIDR="24" 70 | PHOTON_GATEWAY="192.168.1.1" 71 | PHOTON_DNS="192.168.1.1" 72 | ESXI_IP="192.168.1.100" 73 | ESXI_PASSWORD="VMware1!" 74 | ESXI_NETMASK="255.255.255.0" 75 | ESXI_GATEWAY="192.168.1.1" 76 | ESXI_HOSTNAME="nuc.primp-industries.com" 77 | ESXI_DNS="192.168.1.1" 78 | VCSA_IP="192.168.1.200" 79 | VCSA_HOSTNAME="192.168.1.200" 80 | VCSA_PREFIX="24" 81 | VCSA_GATEWAY="192.168.1.1" 82 | VCSA_DNS="192.168.1.1" 83 | VCSA_SSO_DOMAIN_NAME="vsphere.local" 84 | VCSA_SSO_SITE_NAME="virtuallyGhetto" 85 | VCSA_ROOT_PASSWORD="VMware1!" 86 | VCSA_SSO_PASSWORD="VMware1!" 87 | VCSA_SSH_ENABLED="true" 88 | VCSA_CEIP_ENABLED="true" 89 | VCSA_DATACENTER_NAME="VSAN-Datacenter" 90 | VCSA_CLUSTER_NAME="VSAN-Cluster" 91 | VCSA_WEBCLIENT_THEME_NAME="CormacHogan" 92 | ``` 93 | The variables should be pretty self-explantory, but here are few that need some additional explanation: 94 | 95 | * **VSAN_DISK_TYPE** - Defines whether you have a Hybrid or All-Flash vSAN setup based on your physical disks. The valid values are `HYBRID` or `AF`. 96 | 97 | * **PHOTON_IP** - This is the IP Address of the DeployVM. If you are deploying in an isolated network (e.g. using cross-over cable between your laptop and server), make sure the network between DeployVM and ESXi host is on the same network. 98 | 99 | * **PHOTON_CIDR** - This is the CIDR network for DeployVM (e.g. 24 = /24 = 255.255.255.0). 100 | 101 | * **PHOTON_GATEWAY** and **PHOTON_DNS** - This is the Gateway and DNS Server for DeployVM. 102 | 103 | * **VCSA_IP** and **VCSA_HOSTNAME** - If you do not have valid DNS in your enviornment which both forward and reverse is functional, then make sure both these variables have the exact same IP Addresss or your VCSA deployment will fail as it will try to resolve the hostname (FQDN) with the DNS server you provided. 104 | 105 | * **VCSA_WEBCLIENT_THEME_NAME** - Defines the theme that will automatically be applied if you wish to customize the vSphere Web Client as described [here](https://github.com/lamw/customize-vsphere-web-client-6.). You can find the complete list of theme names [here](https://github.com/lamw/customize-vsphere-web-client-6.5/tree/master/theme). 106 | 107 | **Step 5** - Next, edit either **create_sddc_deployment_on_usb_for_osx.sh** or **create_sddc_deployment_on_usb_for_linux.sh** depending on the platform that you will be using to create the USB installer. You will need to edit the following variables at the top of the script which will point it to the download files you had performed in Step 3. 108 | 109 | * **UNETBOOTIN_APP_PATH** - Complete path to the UNetbootin application directory, only applicable for Mac OS X users 110 | * **ESXI_ISO_PATH** - Complete path to the ESXi ISO 111 | * **VCSA_ISO_PATH** - Complete path to the VCSA ISO 112 | * **ESXI_KICKSTART_PATH** - Complete path to the KS.CFG 113 | * **DEPLOYVM_ZIP_PATH** - Complete path to the DeployVM zip 114 | 115 | **Step 6** - The next step is to now use **the create_sddc_deployment_on_usb_for_X.sh** to create our USB insaller. Plug a USB key into your system. Please be aware, all contents on the USB key will be wiped *after* you confirm the USB key that is to be used to create the automated installer. 116 | 117 | First you need to identify the USB device that was plugged in to your system to make sure you select the right one. 118 | * On macOS - You can run ```diskutil list``` and identify the device which should look like `/dev/diskX`. 119 | * On Linux - You can run ```parted -l``` and identify the device which should look like `/dev/sdX`. 120 | 121 | Next, to run the script you will need to use **sudo** and pass in the device that you had retrieved from the previous commands. 122 | 123 | Here is an example of running on macOS system: 124 | 125 | ``` 126 | sudo ./create_sddc_deployment_on_usb_for_osx.sh /dev/disk4 127 | ``` 128 | 129 | The script will automatically clear existing partitions and create the expected partition scheme. It will copy all the software packages you had downloaded from Step 3 and once it has completed, it will also unmount the USB device. 130 | 131 | **Step 7** - The final step is to now take the USB key and plug it into your system and simply power it on. If you want to verify that things are working, you can connect an external monitor and watch the installation but I will warn you, it is pretty boring :) If things are going well, you should see the ESXi installer stay on the *"reading installation file"* for quite a while as this is where the majority of the time is spent during the `%pre` section where it forms the vSAN datastore and copies all the files from PAYLOAD partition over to vSAN. 132 | 133 | Once ESXi has been successfully installed, which you can verify by observing it is on the main boot screen with an IP Address. You can open a browser to ESXi Embedded Host Client (e.g https://[IP]/ui) and login. Depending on when this is done, you may only see the DeployVM and/or VCSA being deployed. If you want to follow the remainder progress of the deployment, you can login to the DeployVM using the IP Address you had assigned it and the credentials is `root/VMware1!` by default. 134 | 135 | Once logged into the DeployVM, you can tail `/root/script.log` which will give you the progress of the VCSA deployment and configuration. 136 | 137 | ## Expected Timings 138 | 139 | Here is what you can expect from a timing standpoint from creating the USB Installer to plugging it into your system and provisioning the SDDC. From the testing I have observed in my personal lab, the USB Installer took 11min and the USB to SDDC deployment took 45min, which is from the time I plugged it into the NUC and powered it on to point where I can login to the vSphere Web Client of the vCenter Server. Obviously, YMMV depending on your hardware configuration. 140 | 141 | | Process | Estimated Time | 142 | |--------------------------|----------------| 143 | | Create USB Installer key | 10-15min | 144 | | USB to SDDC deployment | 45-60min | 145 | 146 | ## Troubleshooting 147 | 148 | * If the ESXi installation failed or halted - You will need console access to your system and see what error is seen on the screen. You may also need to go into the ESXi Shell to see if vSAN datastore was properly configured. You can login using root and no password. 149 | 150 | * If the VCSA deployment failed or halted - You can login to the DeployVM via SSH using the IP Address that you had assigned it and the credentials is `root/VMware1!` by default. Have a look at /root/script.log to see where it last left off or any error messages that have been logged. 151 | -------------------------------------------------------------------------------- /create_sddc_deployment_on_usb_for_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # William Lam 3 | # www.virtuallyghetto.com 4 | 5 | ESXI_ISO_PATH=/root/VMware-VMvisor-Installer-201701001-4887370.x86_64.iso 6 | VCSA_ISO_PATH=/root/VMware-VCSA-all-6.5.0-4827210.iso 7 | ESXI_KICKSTART_PATH=/root/usb-to-sddc/KS.CFG 8 | DEPLOYVM_ZIP_PATH=/root/DeployVM.zip 9 | LOG_OUTPUT=/root/script.log 10 | 11 | if [ ! $(uname -s) == "Linux" ]; then 12 | echo "This script is only meant to run on Linux system" 13 | exit 1 14 | fi 15 | 16 | if [ "$(id -u)" != "0" ]; then 17 | echo "This script must be run as root" 18 | exit 1 19 | fi 20 | 21 | if [ $# -ne 1 ]; then 22 | echo "Usage: $0 [USB_DEVICE]" 23 | echo -e "\n\t$0 /dev/sdb" 24 | exit 25 | fi 26 | 27 | USB_DEVICE=$1 28 | 29 | echo "Please confirm that ${USB_DEVICE} is the USB Device you would like to use (Y/N)" 30 | read RESPONSE 31 | case "$RESPONSE" in [yY]) 32 | ;; 33 | *) echo "Quiting Installer!" 34 | exit 1 35 | ;; 36 | esac 37 | 38 | if [ ! -e ${ESXI_ISO_PATH} ]; then 39 | echo "Error: ESXi ISO was not found" 40 | exit 1 41 | fi 42 | 43 | if [ ! -e ${VCSA_ISO_PATH} ]; then 44 | echo "Error: VCSA ISO was not found" 45 | exit 1 46 | fi 47 | 48 | if [ ! -e ${DEPLOYVM_ZIP_PATH} ]; then 49 | echo "Error: DeployVM zip file was not found" 50 | exit 1 51 | fi 52 | 53 | if [ ! -e ${ESXI_KICKSTART_PATH} ]; then 54 | echo "Error: ESXi Kickstart file was not found" 55 | exit 1 56 | fi 57 | 58 | echo "Setting up syslinux-3.86 ..." 59 | apt-get -y install make gcc nasm unzip 60 | wget https://www.kernel.org/pub/linux/utils/boot/syslinux/3.xx/syslinux-3.86.zip 61 | mkdir /tmp/syslinux-3.86 62 | mv syslinux-3.86.zip /tmp/syslinux-3.86 63 | cd /tmp/syslinux-3.86 64 | unzip syslinux-3.86.zip 65 | make &> ${LOG_OUTPUT} 66 | cd /root 67 | 68 | echo "Clearning all existing partitions on USB Device ..." 69 | dd if=/dev/zero of=${USB_DEVICE} bs=512 count=1 70 | 71 | echo "Creating BOOT and PAYLOAD partition on USB Device ..." 72 | echo "n 73 | p 74 | 1 75 | 76 | +2G 77 | t 78 | 6 79 | n 80 | p 81 | 2 82 | 83 | 84 | t 85 | 2 86 | b 87 | w 88 | " | fdisk ${USB_DEVICE} >> ${LOG_OUTPUT} 89 | 90 | mkdosfs -F 16 ${USB_DEVICE}1 -n BOOT >> ${LOG_OUTPUT} 91 | mkdosfs -F 32 ${USB_DEVICE}2 -n PAYLOAD >> ${LOG_OUTPUT} 92 | /tmp/syslinux-3.86/linux/syslinux ${USB_DEVICE}1 93 | cat /tmp/syslinux-3.86/mbr/mbr.bin > ${USB_DEVICE} 94 | 95 | mkdir -p /tmp/{BOOT,PAYLOAD} 96 | mount ${USB_DEVICE}1 /tmp/BOOT 97 | mount ${USB_DEVICE}2 /tmp/PAYLOAD 98 | 99 | echo "Copying ESXi Installation to /tmp/BOOT ..." 100 | mkdir -p /tmp/mnt 101 | mount -o loop ${ESXI_ISO_PATH} /tmp/mnt 102 | cp -r /tmp/mnt/* /tmp/BOOT 103 | umount /tmp/mnt 104 | rmdir /tmp/mnt 105 | mv /tmp/BOOT/isolinux.cfg /tmp/BOOT/syslinux.cfg 106 | sed -i s/menu.c32/mboot.c32/g /tmp/BOOT/syslinux.cfg 107 | 108 | echo "Copying KS.cfg to /tmp/BOOT ..." 109 | cp ${ESXI_KICKSTART_PATH} /tmp/BOOT 110 | 111 | echo "Updating boot.cfg to use KS.cfg ..." 112 | sed -i 's#kernelopt.*#kernelopt=ks=usb:/KS.CFG#g' /tmp/BOOT/BOOT.CFG 113 | sed -i 's#kernelopt.*#kernelopt=ks=usb:/KS.CFG#g' /tmp/BOOT/EFI/BOOT/BOOT.CFG 114 | 115 | echo "Copying Deploy VM zip to /tmp/PAYLOAD ..." 116 | cp ${DEPLOYVM_ZIP_PATH} /tmp/PAYLOAD 117 | 118 | echo "Checking to see if VCSA ISO has already been splitted into individual 1GB chunks ..." 119 | VCSA_ISO_DIRECTORY=${VCSA_ISO_PATH%/*} 120 | ls ${VCSA_ISO_DIRECTORY}/VCSA-part-* > /dev/null 2>&1 121 | if [ $? -ne 0 ]; then 122 | echo "Spitting VCSA ISO into individual 1GB chunks ... " 123 | split -b 1073741824 ${VCSA_ISO_PATH} ${VCSA_ISO_DIRECTORY}/VCSA-part- 124 | else 125 | echo "VCSA ISO has already been splitted, skipping step ..." 126 | fi 127 | 128 | echo "Copying VCSA chunks to /tmp/PAYLOAD ..." 129 | cp ${VCSA_ISO_DIRECTORY}/VCSA-part-* /tmp/PAYLOAD 130 | 131 | echo "Unmounting /tmp/{BOOT,PAYLOAD} ..." 132 | umount /tmp/BOOT 133 | umount /tmp/PAYLOAD 134 | rmdir /tmp/BOOT 135 | rmdir /tmp/PAYLOAD 136 | -------------------------------------------------------------------------------- /create_sddc_deployment_on_usb_for_osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # William Lam 3 | # www.virtuallyghetto.com 4 | 5 | UNETBOOTIN_APP_PATH=/Users/lamw/Desktop/unetbootin.app 6 | ESXI_ISO_PATH=/Volumes/Storage/Images/Current/VMware-VMvisor-Installer-201701001-4887370.x86_64.iso 7 | VCSA_ISO_PATH=/Volumes/Storage/Images/Current/VMware-VCSA-all-6.5.0-4827210.iso 8 | ESXI_KICKSTART_PATH=/Users/lamw/git/usb-to-sddc/KS.CFG 9 | DEPLOYVM_ZIP_PATH=/Volumes/Storage/DeployVM/Photon/DeployVM.zip 10 | 11 | if [ ! $(uname -s) == "Darwin" ]; then 12 | echo "This script is only meant to run on macOS system" 13 | exit 1 14 | fi 15 | 16 | if [ "$(id -u)" != "0" ]; then 17 | echo "This script must be run as root" 18 | exit 1 19 | fi 20 | 21 | if [ $# -ne 1 ]; then 22 | echo "Usage: $0 [USB_DEVICE]" 23 | echo -e "\n\t$0 /dev/disk4" 24 | exit 25 | fi 26 | 27 | USB_DEVICE=$1 28 | 29 | echo "Please confirm that ${USB_DEVICE} is the USB Device you would like to use (Y/N)" 30 | read RESPONSE 31 | case "$RESPONSE" in [yY]) 32 | ;; 33 | *) echo "Quiting Installer!" 34 | exit 1 35 | ;; 36 | esac 37 | 38 | if [ ! -d ${UNETBOOTIN_APP_PATH} ]; then 39 | echo "Error: Unetbootin App was not found" 40 | exit 1 41 | fi 42 | 43 | if [ ! -e ${ESXI_ISO_PATH} ]; then 44 | echo "Error: ESXi ISO was not found" 45 | exit 1 46 | fi 47 | 48 | if [ ! -e ${VCSA_ISO_PATH} ]; then 49 | echo "Error: VCSA ISO was not found" 50 | exit 1 51 | fi 52 | 53 | if [ ! -e ${DEPLOYVM_ZIP_PATH} ]; then 54 | echo "Error: DeployVM zip file was not found" 55 | exit 1 56 | fi 57 | 58 | if [ ! -e ${ESXI_KICKSTART_PATH} ]; then 59 | echo "Error: ESXi Kickstart file was not found" 60 | exit 1 61 | fi 62 | 63 | echo "Creating BOOT and PAYLOAD partition on USB Device ..." 64 | diskutil partitionDisk ${USB_DEVICE} 2 MBRFormat "MS-DOS FAT16" BOOT 2GB "MS-DOS" PAYLOAD R 65 | 66 | "${UNETBOOTIN_APP_PATH}/Contents/MacOS/unetbootin" lang=en method=diskimage isofile=${ESXI_ISO_PATH} installtype=USB targetdrive=${USB_DEVICE}s1 autoinstall=yes 67 | 68 | echo "Copying KS.cfg to /Volumes/BOOT ..." 69 | cp ${ESXI_KICKSTART_PATH} /Volumes/BOOT 70 | 71 | echo "Updating boot.cfg to use KS.cfg ..." 72 | sed -i .bak 's#kernelopt.*#kernelopt=ks=usb:/KS.CFG#g' /Volumes/BOOT/BOOT.CFG 73 | sed -i .bak 's#kernelopt.*#kernelopt=ks=usb:/KS.CFG#g' /Volumes/BOOT/EFI/BOOT/BOOT.CFG 74 | rm -f /Volumes/BOOT/BOOT.CFG.bak 75 | rm -f /Volumes/BOOT/EFI/BOOT/BOOT.CFG.bak 76 | 77 | echo "Copying Deploy VM zip to /Volumes/PAYLOAD ..." 78 | cp ${DEPLOYVM_ZIP_PATH} /Volumes/PAYLOAD 79 | 80 | echo "Checking to see if VCSA ISO has already been splitted into individual 1GB chunks ..." 81 | VCSA_ISO_DIRECTORY=${VCSA_ISO_PATH%/*} 82 | ls ${VCSA_ISO_DIRECTORY}/VCSA-part-* > /dev/null 2>&1 83 | if [ $? -eq 1 ]; then 84 | echo "Spitting VCSA ISO into individual 1GB chunks ... " 85 | split -b 1073741824 ${VCSA_ISO_PATH} ${VCSA_ISO_DIRECTORY}/VCSA-part- 86 | else 87 | echo "VCSA ISO has already been splitted, skipping step ..." 88 | fi 89 | 90 | echo "Copying VCSA chunks to /Volumes/PAYLOAD ..." 91 | cp ${VCSA_ISO_DIRECTORY}/VCSA-part-* /Volumes/PAYLOAD 92 | 93 | echo "Unmounting /Volumes/{BOOT,PAYLOAD} ..." 94 | diskutil unmountDisk /Volumes/BOOT 95 | -------------------------------------------------------------------------------- /usb-to-sddc-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamw/usb-to-sddc/7eb74f2d7c1dd02b6d9b5bbef13ae8be007a9b11/usb-to-sddc-0.png --------------------------------------------------------------------------------