github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/tools/lxdclient/instance.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxdclient 7 8 import ( 9 "fmt" 10 "math" 11 "strings" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/utils/arch" 16 "github.com/lxc/lxd/shared" 17 ) 18 19 // Constants related to user metadata. 20 const ( 21 MetadataNamespace = "user" 22 23 // This is defined by the cloud-init code: 24 // http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/cloudinit/sources/ 25 // http://cloudinit.readthedocs.org/en/latest/ 26 // Also see https://github.com/lxc/lxd/blob/master/specs/configuration.md. 27 UserdataKey = "user-data" 28 29 megabyte = 1024 * 1024 30 ) 31 32 func resolveConfigKey(name string, namespace ...string) string { 33 parts := append(namespace, name) 34 return strings.Join(parts, ".") 35 } 36 37 func splitConfigKey(key string) (string, string) { 38 parts := strings.SplitN(key, ".", 2) 39 if len(parts) == 1 { 40 return "", parts[0] 41 } 42 return parts[0], parts[1] 43 } 44 45 // AliveStatuses are the LXD statuses that indicate a container is "alive". 46 var AliveStatuses = []string{ 47 // TODO(ericsnow) Also support StatusOK, StatusPending, and StatusThawed? 48 StatusStarting, 49 StatusStarted, 50 StatusRunning, 51 StatusStopping, 52 StatusStopped, 53 } 54 55 // InstanceSpec holds all the information needed to create a new LXD 56 // container. 57 type InstanceSpec struct { 58 // Name is the "name" of the instance. 59 Name string 60 61 // Image is the name of the image to use. 62 Image string 63 64 // ImageRemote identifies the remote to use for images. By default 65 // the client's remote is used. 66 ImageRemote string 67 68 // Profiles are the names of the container profiles to apply to the 69 // new container, in order. 70 Profiles []string 71 72 // Ephemeral indicates whether or not the container should be 73 // destroyed when the LXD host is restarted. 74 Ephemeral bool 75 76 // Metadata is the instance metadata. 77 Metadata map[string]string 78 79 // TODO(ericsnow) Other possible fields: 80 // Disks 81 // Networks 82 // Metadata 83 // Tags 84 } 85 86 func (spec InstanceSpec) config() map[string]string { 87 return resolveMetadata(spec.Metadata) 88 } 89 90 func (spec InstanceSpec) info(namespace string) *shared.ContainerInfo { 91 name := spec.Name 92 if namespace != "" { 93 name = namespace + "-" + name 94 } 95 96 return &shared.ContainerInfo{ 97 Architecture: "", 98 Config: spec.config(), 99 CreationDate: time.Time{}, 100 Devices: shared.Devices{}, 101 Ephemeral: spec.Ephemeral, 102 ExpandedConfig: map[string]string{}, 103 ExpandedDevices: shared.Devices{}, 104 Name: name, 105 Profiles: spec.Profiles, 106 Status: "", 107 StatusCode: 0, 108 } 109 } 110 111 // Summary builds an InstanceSummary based on the spec and returns it. 112 func (spec InstanceSpec) Summary(namespace string) InstanceSummary { 113 info := spec.info(namespace) 114 return newInstanceSummary(info) 115 } 116 117 // InstanceHardware describes the hardware characteristics of a LXC container. 118 type InstanceHardware struct { 119 // Architecture is the CPU architecture. 120 Architecture string 121 122 // NumCores is the number of CPU cores. 123 NumCores uint 124 125 // MemoryMB is the memory allocation for the container. 126 MemoryMB uint 127 128 // RootDiskMB is the size of the root disk, in MB. 129 RootDiskMB uint64 130 } 131 132 // InstanceSummary captures all the data needed by Instance. 133 type InstanceSummary struct { 134 // Name is the "name" of the instance. 135 Name string 136 137 // Status holds the status of the instance at a certain point in time. 138 Status string 139 140 // Hardware describes the instance's hardware characterstics. 141 Hardware InstanceHardware 142 143 // Metadata is the instance metadata. 144 Metadata map[string]string 145 } 146 147 func newInstanceSummary(info *shared.ContainerInfo) InstanceSummary { 148 archStr := arch.NormaliseArch(info.Architecture) 149 150 var numCores uint = 0 // default to all 151 if raw := info.Config["limits.cpu"]; raw != "" { 152 fmt.Sscanf(raw, "%d", &numCores) 153 } 154 155 var mem uint = 0 // default to all 156 if raw := info.Config["limits.memory"]; raw != "" { 157 result, err := shared.ParseByteSizeString(raw) 158 if err != nil { 159 logger.Errorf("failed to parse %s into bytes, ignoring err: %s", raw, err) 160 mem = 0 161 } else { 162 // We're going to put it into MemoryMB, so adjust by a megabyte 163 result = result / megabyte 164 if result > math.MaxUint32 { 165 logger.Errorf("byte string %s overflowed uint32", raw) 166 mem = math.MaxUint32 167 } else { 168 mem = uint(result) 169 } 170 } 171 } 172 173 // TODO(ericsnow) Factor this out into a function. 174 statusStr := info.Status 175 for status, code := range allStatuses { 176 if info.StatusCode == code { 177 statusStr = status 178 break 179 } 180 } 181 182 metadata := extractMetadata(info.Config) 183 184 return InstanceSummary{ 185 Name: info.Name, 186 Status: statusStr, 187 Metadata: metadata, 188 Hardware: InstanceHardware{ 189 Architecture: archStr, 190 NumCores: numCores, 191 MemoryMB: mem, 192 }, 193 } 194 } 195 196 // Instance represents a single realized LXD container. 197 type Instance struct { 198 InstanceSummary 199 200 // spec is the InstanceSpec used to create this instance. 201 spec *InstanceSpec 202 } 203 204 func newInstance(info *shared.ContainerInfo, spec *InstanceSpec) *Instance { 205 summary := newInstanceSummary(info) 206 return NewInstance(summary, spec) 207 } 208 209 // NewInstance builds an instance from the provided summary and spec 210 // and returns it. 211 func NewInstance(summary InstanceSummary, spec *InstanceSpec) *Instance { 212 if spec != nil { 213 // Make a copy. 214 val := *spec 215 spec = &val 216 } 217 return &Instance{ 218 InstanceSummary: summary, 219 spec: spec, 220 } 221 } 222 223 // Status returns a string identifying the status of the instance. 224 func (gi Instance) Status() string { 225 return gi.InstanceSummary.Status 226 } 227 228 // CurrentStatus returns a string identifying the status of the instance. 229 func (gi Instance) CurrentStatus(client *Client) (string, error) { 230 // TODO(ericsnow) Do this a better way? 231 232 inst, err := client.Instance(gi.Name) 233 if err != nil { 234 return "", errors.Trace(err) 235 } 236 return inst.Status(), nil 237 } 238 239 // Metadata returns the user-specified metadata for the instance. 240 func (gi Instance) Metadata() map[string]string { 241 // TODO*ericsnow) return a copy? 242 return gi.InstanceSummary.Metadata 243 } 244 245 func resolveMetadata(metadata map[string]string) map[string]string { 246 config := make(map[string]string) 247 248 for name, val := range metadata { 249 key := resolveConfigKey(name, MetadataNamespace) 250 config[key] = val 251 } 252 253 return config 254 } 255 256 func extractMetadata(config map[string]string) map[string]string { 257 metadata := make(map[string]string) 258 259 for key, val := range config { 260 namespace, name := splitConfigKey(key) 261 if namespace != MetadataNamespace { 262 continue 263 } 264 metadata[name] = val 265 } 266 267 return metadata 268 }