Skip to content

Commit 0efc3bc

Browse files
committed
Added hyper-v support
1 parent 20d8d85 commit 0efc3bc

10 files changed

+321
-91
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ _testmain.go
2323
*.exe
2424
*.test
2525
*.prof
26+
debug

.vscode/launch.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Launch BMC",
9+
"type": "go",
10+
"request": "launch",
11+
"mode": "auto",
12+
"program": "${workspaceFolder}/main.go",
13+
"env": {},
14+
"args": []
15+
}
16+
]
17+
}

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"go.installDependenciesWhenBuilding": false,
3+
}

Gopkg.lock

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+4
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,7 @@
4444
[prune]
4545
go-tests = true
4646
unused-packages = true
47+
48+
[[constraint]]
49+
name = "github.com/hashicorp/packer"
50+
version = "1.3.5"

utils/config.go

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package utils
22

33
import (
4-
"os"
54
"encoding/json"
6-
"log"
7-
"net"
8-
"infra-ecosphere/vm"
95
"infra-ecosphere/bmc"
6+
"infra-ecosphere/vm"
107
"infra-ecosphere/web"
8+
"log"
9+
"net"
10+
"os"
1111
)
1212

1313
type ConfigNode struct {
14-
BMCIP string
14+
BMCIP string
1515
VMName string
16+
VMType vm.InstanceType
1617
}
1718

1819
type ConfigBMCUser struct {
@@ -21,9 +22,9 @@ type ConfigBMCUser struct {
2122
}
2223

2324
type Configuration struct {
24-
Nodes []ConfigNode
25-
BMCUsers []ConfigBMCUser
26-
WebAPIPort int
25+
Nodes []ConfigNode
26+
BMCUsers []ConfigBMCUser
27+
WebAPIPort int
2728
}
2829

2930
func LoadConfig(configFile string) Configuration {
@@ -44,11 +45,7 @@ func LoadConfig(configFile string) Configuration {
4445

4546
// initialize BMCs and Instances
4647
for _, node := range configuration.Nodes {
47-
fakeNode := false
48-
if len(node.VMName) == 0 {
49-
fakeNode = true
50-
}
51-
instance := vm.AddInstnace(node.VMName, fakeNode)
48+
instance := vm.AddInstnace(node.VMName, node.VMType)
5249
bmc.AddBMC(net.ParseIP(node.BMCIP), instance)
5350
}
5451

@@ -64,4 +61,4 @@ func LoadConfig(configFile string) Configuration {
6461
}
6562

6663
return configuration
67-
}
64+
}

vm/fake-instance.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package vm
2+
3+
type FakeInstance struct {
4+
}
5+
6+
func NewFakeInstance(_ string) *FakeInstance {
7+
return &FakeInstance{}
8+
}
9+
10+
func (instance *FakeInstance) IsRunning() bool {
11+
return true
12+
}
13+
14+
func (instance *FakeInstance) SetBootDevice(dev string) {
15+
return
16+
}
17+
18+
func (instance *FakeInstance) PowerOff() {
19+
return
20+
}
21+
22+
func (instance *FakeInstance) ACPIOff() {
23+
return
24+
}
25+
26+
func (instance *FakeInstance) PowerOn() {
27+
return
28+
}
29+
30+
func (instance *FakeInstance) Reset() {
31+
return
32+
}
33+
34+
func (instance *FakeInstance) NICInitialize() {
35+
return
36+
}

vm/hyperv-instance.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package vm
2+
3+
import (
4+
"bytes"
5+
"log"
6+
"text/template"
7+
8+
"github.com/hashicorp/packer/common/powershell"
9+
"github.com/hashicorp/packer/common/powershell/hyperv"
10+
)
11+
12+
type HypervInstance struct {
13+
Name string
14+
15+
defaultBootOrder []string
16+
nextBootOrder []string
17+
changeBootOrder bool
18+
}
19+
20+
const powerShellTemplateGen1 = `
21+
{{$out := .}}
22+
param([string]$vmName)
23+
Hyper-V\Set-VMBios -VMName $vmName -StartupOrder @({{ range $index, $device := .Devices}}{{if $index}},{{end}}{{index $out.DeviceMap 0 $device}}{{end}})
24+
`
25+
26+
const powerShellTemplateGen2 = `
27+
{{$out := .}}
28+
param([string]$vmName)
29+
{{ range $index, $device := .Devices}}
30+
{{index $out.GettersMap $device}}
31+
{{end}}
32+
Hyper-V\Set-VMFirmware -VMName $vmName -BootOrder @({{ range $index, $device := .Devices}}{{if $index}},{{end}}{{index $out.DeviceMap 1 $device}}{{end}})
33+
`
34+
35+
type templateParameters struct {
36+
Devices []string
37+
DeviceMap []map[string]string
38+
GettersMap map[string]string
39+
}
40+
41+
var gettersMap map[string]string
42+
var deviceMap []map[string]string
43+
44+
func init() {
45+
deviceMap = make([]map[string]string, 2)
46+
47+
deviceMap[0] = make(map[string]string)
48+
deviceMap[0][BOOT_DEVICE_PXE] = "\"LegacyNetworkAdapter\""
49+
deviceMap[0][BOOT_DEVICE_DISK] = "\"IDE\""
50+
deviceMap[0][BOOT_DEVICE_CD_DVD] = "\"CD\""
51+
deviceMap[0][BOOT_DEVICE_FLOPPY] = "\"Floppy\""
52+
53+
deviceMap[1] = make(map[string]string)
54+
deviceMap[1][BOOT_DEVICE_PXE] = "$MyNIC"
55+
deviceMap[1][BOOT_DEVICE_DISK] = "$MyHD"
56+
deviceMap[1][BOOT_DEVICE_CD_DVD] = "$MyDVD"
57+
deviceMap[1][BOOT_DEVICE_FLOPPY] = ""
58+
59+
gettersMap = make(map[string]string)
60+
gettersMap[BOOT_DEVICE_PXE] = "$MyNIC = Get-VMNetworkAdapter $vmName"
61+
gettersMap[BOOT_DEVICE_DISK] = "$MyHD = Get-VMHardDiskDrive $vmName"
62+
gettersMap[BOOT_DEVICE_CD_DVD] = "$MyDVD = Get-VMDvdDrive $vmName"
63+
gettersMap[BOOT_DEVICE_FLOPPY] = ""
64+
}
65+
66+
func NewHypervInstance(name string) *HypervInstance {
67+
return &HypervInstance{
68+
Name: name,
69+
defaultBootOrder: []string{"disk", "net"},
70+
}
71+
}
72+
73+
func (instance *HypervInstance) IsRunning() bool {
74+
running, _ := hyperv.IsRunning(instance.Name)
75+
return running
76+
}
77+
78+
func (instance *HypervInstance) SetBootDevice(dev string) {
79+
instance.nextBootOrder = []string{dev}
80+
instance.changeBootOrder = true
81+
}
82+
83+
func (instance *HypervInstance) PowerOff() {
84+
hyperv.ShutDown(instance.Name)
85+
}
86+
87+
func (instance *HypervInstance) ACPIOff() {
88+
hyperv.TurnOff(instance.Name)
89+
}
90+
91+
func (instance *HypervInstance) PowerOn() {
92+
generation, err := hyperv.GetVirtualMachineGeneration(instance.Name)
93+
if err != nil {
94+
log.Fatalf(" Instance: Failed to get retrieve generation for VM %s: %s", instance.Name, err.Error())
95+
return
96+
}
97+
98+
bootOrder := instance.defaultBootOrder
99+
100+
if instance.changeBootOrder {
101+
bootOrder = instance.nextBootOrder
102+
103+
instance.nextBootOrder = make([]string, 4)
104+
instance.changeBootOrder = false
105+
}
106+
107+
log.Println("Current Boot Order = ", bootOrder)
108+
109+
parameters := templateParameters{
110+
Devices: bootOrder,
111+
DeviceMap: deviceMap,
112+
GettersMap: gettersMap,
113+
}
114+
115+
powerShellTemplate := powerShellTemplateGen2
116+
if generation < 2 {
117+
powerShellTemplate = powerShellTemplateGen1
118+
}
119+
120+
var buffer bytes.Buffer
121+
compiledPowerShellTemplate, err := template.New("script").Parse(powerShellTemplate)
122+
if err != nil {
123+
log.Fatalf(" Instance: Failed to parse boot command for VM %s: %s", instance.Name, err.Error())
124+
return
125+
}
126+
if err := compiledPowerShellTemplate.Execute(&buffer, parameters); err != nil {
127+
log.Fatalf(" Instance: Failed to generate boot command for VM %s: %s", instance.Name, err.Error())
128+
return
129+
}
130+
131+
var ps powershell.PowerShellCmd
132+
err = ps.Run(buffer.String(), instance.Name)
133+
134+
hyperv.StartVirtualMachine(instance.Name)
135+
}
136+
137+
func (instance *HypervInstance) Reset() {
138+
hyperv.RestartVirtualMachine(instance.Name)
139+
}
140+
141+
func (instance *HypervInstance) NICInitialize() {
142+
// Nothing to do here
143+
return
144+
}

vm/instance.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package vm
2+
3+
import (
4+
"log"
5+
)
6+
7+
type InstanceType string
8+
9+
const (
10+
Fake InstanceType = "Fake"
11+
VirtualBox InstanceType = "VirtualBox"
12+
HyperV InstanceType = "Hyper-V"
13+
)
14+
15+
const (
16+
BOOT_DEVICE_PXE = "net"
17+
BOOT_DEVICE_DISK = "disk"
18+
BOOT_DEVICE_CD_DVD = "dvd"
19+
BOOT_DEVICE_FLOPPY = "floppy"
20+
)
21+
22+
type Instance interface {
23+
IsRunning() bool
24+
SetBootDevice(dev string)
25+
PowerOff()
26+
ACPIOff()
27+
PowerOn()
28+
Reset()
29+
NICInitialize()
30+
}
31+
32+
var instances map[string]Instance
33+
34+
func init() {
35+
instances = make(map[string]Instance)
36+
}
37+
38+
func AddInstnace(name string, instanceType InstanceType) Instance {
39+
var newInstance Instance
40+
41+
switch instanceType {
42+
case VirtualBox:
43+
newInstance = NewVirtualBoxInstance(name)
44+
break
45+
case HyperV:
46+
newInstance = NewHypervInstance(name)
47+
break
48+
case Fake:
49+
newInstance = NewFakeInstance(name)
50+
break
51+
default:
52+
log.Println("Unknown instance type ", instanceType)
53+
newInstance = NewFakeInstance(name)
54+
break
55+
}
56+
57+
instances[name] = newInstance
58+
newInstance.NICInitialize()
59+
log.Println("Add instance ", name)
60+
61+
return newInstance
62+
}
63+
64+
func DeleteInstance(name string) {
65+
_, ok := instances[name]
66+
if ok {
67+
delete(instances, name)
68+
}
69+
log.Println("Remove instance ", name)
70+
}
71+
72+
func GetInstance(name string) (instance Instance, ok bool) {
73+
instance, ok = instances[name]
74+
return instance, ok
75+
}

0 commit comments

Comments
 (0)