github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "strings" 9 10 "launchpad.net/gnuflag" 11 12 "launchpad.net/juju-core/cmd" 13 "launchpad.net/juju-core/constraints" 14 "launchpad.net/juju-core/environs/manual" 15 "launchpad.net/juju-core/instance" 16 "launchpad.net/juju-core/juju" 17 "launchpad.net/juju-core/names" 18 "launchpad.net/juju-core/state" 19 "launchpad.net/juju-core/state/api/params" 20 ) 21 22 // sshHostPrefix is the prefix for a machine to be "manually provisioned". 23 const sshHostPrefix = "ssh:" 24 25 var addMachineDoc = ` 26 27 If no container is specified, a new machine will be 28 provisioned. If a container is specified, a new machine will be provisioned 29 with that container. 30 31 To add a container to an existing machine, use the <container>:<machinenumber> 32 format. 33 34 When adding a new machine, you may specify constraints for the machine to be 35 provisioned. Constraints cannot be combined with deploying a container to an 36 existing machine. 37 38 Currently, the only supported container type is lxc. 39 40 Machines are created in a clean state and ready to have units deployed. 41 42 This command also supports manual provisioning of existing machines via SSH. The 43 target machine must be able to communicate with the API server, and be able to 44 access the environment storage. 45 46 Examples: 47 juju add-machine (starts a new machine) 48 juju add-machine lxc (starts a new machine with an lxc container) 49 juju add-machine lxc:4 (starts a new lxc container on machine 4) 50 juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM) 51 juju add-machine ssh:user@10.10.0.3 (manually provisions a machine with ssh) 52 53 See Also: 54 juju help constraints 55 ` 56 57 // AddMachineCommand starts a new machine and registers it in the environment. 58 type AddMachineCommand struct { 59 cmd.EnvCommandBase 60 // If specified, use this series, else use the environment default-series 61 Series string 62 // If specified, these constraints are merged with those already in the environment. 63 Constraints constraints.Value 64 MachineId string 65 ContainerType instance.ContainerType 66 SSHHost string 67 } 68 69 func (c *AddMachineCommand) Info() *cmd.Info { 70 return &cmd.Info{ 71 Name: "add-machine", 72 Args: "[<container>:machine | <container> | ssh:[user@]host]", 73 Purpose: "start a new, empty machine and optionally a container, or add a container to a machine", 74 Doc: addMachineDoc, 75 } 76 } 77 78 func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) { 79 c.EnvCommandBase.SetFlags(f) 80 f.StringVar(&c.Series, "series", "", "the charm series") 81 f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "additional machine constraints") 82 } 83 84 func (c *AddMachineCommand) Init(args []string) error { 85 if c.Constraints.Container != nil { 86 return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container) 87 } 88 containerSpec, err := cmd.ZeroOrOneArgs(args) 89 if err != nil { 90 return err 91 } 92 if containerSpec == "" { 93 return nil 94 } 95 if strings.HasPrefix(containerSpec, sshHostPrefix) { 96 c.SSHHost = containerSpec[len(sshHostPrefix):] 97 } else { 98 // container arg can either be 'type:machine' or 'type' 99 if c.ContainerType, err = instance.ParseContainerType(containerSpec); err != nil { 100 if names.IsMachine(containerSpec) || !cmd.IsMachineOrNewContainer(containerSpec) { 101 return fmt.Errorf("malformed container argument %q", containerSpec) 102 } 103 sep := strings.Index(containerSpec, ":") 104 c.MachineId = containerSpec[sep+1:] 105 c.ContainerType, err = instance.ParseContainerType(containerSpec[:sep]) 106 } 107 } 108 return err 109 } 110 111 // addMachine1dot16 runs Client.AddMachines using a direct DB connection to maintain 112 // compatibility with an API server running 1.16 or older (when AddMachines 113 // was not available). This fallback can be removed when we no longer maintain 114 // 1.16 compatibility. 115 // This was copied directly from the code in AddMachineCommand.Run in 1.16 116 func (c *AddMachineCommand) addMachine1dot16() (string, error) { 117 conn, err := juju.NewConnFromName(c.EnvName) 118 if err != nil { 119 return "", err 120 } 121 defer conn.Close() 122 123 series := c.Series 124 if series == "" { 125 conf, err := conn.State.EnvironConfig() 126 if err != nil { 127 return "", err 128 } 129 series = conf.DefaultSeries() 130 } 131 template := state.MachineTemplate{ 132 Series: series, 133 Constraints: c.Constraints, 134 Jobs: []state.MachineJob{state.JobHostUnits}, 135 } 136 var m *state.Machine 137 switch { 138 case c.ContainerType == "": 139 m, err = conn.State.AddOneMachine(template) 140 case c.MachineId != "": 141 m, err = conn.State.AddMachineInsideMachine(template, c.MachineId, c.ContainerType) 142 default: 143 m, err = conn.State.AddMachineInsideNewMachine(template, template, c.ContainerType) 144 } 145 if err != nil { 146 return "", err 147 } 148 return m.String(), err 149 } 150 151 func (c *AddMachineCommand) Run(ctx *cmd.Context) error { 152 if c.SSHHost != "" { 153 args := manual.ProvisionMachineArgs{ 154 Host: c.SSHHost, 155 EnvName: c.EnvName, 156 Stdin: ctx.Stdin, 157 Stdout: ctx.Stdout, 158 Stderr: ctx.Stderr, 159 } 160 _, err := manual.ProvisionMachine(args) 161 return err 162 } 163 164 client, err := juju.NewAPIClientFromName(c.EnvName) 165 if err != nil { 166 return err 167 } 168 defer client.Close() 169 170 machineParams := params.AddMachineParams{ 171 ParentId: c.MachineId, 172 ContainerType: c.ContainerType, 173 Series: c.Series, 174 Constraints: c.Constraints, 175 Jobs: []params.MachineJob{params.JobHostUnits}, 176 } 177 results, err := client.AddMachines([]params.AddMachineParams{machineParams}) 178 var machineId string 179 if params.IsCodeNotImplemented(err) { 180 logger.Infof("AddMachines not supported by the API server, " + 181 "falling back to 1.16 compatibility mode (direct DB access)") 182 machineId, err = c.addMachine1dot16() 183 } else if err != nil { 184 return err 185 } else { 186 // Currently, only one machine is added, but in future there may be several added in one call. 187 machineInfo := results[0] 188 var machineErr *params.Error 189 machineId, machineErr = machineInfo.Machine, machineInfo.Error 190 if machineErr != nil { 191 err = machineErr 192 } 193 } 194 if err != nil { 195 return err 196 } 197 if c.ContainerType == "" { 198 logger.Infof("created machine %v", machineId) 199 } else { 200 logger.Infof("created %q container on machine %v", c.ContainerType, machineId) 201 } 202 return nil 203 }