github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/juju/addmachine.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 9 "github.com/juju/names" 10 "launchpad.net/gnuflag" 11 12 "github.com/juju/juju/cmd" 13 "github.com/juju/juju/cmd/envcmd" 14 "github.com/juju/juju/constraints" 15 "github.com/juju/juju/environs/manual" 16 "github.com/juju/juju/instance" 17 "github.com/juju/juju/juju" 18 "github.com/juju/juju/state/api/params" 19 ) 20 21 // sshHostPrefix is the prefix for a machine to be "manually provisioned". 22 const sshHostPrefix = "ssh:" 23 24 var addMachineDoc = ` 25 26 If no container is specified, a new machine will be 27 provisioned. If a container is specified, a new machine will be provisioned 28 with that container. 29 30 To add a container to an existing machine, use the <container>:<machinenumber> 31 format. 32 33 When adding a new machine, you may specify constraints for the machine to be 34 provisioned. Constraints cannot be combined with deploying a container to an 35 existing machine. 36 37 Currently, the only supported container type is lxc. 38 39 Machines are created in a clean state and ready to have units deployed. 40 41 This command also supports manual provisioning of existing machines via SSH. The 42 target machine must be able to communicate with the API server, and be able to 43 access the environment storage. 44 45 Examples: 46 juju add-machine (starts a new machine) 47 juju add-machine lxc (starts a new machine with an lxc container) 48 juju add-machine lxc:4 (starts a new lxc container on machine 4) 49 juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM) 50 juju add-machine ssh:user@10.10.0.3 (manually provisions a machine with ssh) 51 52 See Also: 53 juju help constraints 54 ` 55 56 // AddMachineCommand starts a new machine and registers it in the environment. 57 type AddMachineCommand struct { 58 envcmd.EnvCommandBase 59 // If specified, use this series, else use the environment default-series 60 Series string 61 // If specified, these constraints are merged with those already in the environment. 62 Constraints constraints.Value 63 // Placement is passed verbatim to the API, to be parsed and evaluated server-side. 64 Placement *instance.Placement 65 } 66 67 func (c *AddMachineCommand) Info() *cmd.Info { 68 return &cmd.Info{ 69 Name: "add-machine", 70 Args: "[<container>:machine | <container> | ssh:[user@]host]", 71 Purpose: "start a new, empty machine and optionally a container, or add a container to a machine", 72 Doc: addMachineDoc, 73 } 74 } 75 76 func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) { 77 f.StringVar(&c.Series, "series", "", "the charm series") 78 f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "additional machine constraints") 79 } 80 81 func (c *AddMachineCommand) Init(args []string) error { 82 if c.Constraints.Container != nil { 83 return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container) 84 } 85 placement, err := cmd.ZeroOrOneArgs(args) 86 if err != nil { 87 return err 88 } 89 c.Placement, err = instance.ParsePlacement(placement) 90 if err == instance.ErrPlacementScopeMissing { 91 placement = c.EnvName + ":" + placement 92 c.Placement, err = instance.ParsePlacement(placement) 93 } 94 if err != nil { 95 return err 96 } 97 return nil 98 } 99 100 func (c *AddMachineCommand) Run(ctx *cmd.Context) error { 101 if c.Placement != nil && c.Placement.Scope == "ssh" { 102 args := manual.ProvisionMachineArgs{ 103 Host: c.Placement.Directive, 104 EnvName: c.EnvName, 105 Stdin: ctx.Stdin, 106 Stdout: ctx.Stdout, 107 Stderr: ctx.Stderr, 108 } 109 _, err := manual.ProvisionMachine(args) 110 return err 111 } 112 113 client, err := juju.NewAPIClientFromName(c.EnvName) 114 if err != nil { 115 return err 116 } 117 defer client.Close() 118 119 if c.Placement != nil && c.Placement.Scope == instance.MachineScope { 120 // It does not make sense to add-machine <id>. 121 return fmt.Errorf("machine-id cannot be specified when adding machines") 122 } 123 124 machineParams := params.AddMachineParams{ 125 Placement: c.Placement, 126 Series: c.Series, 127 Constraints: c.Constraints, 128 Jobs: []params.MachineJob{params.JobHostUnits}, 129 } 130 results, err := client.AddMachines([]params.AddMachineParams{machineParams}) 131 if params.IsCodeNotImplemented(err) { 132 if c.Placement != nil { 133 containerType, parseErr := instance.ParseContainerType(c.Placement.Scope) 134 if parseErr != nil { 135 // The user specified a non-container placement directive: 136 // return original API not implemented error. 137 return err 138 } 139 machineParams.ContainerType = containerType 140 machineParams.ParentId = c.Placement.Directive 141 machineParams.Placement = nil 142 } 143 logger.Infof( 144 "AddMachinesWithPlacement not supported by the API server, " + 145 "falling back to 1.18 compatibility mode", 146 ) 147 results, err = client.AddMachines1dot18([]params.AddMachineParams{machineParams}) 148 } 149 if err != nil { 150 return err 151 } 152 153 // Currently, only one machine is added, but in future there may be several added in one call. 154 machineInfo := results[0] 155 if machineInfo.Error != nil { 156 return machineInfo.Error 157 } 158 machineId := machineInfo.Machine 159 160 if names.IsContainerMachine(machineId) { 161 ctx.Infof("created container %v", machineId) 162 } else { 163 ctx.Infof("created machine %v", machineId) 164 } 165 return nil 166 }