github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/cloudsigma/client.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloudsigma 5 6 import ( 7 "github.com/altoros/gosigma" 8 "github.com/juju/errors" 9 "github.com/juju/loggo" 10 "github.com/juju/utils" 11 "github.com/juju/utils/arch" 12 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/imagemetadata" 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/state/multiwatcher" 17 ) 18 19 type environClient struct { 20 conn *gosigma.Client 21 uuid string 22 config *environConfig 23 } 24 25 type tracer struct{} 26 27 func (tracer) Logf(format string, args ...interface{}) { 28 logger.Tracef(format, args...) 29 } 30 31 // newClient returns an instance of the CloudSigma client. 32 var newClient = func(cfg *environConfig) (client *environClient, err error) { 33 uuid := cfg.UUID() 34 logger.Debugf("creating CloudSigma client: id=%q", uuid) 35 36 // create connection to CloudSigma 37 conn, err := gosigma.NewClient(cfg.endpoint(), cfg.username(), cfg.password(), nil) 38 if err != nil { 39 return nil, err 40 } 41 42 // configure trace logger 43 if logger.LogLevel() <= loggo.TRACE { 44 conn.Logger(&tracer{}) 45 } 46 47 client = &environClient{ 48 conn: conn, 49 uuid: uuid, 50 config: cfg, 51 } 52 53 return client, nil 54 } 55 56 const ( 57 jujuMetaInstance = "juju-instance" 58 jujuMetaInstanceController = "controller" 59 jujuMetaInstanceServer = "server" 60 61 jujuMetaEnvironment = "juju-model" 62 jujuMetaCoudInit = "cloudinit-user-data" 63 jujuMetaBase64 = "base64_fields" 64 ) 65 66 func (c *environClient) isMyEnvironment(s gosigma.Server) bool { 67 if v, ok := s.Get(jujuMetaEnvironment); ok && c.uuid == v { 68 return true 69 } 70 return false 71 } 72 73 func (c *environClient) isMyServer(s gosigma.Server) bool { 74 if _, ok := s.Get(jujuMetaInstance); ok { 75 return c.isMyEnvironment(s) 76 } 77 return false 78 } 79 80 // isMyController is used to filter servers in the CloudSigma account 81 func (c environClient) isMyController(s gosigma.Server) bool { 82 if v, ok := s.Get(jujuMetaInstance); ok && v == jujuMetaInstanceController { 83 return c.isMyEnvironment(s) 84 } 85 return false 86 } 87 88 // instances returns a list of CloudSigma servers for this environment 89 func (c *environClient) instances() ([]gosigma.Server, error) { 90 return c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer) 91 } 92 93 // instanceMap of server ids to servers at CloudSigma account 94 func (c *environClient) instanceMap() (map[string]gosigma.Server, error) { 95 servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer) 96 if err != nil { 97 return nil, errors.Trace(err) 98 } 99 100 m := make(map[string]gosigma.Server, len(servers)) 101 for _, s := range servers { 102 m[s.UUID()] = s 103 } 104 105 return m, nil 106 } 107 108 //getControllerIds get list of ids for all controller instances 109 func (c *environClient) getControllerIds() (ids []instance.Id, err error) { 110 logger.Tracef("query state...") 111 112 servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyController) 113 if err != nil { 114 return []instance.Id{}, errors.Trace(err) 115 } 116 117 if len(servers) == 0 { 118 return []instance.Id{}, environs.ErrNotBootstrapped 119 } 120 121 ids = make([]instance.Id, len(servers)) 122 123 for i, server := range servers { 124 logger.Tracef("controller id: %s", server.UUID()) 125 ids[i] = instance.Id(server.UUID()) 126 } 127 128 return ids, nil 129 } 130 131 //stopInstance stops the CloudSigma server corresponding to the given instance ID. 132 func (c *environClient) stopInstance(id instance.Id) error { 133 uuid := string(id) 134 if uuid == "" { 135 return errors.New("invalid instance id") 136 } 137 138 s, err := c.conn.Server(uuid) 139 if err != nil { 140 return errors.Trace(err) 141 } 142 143 err = s.StopWait() 144 logger.Tracef("environClient.StopInstance - stop server, %q = %v", uuid, err) 145 146 err = s.Remove(gosigma.RecurseAllDrives) 147 logger.Tracef("environClient.StopInstance - remove server, %q = %v", uuid, err) 148 149 return nil 150 } 151 152 //newInstance creates and starts new instance. 153 func (c *environClient) newInstance(args environs.StartInstanceParams, img *imagemetadata.ImageMetadata, userData []byte) (srv gosigma.Server, drv gosigma.Drive, ar string, err error) { 154 155 defer func() { 156 if err == nil { 157 return 158 } 159 if srv != nil { 160 srv.Remove(gosigma.RecurseAllDrives) 161 } else if drv != nil { 162 drv.Remove() 163 } 164 srv = nil 165 drv = nil 166 }() 167 168 if args.InstanceConfig == nil { 169 err = errors.New("invalid configuration for new instance: InstanceConfig is nil") 170 return nil, nil, "", err 171 } 172 173 logger.Debugf("Tools: %v", args.Tools.URLs()) 174 logger.Debugf("Juju Constraints:" + args.Constraints.String()) 175 logger.Debugf("InstanceConfig: %#v", args.InstanceConfig) 176 177 constraints := newConstraints(args.InstanceConfig.Bootstrap, args.Constraints, img) 178 logger.Debugf("CloudSigma Constraints: %v", constraints) 179 180 originalDrive, err := c.conn.Drive(constraints.driveTemplate, gosigma.LibraryMedia) 181 if err != nil { 182 err = errors.Annotatef(err, "Failed to query drive template") 183 return nil, nil, "", err 184 } 185 186 baseName := "juju-" + c.uuid + "-" + args.InstanceConfig.MachineId 187 188 cloneParams := gosigma.CloneParams{Name: baseName} 189 if drv, err = originalDrive.CloneWait(cloneParams, nil); err != nil { 190 err = errors.Errorf("error cloning drive: %v", err) 191 return nil, nil, "", err 192 } 193 194 if drv.Size() < constraints.driveSize { 195 if err = drv.ResizeWait(constraints.driveSize); err != nil { 196 err = errors.Errorf("error resizing drive: %v", err) 197 return nil, nil, "", err 198 } 199 } 200 201 cc, err := c.generateSigmaComponents(baseName, constraints, args, drv, userData) 202 if err != nil { 203 return nil, nil, "", errors.Trace(err) 204 } 205 206 if srv, err = c.conn.CreateServer(cc); err != nil { 207 err = errors.Annotatef(err, "error creating new instance") 208 return nil, nil, "", err 209 } 210 211 if err = srv.Start(); err != nil { 212 err = errors.Annotatef(err, "error booting new instance") 213 return nil, nil, "", err 214 } 215 216 // populate root drive hardware characteristics 217 switch originalDrive.Arch() { 218 case "64": 219 ar = arch.AMD64 220 case "32": 221 ar = arch.I386 222 default: 223 err = errors.Errorf("unknown arch: %v", ar) 224 return nil, nil, "", err 225 } 226 227 return srv, drv, ar, nil 228 } 229 230 func (c *environClient) generateSigmaComponents(baseName string, constraints *sigmaConstraints, args environs.StartInstanceParams, drv gosigma.Drive, userData []byte) (cc gosigma.Components, err error) { 231 cc.SetName(baseName) 232 cc.SetDescription(baseName) 233 cc.SetSMP(constraints.cores) 234 cc.SetCPU(constraints.power) 235 cc.SetMem(constraints.mem) 236 237 vncpass, err := utils.RandomPassword() 238 if err != nil { 239 err = errors.Errorf("error generating password: %v", err) 240 return 241 } 242 cc.SetVNCPassword(vncpass) 243 logger.Debugf("Setting ssh key: %s end", c.config.AuthorizedKeys()) 244 cc.SetSSHPublicKey(c.config.AuthorizedKeys()) 245 cc.AttachDrive(1, "0:0", "virtio", drv.UUID()) 246 cc.NetworkDHCP4(gosigma.ModelVirtio) 247 248 if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) { 249 cc.SetMeta(jujuMetaInstance, jujuMetaInstanceController) 250 } else { 251 cc.SetMeta(jujuMetaInstance, jujuMetaInstanceServer) 252 } 253 254 cc.SetMeta(jujuMetaEnvironment, c.uuid) 255 cc.SetMeta(jujuMetaCoudInit, string(userData)) 256 cc.SetMeta(jujuMetaBase64, jujuMetaCoudInit) 257 258 return cc, nil 259 }