1/* 2 * Copyright 2014 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5package vmwarevcloudair 6 7import ( 8 "fmt" 9 "io/ioutil" 10 "net" 11 "strconv" 12 "strings" 13 14 "github.com/vmware/govcloudair" 15 16 "github.com/docker/machine/libmachine/drivers" 17 "github.com/docker/machine/libmachine/log" 18 "github.com/docker/machine/libmachine/mcnflag" 19 "github.com/docker/machine/libmachine/mcnutils" 20 "github.com/docker/machine/libmachine/ssh" 21 "github.com/docker/machine/libmachine/state" 22) 23 24type Driver struct { 25 *drivers.BaseDriver 26 UserName string 27 UserPassword string 28 ComputeID string 29 VDCID string 30 OrgVDCNet string 31 EdgeGateway string 32 PublicIP string 33 Catalog string 34 CatalogItem string 35 DockerPort int 36 CPUCount int 37 MemorySize int 38 VAppID string 39} 40 41const ( 42 defaultCatalog = "Public Catalog" 43 defaultCatalogItem = "Ubuntu Server 12.04 LTS (amd64 20150127)" 44 defaultCpus = 1 45 defaultMemory = 2048 46 defaultSSHPort = 22 47 defaultDockerPort = 2376 48) 49 50// GetCreateFlags registers the flags this driver adds to 51// "docker hosts create" 52func (d *Driver) GetCreateFlags() []mcnflag.Flag { 53 return []mcnflag.Flag{ 54 mcnflag.StringFlag{ 55 EnvVar: "VCLOUDAIR_USERNAME", 56 Name: "vmwarevcloudair-username", 57 Usage: "vCloud Air username", 58 }, 59 mcnflag.StringFlag{ 60 EnvVar: "VCLOUDAIR_PASSWORD", 61 Name: "vmwarevcloudair-password", 62 Usage: "vCloud Air password", 63 }, 64 mcnflag.StringFlag{ 65 EnvVar: "VCLOUDAIR_COMPUTEID", 66 Name: "vmwarevcloudair-computeid", 67 Usage: "vCloud Air Compute ID (if using Dedicated Cloud)", 68 }, 69 mcnflag.StringFlag{ 70 EnvVar: "VCLOUDAIR_VDCID", 71 Name: "vmwarevcloudair-vdcid", 72 Usage: "vCloud Air VDC ID", 73 }, 74 mcnflag.StringFlag{ 75 EnvVar: "VCLOUDAIR_ORGVDCNETWORK", 76 Name: "vmwarevcloudair-orgvdcnetwork", 77 Usage: "vCloud Air Org VDC Network (Default is <vdcid>-default-routed)", 78 }, 79 mcnflag.StringFlag{ 80 EnvVar: "VCLOUDAIR_EDGEGATEWAY", 81 Name: "vmwarevcloudair-edgegateway", 82 Usage: "vCloud Air Org Edge Gateway (Default is <vdcid>)", 83 }, 84 mcnflag.StringFlag{ 85 EnvVar: "VCLOUDAIR_PUBLICIP", 86 Name: "vmwarevcloudair-publicip", 87 Usage: "vCloud Air Org Public IP to use", 88 }, 89 mcnflag.StringFlag{ 90 EnvVar: "VCLOUDAIR_CATALOG", 91 Name: "vmwarevcloudair-catalog", 92 Usage: "vCloud Air Catalog (default is Public Catalog)", 93 Value: defaultCatalog, 94 }, 95 mcnflag.StringFlag{ 96 EnvVar: "VCLOUDAIR_CATALOGITEM", 97 Name: "vmwarevcloudair-catalogitem", 98 Usage: "vCloud Air Catalog Item (default is Ubuntu Precise)", 99 Value: defaultCatalogItem, 100 }, 101 mcnflag.IntFlag{ 102 EnvVar: "VCLOUDAIR_CPU_COUNT", 103 Name: "vmwarevcloudair-cpu-count", 104 Usage: "vCloud Air VM Cpu Count (default 1)", 105 Value: defaultCpus, 106 }, 107 mcnflag.IntFlag{ 108 EnvVar: "VCLOUDAIR_MEMORY_SIZE", 109 Name: "vmwarevcloudair-memory-size", 110 Usage: "vCloud Air VM Memory Size in MB (default 2048)", 111 Value: defaultMemory, 112 }, 113 mcnflag.IntFlag{ 114 EnvVar: "VCLOUDAIR_SSH_PORT", 115 Name: "vmwarevcloudair-ssh-port", 116 Usage: "vCloud Air SSH port", 117 Value: defaultSSHPort, 118 }, 119 mcnflag.IntFlag{ 120 EnvVar: "VCLOUDAIR_DOCKER_PORT", 121 Name: "vmwarevcloudair-docker-port", 122 Usage: "vCloud Air Docker port", 123 Value: defaultDockerPort, 124 }, 125 } 126} 127 128func NewDriver(hostName, storePath string) drivers.Driver { 129 return &Driver{ 130 Catalog: defaultCatalog, 131 CatalogItem: defaultCatalogItem, 132 CPUCount: defaultCpus, 133 MemorySize: defaultMemory, 134 DockerPort: defaultDockerPort, 135 BaseDriver: &drivers.BaseDriver{ 136 SSHPort: defaultSSHPort, 137 MachineName: hostName, 138 StorePath: storePath, 139 }, 140 } 141} 142 143func (d *Driver) GetSSHHostname() (string, error) { 144 return d.GetIP() 145} 146 147// DriverName returns the name of the driver 148func (d *Driver) DriverName() string { 149 return "vmwarevcloudair" 150} 151 152func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { 153 154 d.UserName = flags.String("vmwarevcloudair-username") 155 d.UserPassword = flags.String("vmwarevcloudair-password") 156 d.VDCID = flags.String("vmwarevcloudair-vdcid") 157 d.PublicIP = flags.String("vmwarevcloudair-publicip") 158 d.SetSwarmConfigFromFlags(flags) 159 160 // Check for required Params 161 if d.UserName == "" || d.UserPassword == "" || d.VDCID == "" || d.PublicIP == "" { 162 return fmt.Errorf("Please specify vcloudair mandatory params using options: -vmwarevcloudair-username -vmwarevcloudair-password -vmwarevcloudair-vdcid and -vmwarevcloudair-publicip") 163 } 164 165 // If ComputeID is not set we're using a VPC, hence setting ComputeID = VDCID 166 if flags.String("vmwarevcloudair-computeid") == "" { 167 d.ComputeID = flags.String("vmwarevcloudair-vdcid") 168 } else { 169 d.ComputeID = flags.String("vmwarevcloudair-computeid") 170 } 171 172 // If the Org VDC Network is empty, set it to the default routed network. 173 if flags.String("vmwarevcloudair-orgvdcnetwork") == "" { 174 d.OrgVDCNet = flags.String("vmwarevcloudair-vdcid") + "-default-routed" 175 } else { 176 d.OrgVDCNet = flags.String("vmwarevcloudair-orgvdcnetwork") 177 } 178 179 // If the Edge Gateway is empty, just set it to the default edge gateway. 180 if flags.String("vmwarevcloudair-edgegateway") == "" { 181 d.EdgeGateway = flags.String("vmwarevcloudair-vdcid") 182 } else { 183 d.EdgeGateway = flags.String("vmwarevcloudair-edgegateway") 184 } 185 186 d.Catalog = flags.String("vmwarevcloudair-catalog") 187 d.CatalogItem = flags.String("vmwarevcloudair-catalogitem") 188 189 d.DockerPort = flags.Int("vmwarevcloudair-docker-port") 190 d.SSHUser = "root" 191 d.SSHPort = flags.Int("vmwarevcloudair-ssh-port") 192 d.CPUCount = flags.Int("vmwarevcloudair-cpu-count") 193 d.MemorySize = flags.Int("vmwarevcloudair-memory-size") 194 195 return nil 196} 197 198func (d *Driver) GetURL() (string, error) { 199 if err := drivers.MustBeRunning(d); err != nil { 200 return "", err 201 } 202 203 return fmt.Sprintf("tcp://%s", net.JoinHostPort(d.PublicIP, strconv.Itoa(d.DockerPort))), nil 204} 205 206func (d *Driver) GetIP() (string, error) { 207 return d.PublicIP, nil 208} 209 210func (d *Driver) GetState() (state.State, error) { 211 p, err := govcloudair.NewClient() 212 if err != nil { 213 return state.Error, err 214 } 215 216 log.Debug("Connecting to vCloud Air to fetch vApp Status...") 217 // Authenticate to vCloud Air 218 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 219 if err != nil { 220 return state.Error, err 221 } 222 223 vapp, err := v.FindVAppByID(d.VAppID) 224 if err != nil { 225 return state.Error, err 226 } 227 228 status, err := vapp.GetStatus() 229 if err != nil { 230 return state.Error, err 231 } 232 233 if err = p.Disconnect(); err != nil { 234 return state.Error, err 235 } 236 237 switch status { 238 case "POWERED_ON": 239 return state.Running, nil 240 case "POWERED_OFF": 241 return state.Stopped, nil 242 } 243 return state.None, nil 244} 245 246func (d *Driver) Create() error { 247 key, err := d.createSSHKey() 248 if err != nil { 249 return err 250 } 251 252 p, err := govcloudair.NewClient() 253 if err != nil { 254 return err 255 } 256 257 log.Infof("Connecting to vCloud Air...") 258 // Authenticate to vCloud Air 259 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 260 if err != nil { 261 return err 262 } 263 264 // Find VDC Network 265 net, err := v.FindVDCNetwork(d.OrgVDCNet) 266 if err != nil { 267 return err 268 } 269 270 // Find our Edge Gateway 271 edge, err := v.FindEdgeGateway(d.EdgeGateway) 272 if err != nil { 273 return err 274 } 275 276 // Get the Org our VDC belongs to 277 org, err := v.GetVDCOrg() 278 if err != nil { 279 return err 280 } 281 282 // Find our Catalog 283 cat, err := org.FindCatalog(d.Catalog) 284 if err != nil { 285 return err 286 } 287 288 // Find our Catalog Item 289 cati, err := cat.FindCatalogItem(d.CatalogItem) 290 if err != nil { 291 return err 292 } 293 294 // Fetch the vApp Template in the Catalog Item 295 vapptemplate, err := cati.GetVAppTemplate() 296 if err != nil { 297 return err 298 } 299 300 // Create a new empty vApp 301 vapp := govcloudair.NewVApp(p) 302 303 log.Infof("Creating a new vApp: %s...", d.MachineName) 304 // Compose the vApp with ComposeVApp 305 task, err := vapp.ComposeVApp(net, vapptemplate, d.MachineName, "Container Host created with Docker Host") 306 if err != nil { 307 return err 308 } 309 310 // Wait for the creation to be completed 311 if err = task.WaitTaskCompletion(); err != nil { 312 return err 313 } 314 315 task, err = vapp.ChangeCPUcount(d.CPUCount) 316 if err != nil { 317 return err 318 } 319 320 if err = task.WaitTaskCompletion(); err != nil { 321 return err 322 } 323 324 task, err = vapp.ChangeMemorySize(d.MemorySize) 325 if err != nil { 326 return err 327 } 328 329 if err = task.WaitTaskCompletion(); err != nil { 330 return err 331 } 332 333 sshCustomScript := "echo \"" + strings.TrimSpace(key) + "\" > /root/.ssh/authorized_keys" 334 335 task, err = vapp.RunCustomizationScript(d.MachineName, sshCustomScript) 336 if err != nil { 337 return err 338 } 339 340 if err = task.WaitTaskCompletion(); err != nil { 341 return err 342 } 343 344 task, err = vapp.PowerOn() 345 if err != nil { 346 return err 347 } 348 349 log.Infof("Waiting for the VM to power on and run the customization script...") 350 351 if err = task.WaitTaskCompletion(); err != nil { 352 return err 353 } 354 355 log.Infof("Creating NAT and Firewall Rules on %s...", d.EdgeGateway) 356 task, err = edge.Create1to1Mapping(vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress, d.PublicIP, d.MachineName) 357 if err != nil { 358 return err 359 } 360 361 if err = task.WaitTaskCompletion(); err != nil { 362 return err 363 } 364 365 log.Debugf("Disconnecting from vCloud Air...") 366 367 if err = p.Disconnect(); err != nil { 368 return err 369 } 370 371 // Set VAppID with ID of the created VApp 372 d.VAppID = vapp.VApp.ID 373 374 d.IPAddress, err = d.GetIP() 375 return err 376} 377 378func (d *Driver) Remove() error { 379 p, err := govcloudair.NewClient() 380 if err != nil { 381 return err 382 } 383 384 log.Infof("Connecting to vCloud Air...") 385 // Authenticate to vCloud Air 386 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 387 if err != nil { 388 return err 389 } 390 391 // Find our Edge Gateway 392 edge, err := v.FindEdgeGateway(d.EdgeGateway) 393 if err != nil { 394 return err 395 } 396 397 vapp, err := v.FindVAppByID(d.VAppID) 398 if err != nil { 399 log.Infof("Can't find the vApp, assuming it was deleted already...") 400 return nil 401 } 402 403 status, err := vapp.GetStatus() 404 if err != nil { 405 return err 406 } 407 408 log.Infof("Removing NAT and Firewall Rules on %s...", d.EdgeGateway) 409 task, err := edge.Remove1to1Mapping(vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress, d.PublicIP) 410 if err != nil { 411 return err 412 } 413 if err = task.WaitTaskCompletion(); err != nil { 414 return err 415 } 416 417 if status == "POWERED_ON" { 418 // If it's powered on, power it off before deleting 419 log.Infof("Powering Off %s...", d.MachineName) 420 task, err = vapp.PowerOff() 421 if err != nil { 422 return err 423 } 424 if err = task.WaitTaskCompletion(); err != nil { 425 return err 426 } 427 428 } 429 430 log.Debugf("Undeploying %s...", d.MachineName) 431 task, err = vapp.Undeploy() 432 if err != nil { 433 return err 434 } 435 if err = task.WaitTaskCompletion(); err != nil { 436 return err 437 } 438 439 log.Infof("Deleting %s...", d.MachineName) 440 task, err = vapp.Delete() 441 if err != nil { 442 return err 443 } 444 if err = task.WaitTaskCompletion(); err != nil { 445 return err 446 } 447 448 err = p.Disconnect() 449 return err 450} 451 452func (d *Driver) Start() error { 453 p, err := govcloudair.NewClient() 454 if err != nil { 455 return err 456 } 457 458 log.Infof("Connecting to vCloud Air...") 459 // Authenticate to vCloud Air 460 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 461 if err != nil { 462 return err 463 } 464 465 vapp, err := v.FindVAppByID(d.VAppID) 466 if err != nil { 467 return err 468 } 469 470 status, err := vapp.GetStatus() 471 if err != nil { 472 return err 473 } 474 475 if status == "POWERED_OFF" { 476 log.Infof("Starting %s...", d.MachineName) 477 task, err := vapp.PowerOn() 478 if err != nil { 479 return err 480 } 481 if err = task.WaitTaskCompletion(); err != nil { 482 return err 483 } 484 485 } 486 487 if err = p.Disconnect(); err != nil { 488 return err 489 } 490 491 d.IPAddress, err = d.GetIP() 492 return err 493} 494 495func (d *Driver) Stop() error { 496 p, err := govcloudair.NewClient() 497 if err != nil { 498 return err 499 } 500 501 log.Infof("Connecting to vCloud Air...") 502 // Authenticate to vCloud Air 503 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 504 if err != nil { 505 return err 506 } 507 508 vapp, err := v.FindVAppByID(d.VAppID) 509 if err != nil { 510 return err 511 } 512 513 task, err := vapp.Shutdown() 514 if err != nil { 515 return err 516 } 517 if err = task.WaitTaskCompletion(); err != nil { 518 return err 519 } 520 521 if err = p.Disconnect(); err != nil { 522 return err 523 } 524 525 d.IPAddress = "" 526 527 return nil 528} 529 530func (d *Driver) Restart() error { 531 p, err := govcloudair.NewClient() 532 if err != nil { 533 return err 534 } 535 536 log.Infof("Connecting to vCloud Air...") 537 // Authenticate to vCloud Air 538 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 539 if err != nil { 540 return err 541 } 542 543 vapp, err := v.FindVAppByID(d.VAppID) 544 if err != nil { 545 return err 546 } 547 548 task, err := vapp.Reset() 549 if err != nil { 550 return err 551 } 552 if err = task.WaitTaskCompletion(); err != nil { 553 return err 554 } 555 556 if err = p.Disconnect(); err != nil { 557 return err 558 } 559 560 d.IPAddress, err = d.GetIP() 561 return err 562} 563 564func (d *Driver) Kill() error { 565 p, err := govcloudair.NewClient() 566 if err != nil { 567 return err 568 } 569 570 log.Infof("Connecting to vCloud Air...") 571 // Authenticate to vCloud Air 572 v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID) 573 if err != nil { 574 return err 575 } 576 577 vapp, err := v.FindVAppByID(d.VAppID) 578 if err != nil { 579 return err 580 } 581 582 task, err := vapp.PowerOff() 583 if err != nil { 584 return err 585 } 586 if err = task.WaitTaskCompletion(); err != nil { 587 return err 588 } 589 590 if err = p.Disconnect(); err != nil { 591 return err 592 } 593 594 d.IPAddress = "" 595 596 return nil 597} 598 599// Helpers 600 601func generateVMName() string { 602 randomID := mcnutils.TruncateID(mcnutils.GenerateRandomID()) 603 return fmt.Sprintf("docker-host-%s", randomID) 604} 605 606func (d *Driver) createSSHKey() (string, error) { 607 if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { 608 return "", err 609 } 610 611 publicKey, err := ioutil.ReadFile(d.publicSSHKeyPath()) 612 if err != nil { 613 return "", err 614 } 615 616 return string(publicKey), nil 617} 618 619func (d *Driver) publicSSHKeyPath() string { 620 return d.GetSSHKeyPath() + ".pub" 621} 622