Add a snapshot (#22)

This commit is contained in:
Elizaveta Tretyakova 2017-06-27 10:32:59 +03:00 committed by Michael Kuzmin
parent e394b27a21
commit fbe2065ec2
7 changed files with 135 additions and 58 deletions

View File

@ -51,7 +51,8 @@ $ packer build template.json
* `dc_name` (source datacenter) * `dc_name` (source datacenter)
* Post-processing: * Post-processing:
* `linked_clone` * `linked_clone`
* `to_template` * `create_snapshot`
* `convert_to_template`
See an example below: See an example below:
```json ```json
@ -71,7 +72,8 @@ See an example below:
"vm_name": "clone_name", "vm_name": "clone_name",
"host": "172.16.0.1", "host": "172.16.0.1",
"linked_clone": true, "linked_clone": true,
"to_template": true, "create_snapshot": true,
"convert_to_template": true,
"RAM": "1024", "RAM": "1024",
"cpus": "2", "cpus": "2",

View File

@ -35,6 +35,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps. // Build the steps.
steps := []multistep.Step{ steps := []multistep.Step{
&StepSetup{
config: b.config,
},
&StepCloneVM{ &StepCloneVM{
config: b.config, config: b.config,
}, },
@ -65,8 +68,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Command: b.config.ShutdownCommand, Command: b.config.ShutdownCommand,
ShutdownTimeout: b.config.ShutdownTimeout, ShutdownTimeout: b.config.ShutdownTimeout,
}, },
&StepPostProcess{ &StepCreateSnapshot{
ToTemplate: b.config.ToTemplate, createSnapshot: b.config.CreateSnapshot,
},
&StepConvertToTemplate{
ConvertToTemplate: b.config.ConvertToTemplate,
}, },
} }

View File

@ -31,14 +31,16 @@ type Config struct {
// Settings // Settings
LinkedClone bool `mapstructure:"linked_clone"` LinkedClone bool `mapstructure:"linked_clone"`
ToTemplate bool `mapstructure:"to_template"` ConvertToTemplate bool `mapstructure:"convert_to_template"`
RawShutdownTimeout string `mapstructure:"shutdown_timeout"` RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
ShutdownTimeout time.Duration `` ShutdownTimeout time.Duration ``
// Hardware // Customization
Cpus string `mapstructure:"cpus"` Cpus string `mapstructure:"cpus"`
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
Ram string `mapstructure:"RAM"` Ram string `mapstructure:"RAM"`
CreateSnapshot bool `mapstructure:"create_snapshot"`
ctx interpolate.Context ctx interpolate.Context
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/find" "github.com/vmware/govmomi/find"
"fmt" "fmt"
"net/url"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
"errors" "errors"
) )
@ -31,25 +30,15 @@ type StepCloneVM struct{
} }
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction { func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*govmomi.Client)
ctx := state.Get("ctx").(context.Context)
finder := state.Get("finder").(*find.Finder)
dc := state.Get("dc").(*object.Datacenter)
vmSrc := state.Get("vmSrc").(*object.VirtualMachine)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ui.Say("start cloning...") ui.Say("start cloning...")
// Prepare entities: client (authentification), finder, folder, virtual machine
client, ctx, err := createClient(s.config.Url, s.config.Username, s.config.Password)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Set up finder
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, s.config.DCName)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
finder.SetDatacenter(dc)
// Get folder // Get folder
folder, err := finder.FolderOrDefault(ctx, s.config.FolderName) folder, err := finder.FolderOrDefault(ctx, s.config.FolderName)
if err != nil { if err != nil {
@ -74,13 +63,6 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
} }
} }
// Get source VM
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
vm, err := cloneVM(&CloneParameters{ vm, err := cloneVM(&CloneParameters{
client: client, client: client,
folder: folder, folder: folder,
@ -179,24 +161,3 @@ func cloneVM(params *CloneParameters) (vm *object.VirtualMachine, err error) {
vm = object.NewVirtualMachine(params.client.Client, info.Result.(types.ManagedObjectReference)) vm = object.NewVirtualMachine(params.client.Client, info.Result.(types.ManagedObjectReference))
return vm, nil return vm, nil
} }
func createClient(URL, username, password string) (*govmomi.Client, context.Context, error) {
// create context
ctx := context.TODO() // an empty, default context (for those, who is unsure)
// create a client
// (connected to the specified URL,
// logged in with the username-password)
u, err := url.Parse(URL) // create a URL object from string
if err != nil {
return nil, nil, err
}
u.User = url.UserPassword(username, password) // set username and password for automatical authentification
fmt.Println(u.String())
client, err := govmomi.NewClient(ctx, u,true) // creating a client (logs in with given uname&pswd)
if err != nil {
return nil, nil, err
}
return client, ctx, nil
}

33
step_create_snapshot.go Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"github.com/mitchellh/multistep"
"github.com/hashicorp/packer/packer"
"context"
"github.com/vmware/govmomi/object"
)
type StepCreateSnapshot struct{
createSnapshot bool
}
func (s *StepCreateSnapshot) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*object.VirtualMachine)
ctx := state.Get("ctx").(context.Context)
if s.createSnapshot {
ui.Say("creating snapshot...")
_, err := vm.CreateSnapshot(ctx, "packer_snapshot", "", true, true)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Say("done")
}
return multistep.ActionContinue
}
func (s *StepCreateSnapshot) Cleanup(state multistep.StateBag) {}

73
step_setup.go Normal file
View File

@ -0,0 +1,73 @@
package main
import (
"github.com/mitchellh/multistep"
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/find"
"fmt"
"github.com/vmware/govmomi"
"context"
"net/url"
)
type StepSetup struct{
config *Config
}
func (s *StepSetup) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("setup...")
// Prepare entities: client (authentification), finder, folder, virtual machine
client, ctx, err := createClient(s.config.Url, s.config.Username, s.config.Password)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Set up finder
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, s.config.DCName)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
finder.SetDatacenter(dc)
// Get source VM
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("client", client)
state.Put("ctx", ctx)
state.Put("finder", finder)
state.Put("dc", dc)
state.Put("vmSrc", vmSrc)
return multistep.ActionContinue
}
func (s *StepSetup) Cleanup(state multistep.StateBag) {}
func createClient(URL, username, password string) (*govmomi.Client, context.Context, error) {
// create context
ctx := context.TODO() // an empty, default context (for those, who is unsure)
// create a client
// (connected to the specified URL,
// logged in with the username-password)
u, err := url.Parse(URL) // create a URL object from string
if err != nil {
return nil, nil, err
}
u.User = url.UserPassword(username, password) // set username and password for automatical authentification
fmt.Println(u.String())
client, err := govmomi.NewClient(ctx, u,true) // creating a client (logs in with given uname&pswd)
if err != nil {
return nil, nil, err
}
return client, ctx, nil
}

View File

@ -7,17 +7,17 @@ import (
"context" "context"
) )
type StepPostProcess struct{ type StepConvertToTemplate struct{
ToTemplate bool ConvertToTemplate bool
} }
func (s *StepPostProcess) Run(state multistep.StateBag) multistep.StepAction { func (s *StepConvertToTemplate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*object.VirtualMachine) vm := state.Get("vm").(*object.VirtualMachine)
ctx := state.Get("ctx").(context.Context) ctx := state.Get("ctx").(context.Context)
// Turning into template if needed // Turning into template if needed
if s.ToTemplate { if s.ConvertToTemplate {
ui.Say("turning into template...") ui.Say("turning into template...")
err := vm.MarkAsTemplate(ctx) err := vm.MarkAsTemplate(ctx)
if err != nil { if err != nil {
@ -30,4 +30,4 @@ func (s *StepPostProcess) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepPostProcess) Cleanup(state multistep.StateBag) {} func (s *StepConvertToTemplate) Cleanup(state multistep.StateBag) {}