github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/maas/constraints_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package maas 5 6 import ( 7 "net/url" 8 9 "github.com/juju/errors" 10 "github.com/juju/gomaasapi" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/core/constraints" 15 ) 16 17 func (*environSuite) TestConvertConstraints(c *gc.C) { 18 for i, test := range []struct { 19 cons constraints.Value 20 expected url.Values 21 }{{ 22 cons: constraints.Value{Arch: stringp("arm")}, 23 expected: url.Values{"arch": {"arm"}}, 24 }, { 25 cons: constraints.Value{CpuCores: uint64p(4)}, 26 expected: url.Values{"cpu_count": {"4"}}, 27 }, { 28 cons: constraints.Value{Mem: uint64p(1024)}, 29 expected: url.Values{"mem": {"1024"}}, 30 }, { // Spaces are converted to bindings and not_networks, but only in acquireNode 31 cons: constraints.Value{Spaces: stringslicep("foo", "bar", "^baz", "^oof")}, 32 expected: url.Values{}, 33 }, { 34 cons: constraints.Value{Tags: stringslicep("tag1", "tag2", "^tag3", "^tag4")}, 35 expected: url.Values{ 36 "tags": {"tag1,tag2"}, 37 "not_tags": {"tag3,tag4"}, 38 }, 39 }, { // CpuPower is ignored. 40 cons: constraints.Value{CpuPower: uint64p(1024)}, 41 expected: url.Values{}, 42 }, { // RootDisk is ignored. 43 cons: constraints.Value{RootDisk: uint64p(8192)}, 44 expected: url.Values{}, 45 }, { 46 cons: constraints.Value{Tags: stringslicep("foo", "bar")}, 47 expected: url.Values{"tags": {"foo,bar"}}, 48 }, { 49 cons: constraints.Value{ 50 Arch: stringp("arm"), 51 CpuCores: uint64p(4), 52 Mem: uint64p(1024), 53 CpuPower: uint64p(1024), 54 RootDisk: uint64p(8192), 55 Spaces: stringslicep("foo", "^bar"), 56 Tags: stringslicep("^tag1", "tag2"), 57 }, 58 expected: url.Values{ 59 "arch": {"arm"}, 60 "cpu_count": {"4"}, 61 "mem": {"1024"}, 62 "tags": {"tag2"}, 63 "not_tags": {"tag1"}, 64 }, 65 }} { 66 c.Logf("test #%d: cons=%s", i, test.cons.String()) 67 c.Check(convertConstraints(test.cons), jc.DeepEquals, test.expected) 68 } 69 } 70 71 func (*environSuite) TestConvertConstraints2(c *gc.C) { 72 for i, test := range []struct { 73 cons constraints.Value 74 expected gomaasapi.AllocateMachineArgs 75 }{{ 76 cons: constraints.Value{Arch: stringp("arm")}, 77 expected: gomaasapi.AllocateMachineArgs{Architecture: "arm"}, 78 }, { 79 cons: constraints.Value{CpuCores: uint64p(4)}, 80 expected: gomaasapi.AllocateMachineArgs{MinCPUCount: 4}, 81 }, { 82 cons: constraints.Value{Mem: uint64p(1024)}, 83 expected: gomaasapi.AllocateMachineArgs{MinMemory: 1024}, 84 }, { // Spaces are converted to bindings and not_networks, but only in acquireNode 85 cons: constraints.Value{Spaces: stringslicep("foo", "bar", "^baz", "^oof")}, 86 expected: gomaasapi.AllocateMachineArgs{}, 87 }, { 88 cons: constraints.Value{Tags: stringslicep("tag1", "tag2", "^tag3", "^tag4")}, 89 expected: gomaasapi.AllocateMachineArgs{ 90 Tags: []string{"tag1", "tag2"}, 91 NotTags: []string{"tag3", "tag4"}, 92 }, 93 }, { // CpuPower is ignored. 94 cons: constraints.Value{CpuPower: uint64p(1024)}, 95 expected: gomaasapi.AllocateMachineArgs{}, 96 }, { // RootDisk is ignored. 97 cons: constraints.Value{RootDisk: uint64p(8192)}, 98 expected: gomaasapi.AllocateMachineArgs{}, 99 }, { 100 cons: constraints.Value{Tags: stringslicep("foo", "bar")}, 101 expected: gomaasapi.AllocateMachineArgs{ 102 Tags: []string{"foo", "bar"}, 103 }, 104 }, { 105 cons: constraints.Value{ 106 Arch: stringp("arm"), 107 CpuCores: uint64p(4), 108 Mem: uint64p(1024), 109 CpuPower: uint64p(1024), 110 RootDisk: uint64p(8192), 111 Spaces: stringslicep("foo", "^bar"), 112 Tags: stringslicep("^tag1", "tag2"), 113 }, 114 expected: gomaasapi.AllocateMachineArgs{ 115 Architecture: "arm", 116 MinCPUCount: 4, 117 MinMemory: 1024, 118 Tags: []string{"tag2"}, 119 NotTags: []string{"tag1"}, 120 }, 121 }} { 122 c.Logf("test #%d: cons2=%s", i, test.cons.String()) 123 c.Check(convertConstraints2(test.cons), jc.DeepEquals, test.expected) 124 } 125 } 126 127 var nilStringSlice []string 128 129 func (*environSuite) TestConvertTagsToParams(c *gc.C) { 130 for i, test := range []struct { 131 tags *[]string 132 expected url.Values 133 }{{ 134 tags: nil, 135 expected: url.Values{}, 136 }, { 137 tags: &nilStringSlice, 138 expected: url.Values{}, 139 }, { 140 tags: &[]string{}, 141 expected: url.Values{}, 142 }, { 143 tags: stringslicep(""), 144 expected: url.Values{}, 145 }, { 146 tags: stringslicep("foo"), 147 expected: url.Values{ 148 "tags": {"foo"}, 149 }, 150 }, { 151 tags: stringslicep("^bar"), 152 expected: url.Values{ 153 "not_tags": {"bar"}, 154 }, 155 }, { 156 tags: stringslicep("foo", "^bar", "baz", "^oof"), 157 expected: url.Values{ 158 "tags": {"foo,baz"}, 159 "not_tags": {"bar,oof"}, 160 }, 161 }, { 162 tags: stringslicep("", "^bar", "^", "^oof"), 163 expected: url.Values{ 164 "not_tags": {"bar,oof"}, 165 }, 166 }, { 167 tags: stringslicep("foo", "^", " b a z ", "^^ ^"), 168 expected: url.Values{ 169 "tags": {"foo, b a z "}, 170 "not_tags": {"^ ^"}, 171 }, 172 }, { 173 tags: stringslicep("", "^bar", " ", " ^ o of "), 174 expected: url.Values{ 175 "tags": {" , ^ o of "}, 176 "not_tags": {"bar"}, 177 }, 178 }, { 179 tags: stringslicep("foo", "foo", "^bar", "^bar"), 180 expected: url.Values{ 181 "tags": {"foo,foo"}, 182 "not_tags": {"bar,bar"}, 183 }, 184 }} { 185 c.Logf("test #%d: tags=%v", i, test.tags) 186 var vals = url.Values{} 187 convertTagsToParams(vals, test.tags) 188 c.Check(vals, jc.DeepEquals, test.expected) 189 } 190 } 191 192 func uint64p(val uint64) *uint64 { 193 return &val 194 } 195 196 func stringp(val string) *string { 197 return &val 198 } 199 200 func stringslicep(values ...string) *[]string { 201 return &values 202 } 203 204 func (suite *environSuite) TestSelectNodeValidZone(c *gc.C) { 205 env := suite.makeEnviron() 206 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0", "zone": "bar"}`) 207 208 snArgs := selectNodeArgs{ 209 AvailabilityZone: "bar", 210 Constraints: constraints.Value{}, 211 } 212 213 node, err := env.selectNode(suite.callCtx, snArgs) 214 c.Assert(err, gc.IsNil) 215 c.Assert(node, gc.NotNil) 216 } 217 218 func (suite *environSuite) TestSelectNodeInvalidZone(c *gc.C) { 219 env := suite.makeEnviron() 220 221 snArgs := selectNodeArgs{ 222 AvailabilityZone: "foo", 223 Constraints: constraints.Value{}, 224 } 225 226 _, err := env.selectNode(suite.callCtx, snArgs) 227 c.Assert(err, gc.NotNil) 228 c.Assert(err.noMatch, jc.IsTrue) 229 c.Assert(err, gc.ErrorMatches, ".*409.*") 230 } 231 232 func (suite *environSuite) TestAcquireNode(c *gc.C) { 233 env := suite.makeEnviron() 234 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 235 236 _, err := env.acquireNode(suite.callCtx, "", "", "", constraints.Value{}, nil, nil) 237 238 c.Check(err, jc.ErrorIsNil) 239 operations := suite.testMAASObject.TestServer.NodeOperations() 240 actions, found := operations["node0"] 241 c.Assert(found, jc.IsTrue) 242 c.Check(actions, gc.DeepEquals, []string{"acquire"}) 243 244 // no "name" parameter should have been passed through 245 values := suite.testMAASObject.TestServer.NodeOperationRequestValues()["node0"][0] 246 _, found = values["name"] 247 c.Assert(found, jc.IsFalse) 248 } 249 250 func (suite *environSuite) TestAcquireNodeByName(c *gc.C) { 251 env := suite.makeEnviron() 252 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 253 254 _, err := env.acquireNode(suite.callCtx, "host0", "", "", constraints.Value{}, nil, nil) 255 256 c.Check(err, jc.ErrorIsNil) 257 operations := suite.testMAASObject.TestServer.NodeOperations() 258 actions, found := operations["node0"] 259 c.Assert(found, jc.IsTrue) 260 c.Check(actions, gc.DeepEquals, []string{"acquire"}) 261 262 // no "name" parameter should have been passed through 263 values := suite.testMAASObject.TestServer.NodeOperationRequestValues()["node0"][0] 264 nodeName := values.Get("name") 265 c.Assert(nodeName, gc.Equals, "host0") 266 } 267 268 func (suite *environSuite) TestAcquireNodeTakesConstraintsIntoAccount(c *gc.C) { 269 env := suite.makeEnviron() 270 suite.testMAASObject.TestServer.NewNode( 271 `{"system_id": "node0", "hostname": "host0", "architecture": "arm/generic", "memory": 2048}`, 272 ) 273 constraints := constraints.Value{Arch: stringp("arm"), Mem: uint64p(1024)} 274 275 _, err := env.acquireNode(suite.callCtx, "", "", "", constraints, nil, nil) 276 277 c.Check(err, jc.ErrorIsNil) 278 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 279 nodeRequestValues, found := requestValues["node0"] 280 c.Assert(found, jc.IsTrue) 281 c.Assert(nodeRequestValues[0].Get("arch"), gc.Equals, "arm") 282 c.Assert(nodeRequestValues[0].Get("mem"), gc.Equals, "1024") 283 } 284 285 func (suite *environSuite) TestParseDelimitedValues(c *gc.C) { 286 for i, test := range []struct { 287 about string 288 input []string 289 positives []string 290 negatives []string 291 }{{ 292 about: "nil input", 293 input: nil, 294 positives: []string{}, 295 negatives: []string{}, 296 }, { 297 about: "empty input", 298 input: []string{}, 299 positives: []string{}, 300 negatives: []string{}, 301 }, { 302 about: "values list with embedded whitespace", 303 input: []string{" val1 ", " val2", " ^ not Val 3 ", " ", " ", "^", "", "^ notVal4 "}, 304 positives: []string{" val1 ", " val2", " ^ not Val 3 ", " ", " "}, 305 negatives: []string{" notVal4 "}, 306 }, { 307 about: "only positives", 308 input: []string{"val1", "val2", "val3"}, 309 positives: []string{"val1", "val2", "val3"}, 310 negatives: []string{}, 311 }, { 312 about: "only negatives", 313 input: []string{"^val1", "^val2", "^val3"}, 314 positives: []string{}, 315 negatives: []string{"val1", "val2", "val3"}, 316 }, { 317 about: "multi-caret negatives", 318 input: []string{"^foo^", "^v^a^l2", " ^^ ^", "^v^al3", "^^", "^"}, 319 positives: []string{" ^^ ^"}, 320 negatives: []string{"foo^", "v^a^l2", "v^al3", "^"}, 321 }, { 322 about: "both positives and negatives", 323 input: []string{"^val1", "val2", "^val3", "val4"}, 324 positives: []string{"val2", "val4"}, 325 negatives: []string{"val1", "val3"}, 326 }, { 327 about: "single positive value", 328 input: []string{"val1"}, 329 positives: []string{"val1"}, 330 negatives: []string{}, 331 }, { 332 about: "single negative value", 333 input: []string{"^val1"}, 334 positives: []string{}, 335 negatives: []string{"val1"}, 336 }} { 337 c.Logf("test %d: %s", i, test.about) 338 positives, negatives := parseDelimitedValues(test.input) 339 c.Check(positives, jc.DeepEquals, test.positives) 340 c.Check(negatives, jc.DeepEquals, test.negatives) 341 } 342 } 343 344 func (suite *environSuite) TestAcquireNodePassedAgentName(c *gc.C) { 345 env := suite.makeEnviron() 346 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 347 348 _, err := env.acquireNode(suite.callCtx, "", "", "", constraints.Value{}, nil, nil) 349 350 c.Check(err, jc.ErrorIsNil) 351 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 352 nodeRequestValues, found := requestValues["node0"] 353 c.Assert(found, jc.IsTrue) 354 c.Assert(nodeRequestValues[0].Get("agent_name"), gc.Equals, env.Config().UUID()) 355 } 356 357 func (suite *environSuite) TestAcquireNodePassesPositiveAndNegativeTags(c *gc.C) { 358 env := suite.makeEnviron() 359 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "tag_names": "tag1,tag3"}`) 360 361 _, err := env.acquireNode( 362 suite.callCtx, 363 "", "", "", 364 constraints.Value{Tags: stringslicep("tag1", "^tag2", "tag3", "^tag4")}, 365 nil, nil, 366 ) 367 368 c.Check(err, jc.ErrorIsNil) 369 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 370 nodeValues, found := requestValues["node0"] 371 c.Assert(found, jc.IsTrue) 372 c.Assert(nodeValues[0].Get("tags"), gc.Equals, "tag1,tag3") 373 c.Assert(nodeValues[0].Get("not_tags"), gc.Equals, "tag2,tag4") 374 } 375 376 func (suite *environSuite) TestAcquireNodePassesPositiveAndNegativeSpaces(c *gc.C) { 377 suite.createFourSpaces(c) 378 env := suite.makeEnviron() 379 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0"}`) 380 381 _, err := env.acquireNode( 382 suite.callCtx, 383 "", "", "", 384 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 385 nil, nil, 386 ) 387 c.Check(err, jc.ErrorIsNil) 388 requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() 389 nodeValues, found := requestValues["node0"] 390 c.Assert(found, jc.IsTrue) 391 c.Check(nodeValues[0].Get("interfaces"), gc.Equals, "0:space=2;1:space=4") 392 c.Check(nodeValues[0]["not_networks"], jc.DeepEquals, []string{"space:3", "space:5"}) 393 } 394 395 func (suite *environSuite) createFourSpaces(c *gc.C) { 396 server := suite.testMAASObject.TestServer 397 server.SetVersionJSON(`{"capabilities": ["network-deployment-ubuntu"]}`) 398 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-1"})) 399 suite.addSubnet(c, 1, 1, "node1") 400 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-2"})) 401 suite.addSubnet(c, 2, 2, "node1") 402 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-3"})) 403 suite.addSubnet(c, 3, 3, "node1") 404 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-4"})) 405 suite.addSubnet(c, 4, 4, "node1") 406 } 407 408 func (suite *environSuite) TestAcquireNodeDisambiguatesNamedLabelsFromIndexedUpToALimit(c *gc.C) { 409 suite.createFourSpaces(c) 410 var shortLimit uint = 0 411 suite.PatchValue(&numericLabelLimit, shortLimit) 412 env := suite.makeEnviron() 413 suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0"}`) 414 415 _, err := env.acquireNode( 416 suite.callCtx, 417 "", "", "", 418 constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")}, 419 []interfaceBinding{{"0", "first-clash"}, {"1", "final-clash"}}, 420 nil, 421 ) 422 c.Assert(err, gc.ErrorMatches, `too many conflicting numeric labels, giving up.`) 423 } 424 425 func (suite *environSuite) TestAcquireNodeStorage(c *gc.C) { 426 server := suite.testMAASObject.TestServer 427 for i, test := range []struct { 428 volumes []volumeInfo 429 expected string 430 }{{ 431 volumes: nil, 432 expected: "", 433 }, { 434 volumes: []volumeInfo{{"volume-1", 1234, nil}}, 435 expected: "volume-1:1234", 436 }, { 437 volumes: []volumeInfo{{"", 1234, []string{"tag1", "tag2"}}}, 438 expected: "1234(tag1,tag2)", 439 }, { 440 volumes: []volumeInfo{{"volume-1", 1234, []string{"tag1", "tag2"}}}, 441 expected: "volume-1:1234(tag1,tag2)", 442 }, { 443 volumes: []volumeInfo{ 444 {"volume-1", 1234, []string{"tag1", "tag2"}}, 445 {"volume-2", 4567, []string{"tag1", "tag3"}}, 446 }, 447 expected: "volume-1:1234(tag1,tag2),volume-2:4567(tag1,tag3)", 448 }} { 449 c.Logf("test #%d: volumes=%v", i, test.volumes) 450 env := suite.makeEnviron() 451 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "space-1"})) 452 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 453 suite.addSubnet(c, 1, 1, "node0") 454 _, err := env.acquireNode(suite.callCtx, "", "", "", constraints.Value{}, nil, test.volumes) 455 c.Check(err, jc.ErrorIsNil) 456 requestValues := server.NodeOperationRequestValues() 457 nodeRequestValues, found := requestValues["node0"] 458 if c.Check(found, jc.IsTrue) { 459 c.Check(nodeRequestValues[0].Get("storage"), gc.Equals, test.expected) 460 } 461 suite.testMAASObject.TestServer.Clear() 462 } 463 } 464 465 func (suite *environSuite) TestAcquireNodeInterfaces(c *gc.C) { 466 server := suite.testMAASObject.TestServer 467 // Add some constraints, including spaces to verify specified bindings 468 // always override any spaces constraints. 469 cons := constraints.Value{ 470 Spaces: stringslicep("foo", "^bar"), 471 } 472 // In the tests below "space:5" means foo, "space:6" means bar. 473 for i, test := range []struct { 474 interfaces []interfaceBinding 475 expectedPositives string 476 expectedNegatives string 477 expectedError string 478 }{{ // without specified bindings, spaces constraints are used instead. 479 interfaces: nil, 480 expectedPositives: "0:space=5", 481 expectedNegatives: "space:6", 482 expectedError: "", 483 }, { 484 interfaces: []interfaceBinding{{"name-1", "space-1"}}, 485 expectedPositives: "name-1:space=space-1;0:space=5", 486 expectedNegatives: "space:6", 487 }, { 488 interfaces: []interfaceBinding{ 489 {"name-1", "1"}, 490 {"name-2", "2"}, 491 {"name-3", "3"}, 492 }, 493 expectedPositives: "name-1:space=1;name-2:space=2;name-3:space=3;0:space=5", 494 expectedNegatives: "space:6", 495 }, { 496 interfaces: []interfaceBinding{{"", "anything"}}, 497 expectedPositives: "0:space=anything;1:space=5", 498 expectedNegatives: "space:6", 499 }, { 500 interfaces: []interfaceBinding{{"shared-db", "6"}}, 501 expectedError: `negative space "bar" from constraints clashes with interface bindings`, 502 }, { 503 interfaces: []interfaceBinding{ 504 {"shared-db", "1"}, 505 {"db", "1"}, 506 }, 507 expectedPositives: "shared-db:space=1;db:space=1;0:space=5", 508 expectedNegatives: "space:6", 509 }, { 510 interfaces: []interfaceBinding{{"", ""}}, 511 expectedError: `invalid interface binding "": space provider ID is required`, 512 }, { 513 interfaces: []interfaceBinding{ 514 {"valid", "ok"}, 515 {"", "valid-but-ignored-space"}, 516 {"valid-name-empty-space", ""}, 517 {"", ""}, 518 }, 519 expectedError: `invalid interface binding "valid-name-empty-space": space provider ID is required`, 520 }, { 521 interfaces: []interfaceBinding{{"foo", ""}}, 522 expectedError: `invalid interface binding "foo": space provider ID is required`, 523 }, { 524 interfaces: []interfaceBinding{ 525 {"bar", ""}, 526 {"valid", "ok"}, 527 {"", "valid-but-ignored-space"}, 528 {"", ""}, 529 }, 530 expectedError: `invalid interface binding "bar": space provider ID is required`, 531 }, { 532 interfaces: []interfaceBinding{ 533 {"dup-name", "1"}, 534 {"dup-name", "2"}, 535 }, 536 expectedError: `duplicated interface binding "dup-name"`, 537 }, { 538 interfaces: []interfaceBinding{ 539 {"valid-1", "0"}, 540 {"dup-name", "1"}, 541 {"dup-name", "2"}, 542 {"valid-2", "3"}, 543 }, 544 expectedError: `duplicated interface binding "dup-name"`, 545 }} { 546 suite.testMAASObject.TestServer.Clear() 547 c.Logf("test #%d: interfaces=%v", i, test.interfaces) 548 suite.createFourSpaces(c) 549 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "foo"})) 550 suite.addSubnetWithSpace(c, 6, 6, "foo", "node1") 551 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "bar"})) 552 suite.addSubnetWithSpace(c, 7, 7, "bar", "node1") 553 env := suite.makeEnviron() 554 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 555 _, err := env.acquireNode(suite.callCtx, "", "", "", cons, test.interfaces, nil) 556 if test.expectedError != "" { 557 c.Check(err, gc.ErrorMatches, test.expectedError) 558 c.Check(err, jc.Satisfies, errors.IsNotValid) 559 continue 560 } 561 c.Check(err, jc.ErrorIsNil) 562 requestValues := server.NodeOperationRequestValues() 563 nodeRequestValues, found := requestValues["node0"] 564 if c.Check(found, jc.IsTrue) { 565 566 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, test.expectedPositives) 567 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, test.expectedNegatives) 568 } 569 } 570 } 571 572 func (suite *environSuite) createFooBarSpaces(c *gc.C) { 573 server := suite.testMAASObject.TestServer 574 server.SetVersionJSON(`{"capabilities": ["network-deployment-ubuntu"]}`) 575 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "foo"})) 576 suite.addSubnetWithSpace(c, 1, 2, "foo", "node1") 577 server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "bar"})) 578 suite.addSubnetWithSpace(c, 2, 3, "bar", "node1") 579 } 580 581 func (suite *environSuite) TestAcquireNodeConvertsSpaceNames(c *gc.C) { 582 server := suite.testMAASObject.TestServer 583 suite.createFooBarSpaces(c) 584 cons := constraints.Value{ 585 Spaces: stringslicep("foo", "^bar"), 586 } 587 env := suite.makeEnviron() 588 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 589 _, err := env.acquireNode(suite.callCtx, "", "", "", cons, nil, nil) 590 c.Assert(err, jc.ErrorIsNil) 591 requestValues := server.NodeOperationRequestValues() 592 nodeRequestValues, found := requestValues["node0"] 593 c.Assert(found, jc.IsTrue) 594 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, "0:space=2") 595 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, "space:3") 596 } 597 598 func (suite *environSuite) TestAcquireNodeTranslatesSpaceNames(c *gc.C) { 599 server := suite.testMAASObject.TestServer 600 suite.createFooBarSpaces(c) 601 cons := constraints.Value{ 602 Spaces: stringslicep("foo-1", "^bar-3"), 603 } 604 env := suite.makeEnviron() 605 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 606 _, err := env.acquireNode(suite.callCtx, "", "", "", cons, nil, nil) 607 c.Assert(err, jc.ErrorIsNil) 608 requestValues := server.NodeOperationRequestValues() 609 nodeRequestValues, found := requestValues["node0"] 610 c.Assert(found, jc.IsTrue) 611 c.Check(nodeRequestValues[0].Get("interfaces"), gc.Equals, "0:space=2") 612 c.Check(nodeRequestValues[0].Get("not_networks"), gc.Equals, "space:3") 613 } 614 615 func (suite *environSuite) TestAcquireNodeUnrecognisedSpace(c *gc.C) { 616 server := suite.testMAASObject.TestServer 617 suite.createFooBarSpaces(c) 618 cons := constraints.Value{ 619 Spaces: stringslicep("baz"), 620 } 621 env := suite.makeEnviron() 622 server.NewNode(`{"system_id": "node0", "hostname": "host0"}`) 623 _, err := env.acquireNode(suite.callCtx, "", "", "", cons, nil, nil) 624 c.Assert(err, gc.ErrorMatches, `unrecognised space in constraint "baz"`) 625 }