forked from canonical/lxd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain_migrate.go
201 lines (165 loc) · 4.64 KB
/
main_migrate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"sort"
"strings"
"syscall"
"github.com/spf13/cobra"
"github.com/lxc/lxd/lxc/utils"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
)
type cmdMigrate struct {
cmd *cobra.Command
global *cmdGlobal
flagConfig []string
flagNetwork string
flagProfile []string
flagStorage string
flagType string
flagNoProfiles bool
}
func (c *cmdMigrate) Command() *cobra.Command {
cmd := &cobra.Command{}
cmd.Use = "lxd-p2c <target URL> <container name> <filesystem root> [<filesystem mounts>...]"
cmd.Short = "Physical to container migration tool"
cmd.Long = `Description:
Physical to container migration tool
This tool lets you turn any Linux filesystem (including your current one)
into a LXD container on a remote LXD host.
It will setup a clean mount tree made of the root filesystem and any
additional mount you list, then transfer this through LXD's migration
API to create a new container from it.
The same set of options as ` + "`lxc launch`" + ` are also supported.
`
cmd.RunE = c.Run
cmd.Flags().StringArrayVarP(&c.flagConfig, "config", "c", nil, "Configuration key and value to set on the container"+"``")
cmd.Flags().StringVarP(&c.flagNetwork, "network", "n", "", "Network to use for the container"+"``")
cmd.Flags().StringArrayVarP(&c.flagProfile, "profile", "p", nil, "Profile to apply to the container"+"``")
cmd.Flags().StringVarP(&c.flagStorage, "storage", "s", "", "Storage pool to use for the container"+"``")
cmd.Flags().StringVarP(&c.flagType, "type", "t", "", "Instance type to use for the container"+"``")
cmd.Flags().BoolVar(&c.flagNoProfiles, "no-profiles", false, "Create the container with no profiles applied")
c.cmd = cmd
return cmd
}
func (c *cmdMigrate) Run(cmd *cobra.Command, args []string) error {
// Help and usage
if len(args) == 0 {
return cmd.Help()
}
// Sanity checks
if os.Geteuid() != 0 {
return fmt.Errorf("This tool must be run as root")
}
_, err := exec.LookPath("rsync")
if err != nil {
return err
}
if c.flagNoProfiles && len(c.flagProfile) != 0 {
return fmt.Errorf("no-profiles can't be specified alongside profiles")
}
// Handle mandatory arguments
if len(args) < 3 {
cmd.Help()
return fmt.Errorf("Missing required arguments")
}
// Get and sort the mounts
mounts := args[2:]
sort.Strings(mounts)
// Create the temporary directory to be used for the mounts
path, err := ioutil.TempDir("", "lxd-p2c_mount_")
if err != nil {
return err
}
// Automatically clean-up the temporary path on exit
defer func(path string) {
syscall.Unmount(path, syscall.MNT_DETACH)
os.Remove(path)
}(path)
// Create the rootfs directory
fullPath := fmt.Sprintf("%s/rootfs", path)
err = os.Mkdir(fullPath, 0755)
if err != nil {
return err
}
// Setup the source (mounts)
err = setupSource(fullPath, mounts)
if err != nil {
return fmt.Errorf("Failed to setup the source: %v", err)
}
// Connect to the target
dst, err := connectTarget(args[0])
if err != nil {
return err
}
// Container creation request
apiArgs := api.ContainersPost{}
apiArgs.Name = args[1]
apiArgs.Source = api.ContainerSource{
Type: "migration",
Mode: "push",
}
// System architecture
architectureName, err := osarch.ArchitectureGetLocal()
if err != nil {
return err
}
apiArgs.Architecture = architectureName
// Instance type
apiArgs.InstanceType = c.flagType
// Config overrides
apiArgs.Config = map[string]string{}
for _, entry := range c.flagConfig {
if !strings.Contains(entry, "=") {
return fmt.Errorf("Bad key=value configuration: %v", entry)
}
fields := strings.SplitN(entry, "=", 2)
apiArgs.Config[fields[0]] = fields[1]
}
// Profiles
if len(c.flagProfile) != 0 {
apiArgs.Profiles = c.flagProfile
}
if c.flagNoProfiles {
apiArgs.Profiles = []string{}
}
// Devices
apiArgs.Devices = map[string]map[string]string{}
network := c.flagNetwork
if network != "" {
apiArgs.Devices["eth0"] = map[string]string{
"type": "nic",
"nictype": "bridged",
"parent": network,
"name": "eth0",
}
}
storage := c.flagStorage
if storage != "" {
apiArgs.Devices["root"] = map[string]string{
"type": "disk",
"pool": storage,
"path": "/",
}
}
// Create the container
op, err := dst.CreateContainer(apiArgs)
if err != nil {
return err
}
progress := utils.ProgressRenderer{Format: "Transferring container: %s"}
_, err = op.AddHandler(progress.UpdateOp)
if err != nil {
progress.Done("")
return err
}
err = transferRootfs(dst, op, fullPath)
if err != nil {
return err
}
progress.Done(fmt.Sprintf("Container %s successfully created", apiArgs.Name))
return nil
}