github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/storage/add.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cmd/modelcmd" 16 "github.com/juju/juju/storage" 17 ) 18 19 // NewAddCommand returns a command used to add unit storage. 20 func NewAddCommand() cmd.Command { 21 cmd := &addCommand{} 22 cmd.newAPIFunc = func() (StorageAddAPI, error) { 23 return cmd.NewStorageAPI() 24 } 25 return modelcmd.Wrap(cmd) 26 } 27 28 const ( 29 addCommandDoc = ` 30 Add storage instances to a unit dynamically using provided storage directives. 31 Specify a unit and a storage specification in the same format 32 as passed to juju deploy --storage=”...”. 33 34 A storage directive consists of a storage name as per charm specification 35 and storage constraints, e.g. pool, count, size. 36 37 The acceptable format for storage constraints is a comma separated 38 sequence of: POOL, COUNT, and SIZE, where 39 40 POOL identifies the storage pool. POOL can be a string 41 starting with a letter, followed by zero or more digits 42 or letters optionally separated by hyphens. 43 44 COUNT is a positive integer indicating how many instances 45 of the storage to create. If unspecified, and SIZE is 46 specified, COUNT defaults to 1. 47 48 SIZE describes the minimum size of the storage instances to 49 create. SIZE is a floating point number and multiplier from 50 the set (M, G, T, P, E, Z, Y), which are all treated as 51 powers of 1024. 52 53 Storage constraints can be optionally ommitted. 54 Model default values will be used for all ommitted constraint values. 55 There is no need to comma-separate ommitted constraints. 56 57 Example: 58 Add 3 ebs storage instances for "data" storage to unit u/0: 59 60 juju add-storage u/0 data=ebs,1024,3 61 or 62 juju add-storage u/0 data=ebs,3 63 or 64 juju add-storage u/0 data=ebs,,3 65 66 67 Add 1 storage instances for "data" storage to unit u/0 68 using default model provider pool: 69 70 juju add-storage u/0 data=1 71 or 72 juju add-storage u/0 data 73 ` 74 addCommandAgs = ` 75 <unit name> <storage directive> ... 76 where storage directive is 77 <charm storage name>=<storage constraints> 78 or 79 <charm storage name> 80 ` 81 ) 82 83 // addCommand adds unit storage instances dynamically. 84 type addCommand struct { 85 StorageCommandBase 86 unitTag string 87 88 // storageCons is a map of storage constraints, keyed on the storage name 89 // defined in charm storage metadata. 90 storageCons map[string]storage.Constraints 91 newAPIFunc func() (StorageAddAPI, error) 92 } 93 94 // Init implements Command.Init. 95 func (c *addCommand) Init(args []string) (err error) { 96 if len(args) < 2 { 97 return errors.New("add-storage requires a unit and a storage directive") 98 } 99 100 u := args[0] 101 if !names.IsValidUnit(u) { 102 return errors.NotValidf("unit name %q", u) 103 } 104 c.unitTag = names.NewUnitTag(u).String() 105 106 c.storageCons, err = storage.ParseConstraintsMap(args[1:], false) 107 return 108 } 109 110 // Info implements Command.Info. 111 func (c *addCommand) Info() *cmd.Info { 112 return &cmd.Info{ 113 Name: "add-storage", 114 Purpose: "adds unit storage dynamically", 115 Doc: addCommandDoc, 116 Args: addCommandAgs, 117 } 118 } 119 120 // Run implements Command.Run. 121 func (c *addCommand) Run(ctx *cmd.Context) (err error) { 122 api, err := c.newAPIFunc() 123 if err != nil { 124 return err 125 } 126 defer api.Close() 127 128 storages := c.createStorageAddParams() 129 results, err := api.AddToUnit(storages) 130 if err != nil { 131 return err 132 } 133 // If there are any failures, display them first. 134 // Then display all added storage. 135 // If there are no failures, then there is no need to display all successes. 136 var added []string 137 138 for i, one := range results { 139 us := storages[i] 140 if one.Error != nil { 141 fmt.Fprintf(ctx.Stderr, fail+": %v\n", us.StorageName, one.Error) 142 continue 143 } 144 added = append(added, fmt.Sprintf(success, us.StorageName)) 145 } 146 if len(added) < len(storages) { 147 fmt.Fprintf(ctx.Stderr, strings.Join(added, "\n")) 148 } 149 return nil 150 } 151 152 var ( 153 storageName = "storage %q" 154 success = "success: " + storageName 155 fail = "fail: " + storageName 156 ) 157 158 // StorageAddAPI defines the API methods that the storage commands use. 159 type StorageAddAPI interface { 160 Close() error 161 AddToUnit(storages []params.StorageAddParams) ([]params.ErrorResult, error) 162 } 163 164 func (c *addCommand) createStorageAddParams() []params.StorageAddParams { 165 all := make([]params.StorageAddParams, 0, len(c.storageCons)) 166 for one, cons := range c.storageCons { 167 all = append(all, 168 params.StorageAddParams{ 169 UnitTag: c.unitTag, 170 StorageName: one, 171 Constraints: params.StorageConstraints{ 172 cons.Pool, 173 &cons.Size, 174 &cons.Count, 175 }, 176 }) 177 } 178 return all 179 }