Merge lp:~dimitern/juju-core/382-api-provisioner-machine-nics into lp:~go-bot/juju-core/trunk
- 382-api-provisioner-machine-nics
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Dimiter Naydenov |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2569 |
Proposed branch: | lp:~dimitern/juju-core/382-api-provisioner-machine-nics |
Merge into: | lp:~go-bot/juju-core/trunk |
Prerequisite: | lp:~dimitern/juju-core/381-state-machine-nics |
Diff against target: |
444 lines (+342/-4) 7 files modified
state/api/params/internal.go (+26/-0) state/api/provisioner/machine.go (+28/-0) state/api/provisioner/provisioner.go (+20/-0) state/api/provisioner/provisioner_test.go (+94/-2) state/apiserver/provisioner/provisioner.go (+36/-0) state/apiserver/provisioner/provisioner_test.go (+134/-2) state/apiserver/testing/errors.go (+4/-0) |
To merge this branch: | bzr merge lp:~dimitern/juju-core/382-api-provisioner-machine-nics |
Related bugs: | |
Related blueprints: |
Support MaaS VLANs in Juju
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+214272@code.launchpad.net |
Commit message
api/provisioner: Allow adding networks and NICs
This adds two new Provisioner API calls:
* AddNetwork (exposed as st.AddNetworks bulk call on
the client-side provisioner API)
* AddNetworkInterface (exposed in the client-side API
as machine.
These will be used to set the networks/interfaces that
will be configured by MAAS at provisioning time.
Next, we'll make the necessary changes to StartInstance()
in general (and in provider/maas specifically) to return
what networks will the machine start with.
https:/
R=gz
Description of the change
api/provisioner: Allow adding networks and NICs
This adds two new Provisioner API calls:
* AddNetwork (exposed as st.AddNetworks bulk call on
the client-side provisioner API)
* AddNetworkInterface (exposed in the client-side API
as machine.
These will be used to set the networks/interfaces that
will be configured by MAAS at provisioning time.
Next, we'll make the necessary changes to StartInstance()
in general (and in provider/maas specifically) to return
what networks will the machine start with.
Dimiter Naydenov (dimitern) wrote : | # |
Martin Packman (gz) wrote : | # |
LGTM. I am a little panicked around future-proofing though, I think
these api structs will want to look a little different, or maybe quite a
bit different, when we add other clouds and better networking support.
We seem to be reserving some pretty generic API names 'AddNetworks" and
will probably want a number of revisions. We may get to several -Ex
suffixes or something.
https:/
File state/api/
https:/
state/api/
This will probably be a non-name id for clouds other than MAAS.
Shouldn't matter apart from naming.
https:/
File state/api/
https:/
state/api/
m.tag {
Why the conditional?
https:/
File state/api/
https:/
state/api/
gc.Equals, expectParams.Name)
Can use c.Check for these two after the err Assert.
https:/
state/api/
error is reported.
This feels like a seperate testcase really, the current test is > one
screen high which is a bit of a personal bugbear of mine. Sharing setup
steps is not that hard.
https:/
File state/apiserver
https:/
state/apiserver
c.Assert(
Can use c.Check, and below.
https:/
state/apiserver
with a non-environment
This feels like a seperate testcase.
https:/
state/apiserver
*withoutStateSe
Giant complicated test again...
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
https:/
File [revision details] (right):
https:/
[revision details]:1: Old revision:
<email address hidden>
> LGTM. I am a little panicked around future-proofing though, I
> think these api structs will want to look a little different,
> or maybe quite a bit different, when we add other clouds and
> better networking support. We seem to be reserving some
> pretty generic API names 'AddNetworks" and will probably want
> a number of revisions. We may get to several -Ex suffixes or
> something.
Structs can evolve and we can phase-out API calls and add new ones with
extended functionality. This is a general problem with the API w/o
versioning.
https:/
File state/api/
https:/
state/api/
On 2014/04/04 15:40:02, gz wrote:
> This will probably be a non-name id for clouds other than MAAS.
Shouldn't matter
> apart from naming.
We can add fields as needed later (and "name" can also be a UUID if
needed :)
https:/
File state/api/
https:/
state/api/
m.tag {
On 2014/04/04 15:40:02, gz wrote:
> Why the conditional?
To make sure we can only add NICs to the current machine (in case it's
empty or different). There are tests for that in state/api/
https:/
File state/api/
https:/
state/api/
gc.Equals, expectParams.Name)
On 2014/04/04 15:40:02, gz wrote:
> Can use c.Check for these two after the err Assert.
Done.
https:/
state/api/
error is reported.
On 2014/04/04 15:40:02, gz wrote:
> This feels like a seperate testcase really, the current test is > one
screen
> high which is a bit of a personal bugbear of mine. Sharing setup steps
is not
> that hard.
Moved to a separate test case.
https:/
File state/apiserver
https:/
state/apiserver
c.Assert(
On 2014/04/04 15:40:02, gz wrote:
> Can use c.Check, and below.
Done.
https:/
William Reade (fwereade) wrote : | # |
I'm a bit disappointed that we're making this so chatty, when ISTM that
we could pack all this into SetProvisioned, but... well, deadlines are
tight and internal APIs are much easier to change in future. Let it
stand, I guess :).
Preview Diff
1 | === modified file 'state/api/params/internal.go' |
2 | --- state/api/params/internal.go 2014-03-31 11:59:16 +0000 |
3 | +++ state/api/params/internal.go 2014-04-04 16:33:28 +0000 |
4 | @@ -378,6 +378,32 @@ |
5 | Results []NetworkResult |
6 | } |
7 | |
8 | +// NetworkParams holds a single network definition. |
9 | +type NetworkParams struct { |
10 | + Name string |
11 | + CIDR string |
12 | + VLANTag int |
13 | +} |
14 | + |
15 | +// AddNetworkParams holds the parameters for making an AddNetwork call. |
16 | +type AddNetworkParams struct { |
17 | + Networks []NetworkParams |
18 | +} |
19 | + |
20 | +// NetworkInterfaceParams holds a single network interface definition. |
21 | +type NetworkInterfaceParams struct { |
22 | + MACAddress string |
23 | + MachineTag string |
24 | + InterfaceName string |
25 | + NetworkName string |
26 | +} |
27 | + |
28 | +// AddNetworkInterfaceParams holds the parameters for making an |
29 | +// AddNetworkInterface call. |
30 | +type AddNetworkInterfaceParams struct { |
31 | + Interfaces []NetworkInterfaceParams |
32 | +} |
33 | + |
34 | // AgentGetEntitiesResults holds the results of a |
35 | // agent.API.GetEntities call. |
36 | type AgentGetEntitiesResults struct { |
37 | |
38 | === modified file 'state/api/provisioner/machine.go' |
39 | --- state/api/provisioner/machine.go 2014-04-04 09:08:42 +0000 |
40 | +++ state/api/provisioner/machine.go 2014-04-04 16:33:28 +0000 |
41 | @@ -74,6 +74,34 @@ |
42 | return result.IncludeNetworks, result.ExcludeNetworks, nil |
43 | } |
44 | |
45 | +// AddNetworkInterfaces creates one or more network interfaces each on |
46 | +// an existing network and bound to the machine, which must not be |
47 | +// provisioned yet. MachineTag inside interfaces params is always set |
48 | +// to the current machine's tag. If any operation fails, the first |
49 | +// error is returned. |
50 | +func (m *Machine) AddNetworkInterfaces(interfaces []params.NetworkInterfaceParams) error { |
51 | + var results params.ErrorResults |
52 | + for i, _ := range interfaces { |
53 | + if interfaces[i].MachineTag != m.tag { |
54 | + interfaces[i].MachineTag = m.tag |
55 | + } |
56 | + } |
57 | + args := params.AddNetworkInterfaceParams{Interfaces: interfaces} |
58 | + err := m.st.call("AddNetworkInterface", args, &results) |
59 | + if err != nil { |
60 | + return err |
61 | + } |
62 | + if n := len(results.Results); n != len(interfaces) { |
63 | + return fmt.Errorf("expected %d result(s), got %d", len(interfaces), n) |
64 | + } |
65 | + for _, result := range results.Results { |
66 | + if err := result.Error; err != nil { |
67 | + return err |
68 | + } |
69 | + } |
70 | + return nil |
71 | +} |
72 | + |
73 | // SetStatus sets the status of the machine. |
74 | func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { |
75 | var result params.ErrorResults |
76 | |
77 | === modified file 'state/api/provisioner/provisioner.go' |
78 | --- state/api/provisioner/provisioner.go 2014-03-26 03:15:41 +0000 |
79 | +++ state/api/provisioner/provisioner.go 2014-04-04 16:33:28 +0000 |
80 | @@ -143,3 +143,23 @@ |
81 | } |
82 | return machines, results.Results, nil |
83 | } |
84 | + |
85 | +// AddNetworks creates one or more networks with the given parameters. |
86 | +// If any operation fails, the first error is returned. |
87 | +func (st *State) AddNetworks(networks []params.NetworkParams) error { |
88 | + var results params.ErrorResults |
89 | + args := params.AddNetworkParams{Networks: networks} |
90 | + err := st.call("AddNetwork", args, &results) |
91 | + if err != nil { |
92 | + return err |
93 | + } |
94 | + if n := len(results.Results); n != len(networks) { |
95 | + return fmt.Errorf("expected %d result(s), got %d", len(networks), n) |
96 | + } |
97 | + for _, result := range results.Results { |
98 | + if err := result.Error; err != nil { |
99 | + return err |
100 | + } |
101 | + } |
102 | + return nil |
103 | +} |
104 | |
105 | === modified file 'state/api/provisioner/provisioner_test.go' |
106 | --- state/api/provisioner/provisioner_test.go 2014-04-04 09:08:42 +0000 |
107 | +++ state/api/provisioner/provisioner_test.go 2014-04-04 16:33:28 +0000 |
108 | @@ -13,6 +13,7 @@ |
109 | "launchpad.net/juju-core/errors" |
110 | "launchpad.net/juju-core/instance" |
111 | "launchpad.net/juju-core/juju/testing" |
112 | + "launchpad.net/juju-core/names" |
113 | "launchpad.net/juju-core/state" |
114 | "launchpad.net/juju-core/state/api" |
115 | "launchpad.net/juju-core/state/api/params" |
116 | @@ -316,8 +317,8 @@ |
117 | c.Assert(cons, gc.DeepEquals, constraints.Value{}) |
118 | } |
119 | |
120 | -func (s *provisionerSuite) TestNetworks(c *gc.C) { |
121 | - // Create a fresh machine with some networks. |
122 | +func (s *provisionerSuite) TestRequestedNetworks(c *gc.C) { |
123 | + // Create a fresh machine with some requested networks. |
124 | template := state.MachineTemplate{ |
125 | Series: "quantal", |
126 | Jobs: []state.MachineJob{state.JobHostUnits}, |
127 | @@ -343,6 +344,97 @@ |
128 | c.Assert(excludeNetworks, gc.HasLen, 0) |
129 | } |
130 | |
131 | +func (s *provisionerSuite) TestAddNetworks(c *gc.C) { |
132 | + args := []params.NetworkParams{ |
133 | + {Name: "vlan42", CIDR: "0.1.2.0/24", VLANTag: 42}, |
134 | + {Name: "net1", CIDR: "0.2.1.0/24", VLANTag: 0}, |
135 | + } |
136 | + err := s.provisioner.AddNetworks(args) |
137 | + c.Assert(err, gc.IsNil) |
138 | + |
139 | + assertNetwork := func(name string, expectParams params.NetworkParams) { |
140 | + net, err := s.State.Network(name) |
141 | + c.Assert(err, gc.IsNil) |
142 | + c.Check(net.CIDR(), gc.Equals, expectParams.CIDR) |
143 | + c.Check(net.Name(), gc.Equals, expectParams.Name) |
144 | + c.Check(net.VLANTag(), gc.Equals, expectParams.VLANTag) |
145 | + } |
146 | + assertNetwork("vlan42", args[0]) |
147 | + assertNetwork("net1", args[1]) |
148 | + |
149 | + // Test the first error is returned. |
150 | + args = []params.NetworkParams{ |
151 | + {Name: "net2", CIDR: "0.2.2.0/24", VLANTag: 0}, |
152 | + {Name: "", CIDR: "0.1.2.0/24", VLANTag: 0}, |
153 | + {Name: "net2", CIDR: "0.2.2.0/24", VLANTag: -1}, |
154 | + } |
155 | + err = s.provisioner.AddNetworks(args) |
156 | + c.Assert(err, gc.ErrorMatches, `cannot add network "": name must be not empty`) |
157 | + |
158 | + assertNetwork("net2", args[0]) |
159 | +} |
160 | + |
161 | +func (s *provisionerSuite) addMachineAndNetworks(c *gc.C) (*state.Machine, *provisioner.Machine) { |
162 | + err := s.provisioner.AddNetworks([]params.NetworkParams{ |
163 | + {Name: "vlan42", CIDR: "0.1.2.0/24", VLANTag: 42}, |
164 | + {Name: "net1", CIDR: "0.2.1.0/24", VLANTag: 0}, |
165 | + }) |
166 | + c.Assert(err, gc.IsNil) |
167 | + machine, err := s.State.AddMachine("quantal", state.JobHostUnits) |
168 | + c.Assert(err, gc.IsNil) |
169 | + apiMachine, err := s.provisioner.Machine(machine.Tag()) |
170 | + c.Assert(err, gc.IsNil) |
171 | + return machine, apiMachine |
172 | +} |
173 | + |
174 | +func (s *provisionerSuite) TestMachineAddNetworkInterfaces(c *gc.C) { |
175 | + machine, apiMachine := s.addMachineAndNetworks(c) |
176 | + |
177 | + args := []params.NetworkInterfaceParams{ |
178 | + {"aa:bb:cc:dd:ee:f0", machine.Tag(), "eth0", "net1"}, |
179 | + {"aa:bb:cc:dd:ee:f1", "", "eth0", "net1"}, // tag filled in when empty |
180 | + {"aa:bb:cc:dd:ee:f2", "machine-42", "eth2", "vlan42"}, // tag overwritten |
181 | + } |
182 | + err := apiMachine.AddNetworkInterfaces(args) |
183 | + c.Assert(err, gc.IsNil) |
184 | + |
185 | + // Check the interfaces are there. |
186 | + ifaces, err := machine.NetworkInterfaces() |
187 | + c.Assert(err, gc.IsNil) |
188 | + c.Assert(ifaces, gc.HasLen, len(args)) |
189 | + actual := make([]params.NetworkInterfaceParams, len(args)) |
190 | + for i, iface := range ifaces { |
191 | + actual[i] = params.NetworkInterfaceParams{ |
192 | + MACAddress: iface.MACAddress(), |
193 | + MachineTag: names.MachineTag(iface.MachineId()), |
194 | + InterfaceName: iface.InterfaceName(), |
195 | + NetworkName: iface.NetworkName(), |
196 | + } |
197 | + } |
198 | + c.Assert(actual, jc.SameContents, args) |
199 | +} |
200 | + |
201 | +func (s *provisionerSuite) TestMachineAddNetworkInterfacesReportsFirstError(c *gc.C) { |
202 | + machine, apiMachine := s.addMachineAndNetworks(c) |
203 | + |
204 | + // Ensure only the first error is reported. |
205 | + args := []params.NetworkInterfaceParams{ |
206 | + {"aa:bb:cc:dd:ee:f3", "", "eth3", "net1"}, |
207 | + {"aa:bb:cc:dd:ee:f4", "", "eth0", "missing"}, |
208 | + {"invalid", "", "eth42", "net1"}, |
209 | + } |
210 | + err := apiMachine.AddNetworkInterfaces(args) |
211 | + c.Assert(err, gc.ErrorMatches, `cannot add network interface to machine 1: network "missing" not found`) |
212 | + |
213 | + ifaces, err := machine.NetworkInterfaces() |
214 | + c.Assert(err, gc.IsNil) |
215 | + c.Assert(ifaces, gc.HasLen, 1) |
216 | + c.Assert(ifaces[0].MachineId(), gc.Equals, machine.Id()) |
217 | + c.Assert(ifaces[0].MACAddress(), gc.Equals, args[0].MACAddress) |
218 | + c.Assert(ifaces[0].InterfaceName(), gc.Equals, args[0].InterfaceName) |
219 | + c.Assert(ifaces[0].NetworkName(), gc.Equals, args[0].NetworkName) |
220 | +} |
221 | + |
222 | func (s *provisionerSuite) TestWatchContainers(c *gc.C) { |
223 | apiMachine, err := s.provisioner.Machine(s.machine.Tag()) |
224 | c.Assert(err, gc.IsNil) |
225 | |
226 | === modified file 'state/apiserver/provisioner/provisioner.go' |
227 | --- state/apiserver/provisioner/provisioner.go 2014-04-04 09:08:42 +0000 |
228 | +++ state/apiserver/provisioner/provisioner.go 2014-04-04 16:33:28 +0000 |
229 | @@ -432,6 +432,42 @@ |
230 | return result, nil |
231 | } |
232 | |
233 | +// AddNetwork creates one or more new networks with the given parameters. |
234 | +// Only the environment manager can add networks. |
235 | +func (p *ProvisionerAPI) AddNetwork(args params.AddNetworkParams) (params.ErrorResults, error) { |
236 | + result := params.ErrorResults{ |
237 | + Results: make([]params.ErrorResult, len(args.Networks)), |
238 | + } |
239 | + if !p.authorizer.AuthEnvironManager() { |
240 | + return result, common.ErrPerm |
241 | + } |
242 | + for i, arg := range args.Networks { |
243 | + _, err := p.st.AddNetwork(arg.Name, arg.CIDR, arg.VLANTag) |
244 | + result.Results[i].Error = common.ServerError(err) |
245 | + } |
246 | + return result, nil |
247 | +} |
248 | + |
249 | +// AddNetworkInterface creates one or more new network interfaces with |
250 | +// the given parameters. |
251 | +func (p *ProvisionerAPI) AddNetworkInterface(args params.AddNetworkInterfaceParams) (params.ErrorResults, error) { |
252 | + result := params.ErrorResults{ |
253 | + Results: make([]params.ErrorResult, len(args.Interfaces)), |
254 | + } |
255 | + canAccess, err := p.getAuthFunc() |
256 | + if err != nil { |
257 | + return result, err |
258 | + } |
259 | + for i, arg := range args.Interfaces { |
260 | + machine, err := p.getMachine(canAccess, arg.MachineTag) |
261 | + if err == nil { |
262 | + _, err = machine.AddNetworkInterface(arg.MACAddress, arg.InterfaceName, arg.NetworkName) |
263 | + } |
264 | + result.Results[i].Error = common.ServerError(err) |
265 | + } |
266 | + return result, nil |
267 | +} |
268 | + |
269 | // SetProvisioned sets the provider specific machine id, nonce and |
270 | // metadata for each given machine. Once set, the instance id cannot |
271 | // be changed. |
272 | |
273 | === modified file 'state/apiserver/provisioner/provisioner_test.go' |
274 | --- state/apiserver/provisioner/provisioner_test.go 2014-04-04 09:08:42 +0000 |
275 | +++ state/apiserver/provisioner/provisioner_test.go 2014-04-04 16:33:28 +0000 |
276 | @@ -15,6 +15,7 @@ |
277 | "launchpad.net/juju-core/instance" |
278 | "launchpad.net/juju-core/juju/osenv" |
279 | "launchpad.net/juju-core/juju/testing" |
280 | + "launchpad.net/juju-core/names" |
281 | "launchpad.net/juju-core/state" |
282 | "launchpad.net/juju-core/state/api/params" |
283 | "launchpad.net/juju-core/state/apiserver/common" |
284 | @@ -758,8 +759,8 @@ |
285 | }) |
286 | } |
287 | |
288 | -func (s *withoutStateServerSuite) TestNetworks(c *gc.C) { |
289 | - // Add a machine with some networks. |
290 | +func (s *withoutStateServerSuite) TestRequestedNetworks(c *gc.C) { |
291 | + // Add a machine with some requested networks. |
292 | template := state.MachineTemplate{ |
293 | Series: "quantal", |
294 | Jobs: []state.MachineJob{state.JobHostUnits}, |
295 | @@ -798,6 +799,137 @@ |
296 | }) |
297 | } |
298 | |
299 | +func (s *withoutStateServerSuite) TestAddNetwork(c *gc.C) { |
300 | + args := params.AddNetworkParams{Networks: []params.NetworkParams{ |
301 | + {Name: "vlan42", CIDR: "0.1.2.0/24", VLANTag: 42}, |
302 | + {Name: "net1", CIDR: "0.2.1.0/24", VLANTag: 0}, |
303 | + {Name: "", CIDR: "0.1.3.0/24", VLANTag: 0}, |
304 | + {Name: "net2", CIDR: "invalid", VLANTag: 0}, |
305 | + {Name: "net1", CIDR: "0.1.4.0/24", VLANTag: 0}, |
306 | + {Name: "net2", CIDR: "0.1.5.0/24", VLANTag: -1}, |
307 | + }} |
308 | + result, err := s.provisioner.AddNetwork(args) |
309 | + c.Assert(err, gc.IsNil) |
310 | + prefix := "cannot add network %q: " |
311 | + c.Assert(result, jc.DeepEquals, params.ErrorResults{ |
312 | + Results: []params.ErrorResult{ |
313 | + {Error: nil}, |
314 | + {Error: nil}, |
315 | + {Error: apiservertesting.PrefixedError( |
316 | + fmt.Sprintf(prefix, ""), |
317 | + "name must be not empty")}, |
318 | + {Error: apiservertesting.PrefixedError( |
319 | + fmt.Sprintf(prefix, "net2"), |
320 | + "invalid CIDR address: invalid")}, |
321 | + {Error: apiservertesting.PrefixedError( |
322 | + fmt.Sprintf(prefix, "net1"), |
323 | + "already exists")}, |
324 | + {Error: apiservertesting.PrefixedError( |
325 | + fmt.Sprintf(prefix, "net2"), |
326 | + "invalid VLAN tag -1: must be between 0 and 4094")}, |
327 | + }}) |
328 | + |
329 | + // Check add networks are there and failed ones are not. |
330 | + net, err := s.State.Network("vlan42") |
331 | + c.Assert(err, gc.IsNil) |
332 | + c.Check(net.Name(), gc.Equals, "vlan42") |
333 | + c.Check(net.CIDR(), gc.Equals, "0.1.2.0/24") |
334 | + c.Check(net.VLANTag(), gc.Equals, 42) |
335 | + net, err = s.State.Network("net1") |
336 | + c.Assert(err, gc.IsNil) |
337 | + c.Check(net.Name(), gc.Equals, "net1") |
338 | + c.Check(net.CIDR(), gc.Equals, "0.2.1.0/24") |
339 | + c.Check(net.VLANTag(), gc.Equals, 0) |
340 | + _, err = s.State.Network("net2") |
341 | + c.Assert(err, jc.Satisfies, errors.IsNotFoundError) |
342 | +} |
343 | + |
344 | +func (s *withoutStateServerSuite) TestAddNetworkFailsWithNonEnvironManager(c *gc.C) { |
345 | + anAuthorizer := s.authorizer |
346 | + anAuthorizer.MachineAgent = true |
347 | + anAuthorizer.EnvironManager = false |
348 | + aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) |
349 | + c.Assert(err, gc.IsNil) |
350 | + c.Assert(aProvisioner, gc.NotNil) |
351 | + _, err = aProvisioner.AddNetwork(params.AddNetworkParams{}) |
352 | + c.Assert(err, gc.ErrorMatches, "permission denied") |
353 | +} |
354 | + |
355 | +func assertInterfaces(c *gc.C, machine *state.Machine, expect ...params.NetworkInterfaceParams) { |
356 | + ifaces, err := machine.NetworkInterfaces() |
357 | + c.Assert(err, gc.IsNil) |
358 | + c.Assert(ifaces, gc.HasLen, len(expect)) |
359 | + actual := make([]params.NetworkInterfaceParams, len(expect)) |
360 | + for i, iface := range ifaces { |
361 | + actual[i] = params.NetworkInterfaceParams{ |
362 | + MACAddress: iface.MACAddress(), |
363 | + MachineTag: names.MachineTag(iface.MachineId()), |
364 | + InterfaceName: iface.InterfaceName(), |
365 | + NetworkName: iface.NetworkName(), |
366 | + } |
367 | + } |
368 | + c.Assert(actual, jc.SameContents, expect) |
369 | +} |
370 | + |
371 | +func (s *withoutStateServerSuite) TestAddNetworkInterface(c *gc.C) { |
372 | + // Add a few networks first. |
373 | + _, err := s.State.AddNetwork("net1", "0.1.2.0/24", 0) |
374 | + c.Assert(err, gc.IsNil) |
375 | + _, err = s.State.AddNetwork("vlan42", "0.2.1.0/24", 42) |
376 | + c.Assert(err, gc.IsNil) |
377 | + |
378 | + args := params.AddNetworkInterfaceParams{Interfaces: []params.NetworkInterfaceParams{ |
379 | + {"aa:bb:cc:dd:ee:f0", s.machines[0].Tag(), "eth0", "net1"}, |
380 | + {"aa:bb:cc:dd:ee:f1", s.machines[1].Tag(), "eth0", "net1"}, |
381 | + {"aa:bb:cc:dd:ee:f2", s.machines[1].Tag(), "eth1", "vlan42"}, |
382 | + {"invalid", s.machines[1].Tag(), "eth1", "vlan42"}, |
383 | + {"aa:bb:cc:dd:ee:ff", s.machines[1].Tag(), "", "net1"}, |
384 | + {"aa:bb:cc:dd:ee:ff", s.machines[1].Tag(), "eth1", "invalid"}, |
385 | + {"aa:bb:cc:dd:ee:ff", s.machines[1].Tag(), "eth1", ""}, |
386 | + {"aa:bb:cc:dd:ee:f1", s.machines[1].Tag(), "eth2", "net1"}, |
387 | + {"aa:bb:cc:dd:ee:f1", s.machines[2].Tag(), "eth0", "net1"}, |
388 | + {"aa:bb:cc:dd:ee:f1", "machine-42", "eth0", "net1"}, |
389 | + {"aa:bb:cc:dd:ee:f1", "unit-foo-42", "eth0", "net1"}, |
390 | + }} |
391 | + err = s.machines[2].SetProvisioned("i-am", "fake_nonce", nil) |
392 | + c.Assert(err, gc.IsNil) |
393 | + |
394 | + prefix := "cannot add network interface to machine %s: " |
395 | + prefixM1 := fmt.Sprintf(prefix, s.machines[1].Id()) |
396 | + result, err := s.provisioner.AddNetworkInterface(args) |
397 | + c.Assert(err, gc.IsNil) |
398 | + c.Assert(result, jc.DeepEquals, params.ErrorResults{ |
399 | + Results: []params.ErrorResult{ |
400 | + {Error: nil}, |
401 | + {Error: nil}, |
402 | + {Error: nil}, |
403 | + {Error: apiservertesting.PrefixedError( |
404 | + prefixM1, "invalid MAC address: invalid")}, |
405 | + {Error: apiservertesting.PrefixedError( |
406 | + prefixM1, "interface name must be not empty")}, |
407 | + {Error: apiservertesting.PrefixedError( |
408 | + prefixM1, `network "invalid" not found`)}, |
409 | + {Error: apiservertesting.PrefixedError( |
410 | + prefixM1, `network "" not found`)}, |
411 | + {Error: apiservertesting.PrefixedError( |
412 | + prefixM1, |
413 | + `interface with MAC address "aa:bb:cc:dd:ee:f1" already exists`), |
414 | + }, |
415 | + {Error: apiservertesting.PrefixedError( |
416 | + fmt.Sprintf(prefix, s.machines[2].Id()), |
417 | + `machine already provisioned: dynamic network interfaces not currently supported`, |
418 | + )}, |
419 | + {Error: apiservertesting.NotFoundError("machine 42")}, |
420 | + {Error: apiservertesting.ErrUnauthorized}, |
421 | + }, |
422 | + }) |
423 | + |
424 | + // Now check the added interfaces are there. |
425 | + assertInterfaces(c, s.machines[0], args.Interfaces[0]) |
426 | + assertInterfaces(c, s.machines[1], args.Interfaces[1], args.Interfaces[2]) |
427 | + assertInterfaces(c, s.machines[2]) // None |
428 | +} |
429 | + |
430 | func (s *withoutStateServerSuite) TestSetProvisioned(c *gc.C) { |
431 | // Provision machine 0 first. |
432 | hwChars := instance.MustParseHardware("arch=i386", "mem=4G") |
433 | |
434 | === modified file 'state/apiserver/testing/errors.go' |
435 | --- state/apiserver/testing/errors.go 2014-01-21 10:05:20 +0000 |
436 | +++ state/apiserver/testing/errors.go 2014-04-04 16:33:28 +0000 |
437 | @@ -41,3 +41,7 @@ |
438 | Code: "", |
439 | } |
440 | } |
441 | + |
442 | +func PrefixedError(prefix, message string) *params.Error { |
443 | + return ServerError(prefix + message) |
444 | +} |
Reviewers: mp+214272_ code.launchpad. net,
Message:
Please take a look.
Description:
api/provisioner: Allow adding networks and NICs
This adds two new Provisioner API calls: AddNetworkInter faces bulk call)
* AddNetwork (exposed as st.AddNetworks bulk call on
the client-side provisioner API)
* AddNetworkInterface (exposed in the client-side API
as machine.
These will be used to set the networks/interfaces that
will be configured by MAAS at provisioning time.
Next, we'll make the necessary changes to StartInstance()
in general (and in provider/maas specifically) to return
what networks will the machine start with.
https:/ /code.launchpad .net/~dimitern/ juju-core/ 382-api- provisioner- machine- nics/+merge/ 214272
Requires: /code.launchpad .net/~dimitern/ juju-core/ 381-state- machine- nics/+merge/ 213796
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/84570043/
Affected files (+322, -4 lines): params/ internal. go provisioner/ machine. go provisioner/ provisioner. go provisioner/ provisioner_ test.go /provisioner/ provisioner. go /provisioner/ provisioner_ test.go
A [revision details]
M state/api/
M state/api/
M state/api/
M state/api/
M state/apiserver
M state/apiserver