From fbe2065ec2e1414f60341086925cbe8816783c2b Mon Sep 17 00:00:00 2001 From: Elizaveta Tretyakova Date: Tue, 27 Jun 2017 10:32:59 +0300 Subject: [PATCH] Add a snapshot (#22) --- README.md | 6 +- builder.go | 10 +++- config.go | 10 ++-- step_clone_vm.go | 51 ++--------------- step_create_snapshot.go | 33 +++++++++++ step_setup.go | 73 ++++++++++++++++++++++++ step_post_process.go => step_template.go | 10 ++-- 7 files changed, 135 insertions(+), 58 deletions(-) create mode 100644 step_create_snapshot.go create mode 100644 step_setup.go rename step_post_process.go => step_template.go (68%) diff --git a/README.md b/README.md index f6253e82c..f0b9b3af3 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ $ packer build template.json * `dc_name` (source datacenter) * Post-processing: * `linked_clone` - * `to_template` + * `create_snapshot` + * `convert_to_template` See an example below: ```json @@ -71,7 +72,8 @@ See an example below: "vm_name": "clone_name", "host": "172.16.0.1", "linked_clone": true, - "to_template": true, + "create_snapshot": true, + "convert_to_template": true, "RAM": "1024", "cpus": "2", diff --git a/builder.go b/builder.go index 91e80927a..1239e4903 100644 --- a/builder.go +++ b/builder.go @@ -35,6 +35,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{ + &StepSetup{ + config: b.config, + }, &StepCloneVM{ 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, ShutdownTimeout: b.config.ShutdownTimeout, }, - &StepPostProcess{ - ToTemplate: b.config.ToTemplate, + &StepCreateSnapshot{ + createSnapshot: b.config.CreateSnapshot, + }, + &StepConvertToTemplate{ + ConvertToTemplate: b.config.ConvertToTemplate, }, } diff --git a/config.go b/config.go index c704e2a55..b98ff9b94 100644 --- a/config.go +++ b/config.go @@ -30,15 +30,17 @@ type Config struct { Datastore string `mapstructure:"datastore"` // Settings - LinkedClone bool `mapstructure:"linked_clone"` - ToTemplate bool `mapstructure:"to_template"` + LinkedClone bool `mapstructure:"linked_clone"` + ConvertToTemplate bool `mapstructure:"convert_to_template"` RawShutdownTimeout string `mapstructure:"shutdown_timeout"` - ShutdownTimeout time.Duration `` + ShutdownTimeout time.Duration `` - // Hardware + // Customization Cpus string `mapstructure:"cpus"` ShutdownCommand string `mapstructure:"shutdown_command"` Ram string `mapstructure:"RAM"` + CreateSnapshot bool `mapstructure:"create_snapshot"` + ctx interpolate.Context } diff --git a/step_clone_vm.go b/step_clone_vm.go index ddbc7c220..35e4043b9 100644 --- a/step_clone_vm.go +++ b/step_clone_vm.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/packer/packer" "github.com/vmware/govmomi/find" "fmt" - "net/url" "github.com/vmware/govmomi/vim25/mo" "errors" ) @@ -31,25 +30,15 @@ type StepCloneVM struct{ } 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.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 folder, err := finder.FolderOrDefault(ctx, s.config.FolderName) 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{ client: client, 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)) 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 -} diff --git a/step_create_snapshot.go b/step_create_snapshot.go new file mode 100644 index 000000000..81a07acee --- /dev/null +++ b/step_create_snapshot.go @@ -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) {} diff --git a/step_setup.go b/step_setup.go new file mode 100644 index 000000000..2b5327c29 --- /dev/null +++ b/step_setup.go @@ -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 +} diff --git a/step_post_process.go b/step_template.go similarity index 68% rename from step_post_process.go rename to step_template.go index 3c4c074a5..3b64985bc 100644 --- a/step_post_process.go +++ b/step_template.go @@ -7,17 +7,17 @@ import ( "context" ) -type StepPostProcess struct{ - ToTemplate bool +type StepConvertToTemplate struct{ + 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) vm := state.Get("vm").(*object.VirtualMachine) ctx := state.Get("ctx").(context.Context) // Turning into template if needed - if s.ToTemplate { + if s.ConvertToTemplate { ui.Say("turning into template...") err := vm.MarkAsTemplate(ctx) if err != nil { @@ -30,4 +30,4 @@ func (s *StepPostProcess) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *StepPostProcess) Cleanup(state multistep.StateBag) {} +func (s *StepConvertToTemplate) Cleanup(state multistep.StateBag) {}