github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/network/netplan/netplan_test.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package netplan_test 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "math/rand" 10 "os" 11 "path" 12 "reflect" 13 "strings" 14 15 "github.com/juju/errors" 16 "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/kr/pretty" 19 gc "gopkg.in/check.v1" 20 "gopkg.in/yaml.v2" 21 22 "github.com/juju/juju/network/netplan" 23 coretesting "github.com/juju/juju/testing" 24 ) 25 26 type NetplanSuite struct { 27 testing.IsolationSuite 28 } 29 30 var _ = gc.Suite(&NetplanSuite{}) 31 32 func MustNetplanFromYaml(c *gc.C, input string) *netplan.Netplan { 33 var np netplan.Netplan 34 if strings.HasPrefix(input, "\n") { 35 input = input[1:] 36 } 37 err := netplan.Unmarshal([]byte(input), &np) 38 c.Assert(err, jc.ErrorIsNil) 39 return &np 40 } 41 42 func checkNetplanRoundTrips(c *gc.C, input string) { 43 if strings.HasPrefix(input, "\n") { 44 input = input[1:] 45 } 46 var np netplan.Netplan 47 err := netplan.Unmarshal([]byte(input), &np) 48 c.Assert(err, jc.ErrorIsNil) 49 out, err := netplan.Marshal(&np) 50 c.Assert(err, jc.ErrorIsNil) 51 c.Check(string(out), gc.Equals, input) 52 } 53 54 func (s *NetplanSuite) TestStructures(c *gc.C) { 55 checkNetplanRoundTrips(c, ` 56 network: 57 version: 2 58 renderer: NetworkManager 59 ethernets: 60 id0: 61 match: 62 macaddress: "00:11:22:33:44:55" 63 wakeonlan: true 64 addresses: 65 - 192.168.14.2/24 66 - 2001:1::1/64 67 critical: true 68 dhcp4: true 69 dhcp-identifier: mac 70 gateway4: 192.168.14.1 71 gateway6: 2001:1::2 72 nameservers: 73 search: [foo.local, bar.local] 74 addresses: [8.8.8.8] 75 routes: 76 - to: 0.0.0.0/0 77 via: 11.0.0.1 78 metric: 3 79 lom: 80 match: 81 driver: ixgbe 82 set-name: lom1 83 dhcp6: true 84 switchports: 85 match: 86 name: enp2* 87 mtu: 1280 88 wifis: 89 all-wlans: 90 access-points: 91 Joe's home: 92 password: s3kr1t 93 wlp1s0: 94 access-points: 95 guest: 96 mode: ap 97 channel: 11 98 bridges: 99 br0: 100 interfaces: [wlp1s0, switchports] 101 dhcp4: false 102 routes: 103 - to: 0.0.0.0/0 104 via: 11.0.0.1 105 metric: 3 106 `) 107 } 108 109 func (s *NetplanSuite) TestBasicBond(c *gc.C) { 110 checkNetplanRoundTrips(c, ` 111 network: 112 version: 2 113 renderer: NetworkManager 114 ethernets: 115 id0: 116 match: 117 macaddress: "00:11:22:33:44:55" 118 set-name: id0 119 id1: 120 match: 121 macaddress: de:ad:be:ef:01:02 122 set-name: id1 123 bridges: 124 br-bond0: 125 interfaces: [bond0] 126 dhcp4: true 127 bonds: 128 bond0: 129 interfaces: [id0, id1] 130 parameters: 131 mode: 802.3ad 132 lacp-rate: fast 133 mii-monitor-interval: 100 134 transmit-hash-policy: layer2 135 up-delay: 0 136 down-delay: 0 137 `) 138 } 139 140 func (s *NetplanSuite) TestParseBridgedBond(c *gc.C) { 141 checkNetplanRoundTrips(c, ` 142 network: 143 version: 2 144 renderer: NetworkManager 145 ethernets: 146 id0: 147 match: 148 macaddress: "00:11:22:33:44:55" 149 set-name: id0 150 id1: 151 match: 152 macaddress: de:ad:be:ef:01:02 153 set-name: id1 154 bridges: 155 br-bond0: 156 interfaces: [bond0] 157 dhcp4: true 158 bonds: 159 bond0: 160 interfaces: [id0, id1] 161 parameters: 162 mode: 802.3ad 163 lacp-rate: fast 164 mii-monitor-interval: 100 165 transmit-hash-policy: layer2 166 up-delay: 0 167 down-delay: 0 168 `) 169 } 170 171 func (s *NetplanSuite) TestBondsIntParameters(c *gc.C) { 172 // several parameters can be specified as an integer or a string 173 // such as 'mode: 0' is the same as 'balance-rr' 174 checkNetplanRoundTrips(c, ` 175 network: 176 version: 2 177 renderer: NetworkManager 178 ethernets: 179 id0: 180 match: 181 macaddress: "00:11:22:33:44:55" 182 set-name: id0 183 id1: 184 match: 185 macaddress: de:ad:be:ef:01:02 186 set-name: id1 187 bonds: 188 bond0: 189 interfaces: [id0, id1] 190 parameters: 191 mode: 0 192 lacp-rate: 1 193 ad-select: 1 194 all-slaves-active: true 195 arp-validate: 0 196 arp-all-targets: 0 197 fail-over-mac-policy: 1 198 primary-reselect-policy: 1 199 `) 200 checkNetplanRoundTrips(c, ` 201 network: 202 version: 2 203 renderer: NetworkManager 204 ethernets: 205 id0: 206 match: 207 macaddress: "00:11:22:33:44:55" 208 set-name: id0 209 id1: 210 match: 211 macaddress: de:ad:be:ef:01:02 212 set-name: id1 213 bonds: 214 bond0: 215 interfaces: [id0, id1] 216 parameters: 217 mode: balance-rr 218 lacp-rate: fast 219 ad-select: bandwidth 220 all-slaves-active: false 221 arp-validate: filter 222 arp-all-targets: all 223 fail-over-mac-policy: follow 224 primary-reselect-policy: always 225 `) 226 } 227 228 func (s *NetplanSuite) TestBondWithVLAN(c *gc.C) { 229 checkNetplanRoundTrips(c, ` 230 network: 231 version: 2 232 renderer: NetworkManager 233 ethernets: 234 id0: 235 match: 236 macaddress: "00:11:22:33:44:55" 237 set-name: id0 238 id1: 239 match: 240 macaddress: de:ad:be:ef:01:02 241 set-name: id1 242 bonds: 243 bond0: 244 interfaces: [id0, id1] 245 parameters: 246 mode: 802.3ad 247 lacp-rate: fast 248 mii-monitor-interval: 100 249 transmit-hash-policy: layer2 250 up-delay: 0 251 down-delay: 0 252 vlans: 253 bond0.209: 254 id: 209 255 link: bond0 256 addresses: 257 - 123.123.123.123/24 258 nameservers: 259 addresses: [8.8.8.8] 260 `) 261 } 262 263 func (s *NetplanSuite) TestBondsAllParameters(c *gc.C) { 264 // All parameters don't inherently make sense at the same time, but we should be able to parse all of them. 265 // nolint: misspell 266 checkNetplanRoundTrips(c, ` 267 network: 268 version: 2 269 renderer: NetworkManager 270 ethernets: 271 id0: 272 match: 273 macaddress: "00:11:22:33:44:55" 274 set-name: id0 275 id1: 276 match: 277 macaddress: de:ad:be:ef:01:02 278 set-name: id1 279 id2: 280 match: 281 macaddress: de:ad:be:ef:01:03 282 id3: 283 match: 284 macaddress: de:ad:be:ef:01:04 285 bonds: 286 bond0: 287 interfaces: [id0, id1] 288 parameters: 289 mode: 802.3ad 290 lacp-rate: fast 291 mii-monitor-interval: 100 292 min-links: 0 293 transmit-hash-policy: layer2 294 ad-select: 1 295 all-slaves-active: true 296 arp-interval: 100 297 arp-ip-targets: 298 - 192.168.0.1 299 - 192.168.10.20 300 arp-validate: none 301 arp-all-targets: all 302 up-delay: 0 303 down-delay: 0 304 fail-over-mac-policy: follow 305 gratuitious-arp: 0 306 packets-per-slave: 0 307 primary-reselect-policy: better 308 resend-igmp: 0 309 learn-packet-interval: 4660 310 primary: id1 311 `) 312 } 313 314 func (s *NetplanSuite) TestBridgesAllParameters(c *gc.C) { 315 // All parameters don't inherently make sense at the same time, but we should be able to parse all of them. 316 checkNetplanRoundTrips(c, ` 317 network: 318 version: 2 319 renderer: NetworkManager 320 ethernets: 321 id0: 322 match: 323 macaddress: "00:11:22:33:44:55" 324 set-name: id0 325 id1: 326 match: 327 macaddress: de:ad:be:ef:01:02 328 set-name: id1 329 id2: 330 match: 331 macaddress: de:ad:be:ef:01:03 332 set-name: id2 333 bridges: 334 br-id0: 335 interfaces: [id0] 336 accept-ra: true 337 addresses: 338 - 123.123.123.123/24 339 dhcp4: false 340 dhcp6: true 341 dhcp-identifier: duid 342 parameters: 343 ageing-time: 0 344 forward-delay: 0 345 hello-time: 0 346 max-age: 0 347 path-cost: 348 id0: 0 349 port-priority: 350 id0: 0 351 priority: 0 352 stp: false 353 br-id1: 354 interfaces: [id1] 355 accept-ra: false 356 addresses: 357 - 2001::1/64 358 dhcp4: true 359 dhcp6: true 360 dhcp-identifier: mac 361 parameters: 362 ageing-time: 100 363 forward-delay: 10 364 hello-time: 20 365 max-age: 10 366 path-cost: 367 id1: 50 368 port-priority: 369 id1: 50 370 priority: 20000 371 stp: true 372 br-id2: 373 interfaces: [id2] 374 br-id3: 375 interfaces: [id2] 376 parameters: 377 ageing-time: 10 378 `) 379 } 380 381 func (s *NetplanSuite) TestAllRoutesParams(c *gc.C) { 382 checkNetplanRoundTrips(c, ` 383 network: 384 version: 2 385 renderer: NetworkManager 386 ethernets: 387 id0: 388 match: 389 macaddress: "00:11:22:33:44:55" 390 set-name: id0 391 routes: 392 - from: 192.168.0.0/24 393 on-link: true 394 scope: global 395 table: 1234 396 to: 192.168.3.1/24 397 type: unicast 398 via: 192.168.3.1 399 metric: 1234567 400 - on-link: false 401 to: 192.168.5.1/24 402 via: 192.168.5.1 403 metric: 0 404 - to: 192.168.5.1/24 405 type: unreachable 406 via: 192.168.5.1 407 routing-policy: 408 - from: 192.168.10.0/24 409 mark: 123 410 priority: 10 411 table: 1234 412 to: 192.168.3.1/24 413 type-of-service: 0 414 - from: 192.168.12.0/24 415 mark: 0 416 priority: 0 417 table: 0 418 to: 192.168.3.1/24 419 type-of-service: 255 420 `) 421 } 422 423 func (s *NetplanSuite) TestAllVLANParams(c *gc.C) { 424 checkNetplanRoundTrips(c, ` 425 network: 426 version: 2 427 renderer: NetworkManager 428 ethernets: 429 id0: 430 match: 431 macaddress: "00:11:22:33:44:55" 432 set-name: id0 433 vlans: 434 id0.123: 435 id: 123 436 link: id0 437 accept-ra: true 438 addresses: 439 - 123.123.123.123/24 440 critical: true 441 dhcp4: false 442 dhcp6: false 443 dhcp-identifier: duid 444 gateway4: 123.123.123.123 445 gateway6: dead::beef 446 nameservers: 447 addresses: [8.8.8.8] 448 macaddress: de:ad:be:ef:12:34 449 mtu: 9000 450 renderer: NetworkManager 451 routes: 452 - table: 102 453 to: 100.0.0.0/8 454 via: 1.2.3.10 455 metric: 5 456 routing-policy: 457 - from: 192.168.5.0/24 458 table: 103 459 optional: true 460 id0.456: 461 id: 456 462 link: id0 463 accept-ra: false 464 `) 465 } 466 467 func (s *NetplanSuite) TestSimpleBridger(c *gc.C) { 468 np := MustNetplanFromYaml(c, ` 469 network: 470 version: 2 471 renderer: NetworkManager 472 ethernets: 473 id0: 474 match: 475 macaddress: "00:11:22:33:44:55" 476 addresses: 477 - 1.2.3.4/24 478 - 2000::1/64 479 gateway4: 1.2.3.5 480 gateway6: 2000::2 481 nameservers: 482 search: [foo.local, bar.local] 483 addresses: [8.8.8.8] 484 routes: 485 - to: 100.0.0.0/8 486 via: 1.2.3.10 487 metric: 5 488 `) 489 expected := ` 490 network: 491 version: 2 492 renderer: NetworkManager 493 ethernets: 494 id0: 495 match: 496 macaddress: "00:11:22:33:44:55" 497 bridges: 498 juju-bridge: 499 interfaces: [id0] 500 addresses: 501 - 1.2.3.4/24 502 - 2000::1/64 503 gateway4: 1.2.3.5 504 gateway6: 2000::2 505 nameservers: 506 search: [foo.local, bar.local] 507 addresses: [8.8.8.8] 508 routes: 509 - to: 100.0.0.0/8 510 via: 1.2.3.10 511 metric: 5 512 `[1:] 513 err := np.BridgeEthernetById("id0", "juju-bridge") 514 c.Assert(err, jc.ErrorIsNil) 515 516 out, err := netplan.Marshal(np) 517 c.Assert(err, jc.ErrorIsNil) 518 c.Check(string(out), gc.Equals, expected) 519 } 520 521 func (s *NetplanSuite) TestBridgerIdempotent(c *gc.C) { 522 input := ` 523 network: 524 version: 2 525 renderer: NetworkManager 526 ethernets: 527 id0: 528 match: 529 macaddress: "00:11:22:33:44:55" 530 bridges: 531 juju-bridge: 532 interfaces: [id0] 533 addresses: 534 - 1.2.3.4/24 535 - 2000::1/64 536 gateway4: 1.2.3.5 537 gateway6: 2000::2 538 nameservers: 539 search: [foo.local, bar.local] 540 addresses: [8.8.8.8] 541 routes: 542 - to: 100.0.0.0/8 543 via: 1.2.3.10 544 metric: 5 545 `[1:] 546 np := MustNetplanFromYaml(c, input) 547 c.Assert(np.BridgeEthernetById("id0", "juju-bridge"), jc.ErrorIsNil) 548 out, err := netplan.Marshal(np) 549 c.Check(string(out), gc.Equals, input) 550 c.Assert(err, jc.ErrorIsNil) 551 } 552 553 func (s *NetplanSuite) TestBridgerBridgeExists(c *gc.C) { 554 np := MustNetplanFromYaml(c, ` 555 network: 556 version: 2 557 renderer: NetworkManager 558 ethernets: 559 id0: 560 match: 561 macaddress: "00:11:22:33:44:55" 562 addresses: 563 - 1.2.3.4/24 564 - 2000::1/64 565 gateway4: 1.2.3.5 566 gateway6: 2000::2 567 nameservers: 568 search: [foo.local, bar.local] 569 addresses: [8.8.8.8] 570 id1: 571 match: 572 driver: ixgbe 573 bridges: 574 juju-bridge: 575 interfaces: [id1] 576 addresses: 577 - 1.2.3.4/24 578 - 2000::1/64 579 gateway4: 1.2.3.5 580 gateway6: 2000::2 581 nameservers: 582 search: [foo.local, bar.local] 583 addresses: [8.8.8.8] 584 `) 585 err := np.BridgeEthernetById("id0", "juju-bridge") 586 c.Check(err, gc.ErrorMatches, `cannot create bridge "juju-bridge" with device "id0" - bridge "juju-bridge" w/ interfaces "id1" already exists`) 587 } 588 589 func (s *NetplanSuite) TestBridgerDeviceBridged(c *gc.C) { 590 np := MustNetplanFromYaml(c, ` 591 network: 592 version: 2 593 renderer: NetworkManager 594 ethernets: 595 id0: 596 match: 597 macaddress: "00:11:22:33:44:55" 598 addresses: 599 - 1.2.3.4/24 600 - 2000::1/64 601 gateway4: 1.2.3.5 602 gateway6: 2000::2 603 nameservers: 604 search: [foo.local, bar.local] 605 addresses: [8.8.8.8] 606 bridges: 607 not-juju-bridge: 608 interfaces: [id0] 609 addresses: 610 - 1.2.3.4/24 611 - 2000::1/64 612 gateway4: 1.2.3.5 613 gateway6: 2000::2 614 nameservers: 615 search: [foo.local, bar.local] 616 addresses: [8.8.8.8] 617 `) 618 err := np.BridgeEthernetById("id0", "juju-bridge") 619 c.Check(err, gc.ErrorMatches, `cannot create bridge "juju-bridge", device "id0" in bridge "not-juju-bridge" already exists`) 620 } 621 622 func (s *NetplanSuite) TestBridgerEthernetMissing(c *gc.C) { 623 np := MustNetplanFromYaml(c, ` 624 network: 625 version: 2 626 renderer: NetworkManager 627 ethernets: 628 id0: 629 match: 630 macaddress: "00:11:22:33:44:55" 631 bridges: 632 not-juju-bridge: 633 interfaces: [id0] 634 addresses: 635 - 1.2.3.4/24 636 - 2000::1/64 637 gateway4: 1.2.3.5 638 gateway6: 2000::2 639 nameservers: 640 search: [foo.local, bar.local] 641 addresses: [8.8.8.8] 642 `) 643 err := np.BridgeEthernetById("id7", "juju-bridge") 644 c.Check(err, gc.ErrorMatches, `ethernet device with id "id7" for bridge "juju-bridge" not found`) 645 c.Check(err, jc.Satisfies, errors.IsNotFound) 646 } 647 648 func (s *NetplanSuite) TestBridgeVLAN(c *gc.C) { 649 np := MustNetplanFromYaml(c, ` 650 network: 651 version: 2 652 renderer: NetworkManager 653 ethernets: 654 id0: 655 match: 656 macaddress: "00:11:22:33:44:55" 657 vlans: 658 id0.1234: 659 link: id0 660 id: 1234 661 addresses: 662 - 1.2.3.4/24 663 - 2000::1/64 664 gateway4: 1.2.3.5 665 gateway6: 2000::2 666 macaddress: "00:11:22:33:44:55" 667 nameservers: 668 search: [foo.local, bar.local] 669 addresses: [8.8.8.8] 670 routes: 671 - to: 100.0.0.0/8 672 via: 1.2.3.10 673 metric: 5 674 `) 675 expected := ` 676 network: 677 version: 2 678 renderer: NetworkManager 679 ethernets: 680 id0: 681 match: 682 macaddress: "00:11:22:33:44:55" 683 bridges: 684 br-id0.1234: 685 interfaces: [id0.1234] 686 addresses: 687 - 1.2.3.4/24 688 - 2000::1/64 689 gateway4: 1.2.3.5 690 gateway6: 2000::2 691 nameservers: 692 search: [foo.local, bar.local] 693 addresses: [8.8.8.8] 694 macaddress: "00:11:22:33:44:55" 695 routes: 696 - to: 100.0.0.0/8 697 via: 1.2.3.10 698 metric: 5 699 vlans: 700 id0.1234: 701 id: 1234 702 link: id0 703 `[1:] 704 err := np.BridgeVLANById("id0.1234", "br-id0.1234") 705 c.Assert(err, jc.ErrorIsNil) 706 707 out, err := netplan.Marshal(np) 708 c.Assert(err, jc.ErrorIsNil) 709 c.Check(string(out), gc.Equals, expected) 710 } 711 712 func (s *NetplanSuite) TestBridgerVLANMissing(c *gc.C) { 713 np := MustNetplanFromYaml(c, ` 714 network: 715 version: 2 716 renderer: NetworkManager 717 ethernets: 718 id0: 719 match: 720 macaddress: "00:11:22:33:44:55" 721 vlans: 722 id0.1234: 723 link: id0 724 id: 1234 725 bridges: 726 not-juju-bridge: 727 interfaces: [id0] 728 addresses: 729 - 1.2.3.4/24 730 - 2000::1/64 731 gateway4: 1.2.3.5 732 gateway6: 2000::2 733 nameservers: 734 search: [foo.local, bar.local] 735 addresses: [8.8.8.8] 736 `) 737 err := np.BridgeVLANById("id0.1235", "br-id0.1235") 738 c.Check(err, gc.ErrorMatches, `VLAN device with id "id0.1235" for bridge "br-id0.1235" not found`) 739 c.Check(err, jc.Satisfies, errors.IsNotFound) 740 } 741 742 func (s *NetplanSuite) TestBridgeVLANAndLinkedDevice(c *gc.C) { 743 np := MustNetplanFromYaml(c, ` 744 network: 745 version: 2 746 renderer: NetworkManager 747 ethernets: 748 id0: 749 match: 750 macaddress: "00:11:22:33:44:55" 751 addresses: 752 - 2.3.4.5/24 753 macaddress: "00:11:22:33:44:55" 754 vlans: 755 id0.1234: 756 link: id0 757 id: 1234 758 addresses: 759 - 1.2.3.4/24 760 - 2000::1/64 761 gateway4: 1.2.3.5 762 gateway6: 2000::2 763 macaddress: "00:11:22:33:44:55" 764 nameservers: 765 search: [foo.local, bar.local] 766 addresses: [8.8.8.8] 767 routes: 768 - to: 100.0.0.0/8 769 via: 1.2.3.10 770 metric: 5 771 `) 772 expected := ` 773 network: 774 version: 2 775 renderer: NetworkManager 776 ethernets: 777 id0: 778 match: 779 macaddress: "00:11:22:33:44:55" 780 bridges: 781 br-id0: 782 interfaces: [id0] 783 addresses: 784 - 2.3.4.5/24 785 macaddress: "00:11:22:33:44:55" 786 br-id0.1234: 787 interfaces: [id0.1234] 788 addresses: 789 - 1.2.3.4/24 790 - 2000::1/64 791 gateway4: 1.2.3.5 792 gateway6: 2000::2 793 nameservers: 794 search: [foo.local, bar.local] 795 addresses: [8.8.8.8] 796 macaddress: "00:11:22:33:44:55" 797 routes: 798 - to: 100.0.0.0/8 799 via: 1.2.3.10 800 metric: 5 801 vlans: 802 id0.1234: 803 id: 1234 804 link: id0 805 `[1:] 806 err := np.BridgeEthernetById("id0", "br-id0") 807 c.Assert(err, jc.ErrorIsNil) 808 err = np.BridgeVLANById("id0.1234", "br-id0.1234") 809 c.Assert(err, jc.ErrorIsNil) 810 811 out, err := netplan.Marshal(np) 812 c.Assert(err, jc.ErrorIsNil) 813 c.Check(string(out), gc.Equals, expected) 814 } 815 816 func (s *NetplanSuite) TestBridgeBond(c *gc.C) { 817 np := MustNetplanFromYaml(c, ` 818 network: 819 version: 2 820 renderer: NetworkManager 821 ethernets: 822 id0: 823 match: 824 macaddress: de:ad:22:33:44:55 825 id1: 826 match: 827 macaddress: de:ad:22:33:44:66 828 bonds: 829 bond0: 830 interfaces: [id0, id1] 831 addresses: 832 - 1.2.3.4/24 833 - 2000::1/64 834 gateway4: 1.2.3.5 835 gateway6: 2000::2 836 nameservers: 837 search: [foo.local, bar.local] 838 addresses: [8.8.8.8] 839 routes: 840 - to: 100.0.0.0/8 841 via: 1.2.3.10 842 metric: 5 843 parameters: 844 lacp-rate: fast 845 `) 846 expected := ` 847 network: 848 version: 2 849 renderer: NetworkManager 850 ethernets: 851 id0: 852 match: 853 macaddress: de:ad:22:33:44:55 854 id1: 855 match: 856 macaddress: de:ad:22:33:44:66 857 bridges: 858 br-bond0: 859 interfaces: [bond0] 860 addresses: 861 - 1.2.3.4/24 862 - 2000::1/64 863 gateway4: 1.2.3.5 864 gateway6: 2000::2 865 nameservers: 866 search: [foo.local, bar.local] 867 addresses: [8.8.8.8] 868 routes: 869 - to: 100.0.0.0/8 870 via: 1.2.3.10 871 metric: 5 872 bonds: 873 bond0: 874 interfaces: [id0, id1] 875 parameters: 876 lacp-rate: fast 877 `[1:] 878 err := np.BridgeBondById("bond0", "br-bond0") 879 c.Assert(err, jc.ErrorIsNil) 880 881 out, err := netplan.Marshal(np) 882 c.Assert(err, jc.ErrorIsNil) 883 c.Check(string(out), gc.Equals, expected) 884 } 885 886 func (s *NetplanSuite) TestBridgerBondMissing(c *gc.C) { 887 np := MustNetplanFromYaml(c, ` 888 network: 889 version: 2 890 renderer: NetworkManager 891 ethernets: 892 id0: 893 match: 894 macaddress: "00:11:22:33:44:55" 895 id1: 896 match: 897 macaddress: "00:11:22:33:44:66" 898 vlans: 899 id0.1234: 900 link: id0 901 id: 1234 902 bonds: 903 bond0: 904 interfaces: [id0, id1] 905 bridges: 906 not-juju-bridge: 907 interfaces: [bond0] 908 addresses: 909 - 1.2.3.4/24 910 - 2000::1/64 911 gateway4: 1.2.3.5 912 gateway6: 2000::2 913 nameservers: 914 search: [foo.local, bar.local] 915 addresses: [8.8.8.8] 916 `) 917 err := np.BridgeBondById("bond1", "br-bond1") 918 c.Check(err, gc.ErrorMatches, `bond device with id "bond1" for bridge "br-bond1" not found`) 919 c.Check(err, jc.Satisfies, errors.IsNotFound) 920 } 921 922 func (s *NetplanSuite) TestFindEthernetByName(c *gc.C) { 923 np := MustNetplanFromYaml(c, ` 924 network: 925 version: 2 926 renderer: NetworkManager 927 ethernets: 928 id0: 929 match: 930 macaddress: "00:11:22:33:44:55" 931 addresses: 932 - 1.2.3.4/24 933 - 2000::1/64 934 gateway4: 1.2.3.5 935 gateway6: 2000::2 936 set-name: eno1 937 nameservers: 938 search: [foo.local, bar.local] 939 addresses: [8.8.8.8] 940 id1: 941 match: 942 macaddress: "00:11:22:33:44:66" 943 name: en*3 944 addresses: 945 - 1.2.4.4/24 946 - 2001::1/64 947 gateway4: 1.2.4.5 948 gateway6: 2001::2 949 nameservers: 950 search: [baz.local] 951 addresses: [8.8.4.4] 952 eno7: 953 addresses: 954 - 3.4.5.6/24 955 `) 956 device, err := np.FindEthernetByName("eno1") 957 c.Assert(err, jc.ErrorIsNil) 958 c.Check(device, gc.Equals, "id0") 959 960 device, err = np.FindEthernetByName("eno3") 961 c.Assert(err, jc.ErrorIsNil) 962 c.Check(device, gc.Equals, "id1") 963 964 device, err = np.FindEthernetByName("eno7") 965 c.Assert(err, jc.ErrorIsNil) 966 c.Check(device, gc.Equals, "eno7") 967 968 device, err = np.FindEthernetByName("eno5") 969 c.Check(err, gc.ErrorMatches, "Ethernet device with name \"eno5\" not found") 970 c.Check(err, jc.Satisfies, errors.IsNotFound) 971 } 972 973 func (s *NetplanSuite) TestFindEthernetByMAC(c *gc.C) { 974 np := MustNetplanFromYaml(c, ` 975 network: 976 version: 2 977 renderer: NetworkManager 978 ethernets: 979 id0: 980 match: 981 macaddress: "00:11:22:33:44:55" 982 addresses: 983 - 1.2.3.4/24 984 - 2000::1/64 985 gateway4: 1.2.3.5 986 gateway6: 2000::2 987 set-name: eno1 988 nameservers: 989 search: [foo.local, bar.local] 990 addresses: [8.8.8.8] 991 id1: 992 match: 993 macaddress: "00:11:22:33:44:66" 994 addresses: 995 - 1.2.4.4/24 996 - 2001::1/64 997 gateway4: 1.2.4.5 998 gateway6: 2001::2 999 nameservers: 1000 search: [baz.local] 1001 addresses: [8.8.4.4] 1002 id2: 1003 addresses: 1004 - 2.3.4.5/24 1005 macaddress: 00:11:22:33:44:77 1006 `) 1007 device, err := np.FindEthernetByMAC("00:11:22:33:44:66") 1008 c.Assert(err, jc.ErrorIsNil) 1009 c.Check(device, gc.Equals, "id1") 1010 1011 device, err = np.FindEthernetByMAC("00:11:22:33:44:88") 1012 c.Check(err, gc.ErrorMatches, "Ethernet device with MAC \"00:11:22:33:44:88\" not found") 1013 c.Check(err, jc.Satisfies, errors.IsNotFound) 1014 1015 device, err = np.FindEthernetByMAC("00:11:22:33:44:77") 1016 c.Assert(err, jc.ErrorIsNil) 1017 c.Check(device, gc.Equals, "id2") 1018 } 1019 1020 func (s *NetplanSuite) TestFindVLANByName(c *gc.C) { 1021 input := ` 1022 network: 1023 version: 2 1024 renderer: NetworkManager 1025 ethernets: 1026 id0: 1027 match: 1028 macaddress: "00:11:22:33:44:55" 1029 addresses: 1030 - 1.2.3.4/24 1031 - 2000::1/64 1032 gateway4: 1.2.3.5 1033 gateway6: 2000::2 1034 set-name: eno1 1035 nameservers: 1036 search: [foo.local, bar.local] 1037 addresses: [8.8.8.8] 1038 vlans: 1039 id0.123: 1040 link: id0 1041 addresses: 1042 - 2.3.4.5/24 1043 `[1:] 1044 var np netplan.Netplan 1045 err := netplan.Unmarshal([]byte(input), &np) 1046 c.Assert(err, jc.ErrorIsNil) 1047 1048 device, err := np.FindVLANByName("id0.123") 1049 c.Assert(err, jc.ErrorIsNil) 1050 c.Check(device, gc.Equals, "id0.123") 1051 1052 device, err = np.FindVLANByName("id0") 1053 c.Check(err, gc.ErrorMatches, "VLAN device with name \"id0\" not found") 1054 c.Check(err, jc.Satisfies, errors.IsNotFound) 1055 } 1056 1057 func (s *NetplanSuite) TestFindVLANByMAC(c *gc.C) { 1058 input := ` 1059 network: 1060 version: 2 1061 renderer: NetworkManager 1062 ethernets: 1063 id0: 1064 match: 1065 macaddress: "00:11:22:33:44:55" 1066 addresses: 1067 - 1.2.3.4/24 1068 - 2000::1/64 1069 gateway4: 1.2.3.5 1070 gateway6: 2000::2 1071 set-name: eno1 1072 nameservers: 1073 search: [foo.local, bar.local] 1074 addresses: [8.8.8.8] 1075 vlans: 1076 id0.123: 1077 id: 123 1078 link: id0 1079 addresses: 1080 - 2.3.4.5/24 1081 macaddress: 00:11:22:33:44:77 1082 `[1:] 1083 var np netplan.Netplan 1084 err := netplan.Unmarshal([]byte(input), &np) 1085 c.Assert(err, jc.ErrorIsNil) 1086 1087 device, err := np.FindVLANByMAC("00:11:22:33:44:77") 1088 c.Assert(err, jc.ErrorIsNil) 1089 c.Check(device, gc.Equals, "id0.123") 1090 1091 // This is an Ethernet, not a VLAN 1092 device, err = np.FindVLANByMAC("00:11:22:33:44:55") 1093 c.Check(err, gc.ErrorMatches, `VLAN device with MAC "00:11:22:33:44:55" not found`) 1094 c.Check(err, jc.Satisfies, errors.IsNotFound) 1095 } 1096 1097 func (s *NetplanSuite) TestFindBondByName(c *gc.C) { 1098 input := ` 1099 network: 1100 version: 2 1101 renderer: NetworkManager 1102 ethernets: 1103 eno1: 1104 match: 1105 macaddress: "00:11:22:33:44:55" 1106 set-name: eno1 1107 eno2: 1108 match: 1109 macaddress: "00:11:22:33:44:66" 1110 set-name: eno2 1111 eno3: 1112 match: 1113 macaddress: "00:11:22:33:44:77" 1114 set-name: eno3 1115 eno4: 1116 match: 1117 macaddress: "00:11:22:33:44:88" 1118 set-name: eno4 1119 bonds: 1120 bond0: 1121 interfaces: [eno1, eno2] 1122 bond1: 1123 interfaces: [eno3, eno4] 1124 macaddress: "00:11:22:33:44:77" 1125 parameters: 1126 primary: eno3 1127 `[1:] 1128 var np netplan.Netplan 1129 err := netplan.Unmarshal([]byte(input), &np) 1130 c.Assert(err, jc.ErrorIsNil) 1131 1132 device, err := np.FindBondByName("bond0") 1133 c.Assert(err, jc.ErrorIsNil) 1134 c.Check(device, gc.Equals, "bond0") 1135 1136 device, err = np.FindBondByName("bond1") 1137 c.Assert(err, jc.ErrorIsNil) 1138 c.Check(device, gc.Equals, "bond1") 1139 1140 device, err = np.FindBondByName("bond3") 1141 c.Check(err, gc.ErrorMatches, "bond device with name \"bond3\" not found") 1142 c.Check(err, jc.Satisfies, errors.IsNotFound) 1143 1144 // eno4 is an Ethernet, not a Bond 1145 device, err = np.FindBondByName("eno4") 1146 c.Check(err, gc.ErrorMatches, "bond device with name \"eno4\" not found") 1147 c.Check(err, jc.Satisfies, errors.IsNotFound) 1148 } 1149 1150 func (s *NetplanSuite) TestFindBondByMAC(c *gc.C) { 1151 input := ` 1152 network: 1153 version: 2 1154 renderer: NetworkManager 1155 ethernets: 1156 eno1: 1157 match: 1158 macaddress: "00:11:22:33:44:55" 1159 set-name: eno1 1160 eno2: 1161 match: 1162 macaddress: "00:11:22:33:44:66" 1163 set-name: eno2 1164 eno3: 1165 match: 1166 macaddress: "00:11:22:33:44:77" 1167 set-name: eno3 1168 eno4: 1169 match: 1170 macaddress: "00:11:22:33:44:88" 1171 set-name: eno4 1172 bonds: 1173 bond0: 1174 interfaces: [eno1, eno2] 1175 bond1: 1176 interfaces: [eno3, eno4] 1177 macaddress: "00:11:22:33:44:77" 1178 parameters: 1179 primary: eno3 1180 `[1:] 1181 var np netplan.Netplan 1182 err := netplan.Unmarshal([]byte(input), &np) 1183 c.Assert(err, jc.ErrorIsNil) 1184 1185 device, err := np.FindBondByMAC("00:11:22:33:44:77") 1186 c.Assert(err, jc.ErrorIsNil) 1187 c.Check(device, gc.Equals, "bond1") 1188 1189 device, err = np.FindBondByMAC("00:11:22:33:44:99") 1190 c.Check(err, gc.ErrorMatches, `bond device with MAC "00:11:22:33:44:99" not found`) 1191 c.Check(err, jc.Satisfies, errors.IsNotFound) 1192 1193 // This is an Ethernet, not a Bond 1194 device, err = np.FindBondByMAC("00:11:22:33:44:55") 1195 c.Check(err, gc.ErrorMatches, `bond device with MAC "00:11:22:33:44:55" not found`) 1196 c.Check(err, jc.Satisfies, errors.IsNotFound) 1197 } 1198 1199 func checkFindDevice(c *gc.C, np *netplan.Netplan, name, mac, device string, dtype netplan.DeviceType, expErr string) { 1200 foundDev, foundType, foundErr := np.FindDeviceByNameOrMAC(name, mac) 1201 if expErr != "" { 1202 c.Check(foundErr, gc.ErrorMatches, expErr) 1203 c.Check(foundErr, jc.Satisfies, errors.IsNotFound) 1204 } else { 1205 c.Assert(foundErr, jc.ErrorIsNil) 1206 c.Check(foundDev, gc.Equals, device) 1207 c.Check(foundType, gc.Equals, dtype) 1208 } 1209 } 1210 1211 func (s *NetplanSuite) TestFindDeviceByNameOrMAC(c *gc.C) { 1212 np := MustNetplanFromYaml(c, ` 1213 network: 1214 version: 2 1215 renderer: NetworkManager 1216 ethernets: 1217 id0: 1218 match: 1219 macaddress: "00:11:22:33:44:55" 1220 set-name: id0 1221 id1: 1222 match: 1223 macaddress: de:ad:be:ef:01:02 1224 set-name: id1 1225 eno3: 1226 match: 1227 macaddress: de:ad:be:ef:01:03 1228 set-name: eno3 1229 bonds: 1230 bond0: 1231 interfaces: [id0, id1] 1232 parameters: 1233 mode: 802.3ad 1234 lacp-rate: fast 1235 mii-monitor-interval: 100 1236 transmit-hash-policy: layer2 1237 up-delay: 0 1238 down-delay: 0 1239 vlans: 1240 bond0.209: 1241 id: 209 1242 link: bond0 1243 addresses: 1244 - 123.123.123.123/24 1245 nameservers: 1246 addresses: [8.8.8.8] 1247 eno3.123: 1248 id: 123 1249 link: eno3 1250 macaddress: de:ad:be:ef:01:03 1251 `) 1252 checkFindDevice(c, np, "missing", "", "missing", "", 1253 `device - name "missing" MAC "" not found`) 1254 checkFindDevice(c, np, "missing", "dd:ee:ff:00:11:22", "missing", "", 1255 `device - name "missing" MAC "dd:ee:ff:00:11:22" not found`) 1256 checkFindDevice(c, np, "", "dd:ee:ff:00:11:22", "missing", "", 1257 `device - name "" MAC "dd:ee:ff:00:11:22" not found`) 1258 checkFindDevice(c, np, "eno3", "", "eno3", netplan.TypeEthernet, "") 1259 checkFindDevice(c, np, "eno3", "de:ad:be:ef:01:03", "eno3", netplan.TypeEthernet, "") 1260 checkFindDevice(c, np, "bond0", "", "bond0", netplan.TypeBond, "") 1261 checkFindDevice(c, np, "bond0.209", "", "bond0.209", netplan.TypeVLAN, "") 1262 checkFindDevice(c, np, "eno3.123", "de:ad:be:ef:01:03", "eno3.123", netplan.TypeVLAN, "") 1263 checkFindDevice(c, np, "", "de:ad:be:ef:01:03", "eno3.123", netplan.TypeVLAN, "") 1264 } 1265 1266 func (s *NetplanSuite) TestReadDirectory(c *gc.C) { 1267 c.Skip("Full netplan merge not supported yet, see https://bugs.launchpad.net/juju/+bug/1701429") 1268 expected := ` 1269 network: 1270 version: 2 1271 renderer: NetworkManager 1272 ethernets: 1273 id0: 1274 match: 1275 macaddress: "00:11:22:33:44:55" 1276 set-name: eno1 1277 addresses: 1278 - 1.2.3.4/24 1279 - 2000::1/64 1280 gateway4: 1.2.3.8 1281 gateway6: 2000::2 1282 nameservers: 1283 search: [foo.local, bar.local] 1284 addresses: [8.8.8.8] 1285 id1: 1286 match: 1287 macaddress: "00:11:22:33:44:66" 1288 addresses: 1289 - 1.2.4.4/24 1290 - 2001::1/64 1291 gateway4: 1.2.4.5 1292 gateway6: 2001::2 1293 nameservers: 1294 search: [baz.local] 1295 addresses: [8.8.4.4] 1296 id2: 1297 match: 1298 driver: iwldvm 1299 bridges: 1300 some-bridge: 1301 interfaces: [id2] 1302 addresses: 1303 - 1.5.6.7/24 1304 `[1:] 1305 np, err := netplan.ReadDirectory("testdata/TestReadDirectory") 1306 c.Assert(err, jc.ErrorIsNil) 1307 1308 out, err := netplan.Marshal(&np) 1309 c.Assert(err, jc.ErrorIsNil) 1310 c.Check(string(out), gc.Equals, expected) 1311 } 1312 1313 // TODO(wpk) 2017-06-14 This test checks broken behaviour, it should be removed when TestReadDirectory passes. 1314 // see https://bugs.launchpad.net/juju/+bug/1701429 1315 func (s *NetplanSuite) TestReadDirectoryWithoutProperMerge(c *gc.C) { 1316 expected := ` 1317 network: 1318 version: 2 1319 renderer: NetworkManager 1320 ethernets: 1321 id0: 1322 gateway4: 1.2.3.8 1323 id1: 1324 match: 1325 macaddress: 00:11:22:33:44:66 1326 addresses: 1327 - 1.2.4.4/24 1328 - 2001::1/64 1329 gateway4: 1.2.4.5 1330 gateway6: 2001::2 1331 nameservers: 1332 search: [baz.local] 1333 addresses: [8.8.4.4] 1334 id2: 1335 match: 1336 driver: iwldvm 1337 set-name: eno3 1338 bridges: 1339 some-bridge: 1340 interfaces: [id2] 1341 addresses: 1342 - 1.5.6.7/24 1343 `[1:] 1344 np, err := netplan.ReadDirectory("testdata/TestReadDirectory") 1345 c.Assert(err, jc.ErrorIsNil) 1346 1347 out, err := netplan.Marshal(&np) 1348 c.Assert(err, jc.ErrorIsNil) 1349 c.Check(string(out), gc.Equals, expected) 1350 } 1351 1352 func (s *NetplanSuite) TestReadWriteBackupRollback(c *gc.C) { 1353 expected := ` 1354 network: 1355 version: 2 1356 renderer: NetworkManager 1357 ethernets: 1358 eno1: 1359 match: 1360 macaddress: "00:11:22:33:44:55" 1361 set-name: eno1 1362 id1: 1363 match: 1364 macaddress: 00:11:22:33:44:66 1365 addresses: 1366 - 1.2.4.4/24 1367 - 2001::1/64 1368 gateway4: 1.2.4.5 1369 gateway6: 2001::2 1370 nameservers: 1371 search: [baz.local] 1372 addresses: [8.8.4.4] 1373 id2: 1374 match: 1375 driver: iwldvm 1376 bridges: 1377 juju-bridge: 1378 interfaces: [eno1] 1379 addresses: 1380 - 1.2.3.4/24 1381 - 2000::1/64 1382 gateway4: 1.2.3.5 1383 gateway6: 2000::2 1384 nameservers: 1385 search: [foo.local, bar.local] 1386 addresses: [8.8.8.8] 1387 some-bridge: 1388 interfaces: [id2] 1389 addresses: 1390 - 1.5.6.7/24 1391 vlans: 1392 eno1.123: 1393 id: 123 1394 link: eno1 1395 macaddress: "00:11:22:33:44:55" 1396 `[1:] 1397 tempDir := c.MkDir() 1398 files := []string{"00.yaml", "01.yaml"} 1399 contents := make([][]byte, len(files)) 1400 for i, file := range files { 1401 var err error 1402 contents[i], err = ioutil.ReadFile(path.Join("testdata/TestReadWriteBackup", file)) 1403 c.Assert(err, jc.ErrorIsNil) 1404 err = ioutil.WriteFile(path.Join(tempDir, file), contents[i], 0644) 1405 c.Assert(err, jc.ErrorIsNil) 1406 } 1407 np, err := netplan.ReadDirectory(tempDir) 1408 c.Assert(err, jc.ErrorIsNil) 1409 1410 err = np.BridgeEthernetById("eno1", "juju-bridge") 1411 c.Assert(err, jc.ErrorIsNil) 1412 1413 generatedFile, err := np.Write("") 1414 c.Assert(err, jc.ErrorIsNil) 1415 1416 _, err = np.Write("") 1417 c.Check(err, gc.ErrorMatches, "Cannot write the same netplan twice") 1418 1419 err = np.MoveYamlsToBak() 1420 c.Assert(err, jc.ErrorIsNil) 1421 1422 err = np.MoveYamlsToBak() 1423 c.Check(err, gc.ErrorMatches, "Cannot backup netplan yamls twice") 1424 1425 fileInfos, err := ioutil.ReadDir(tempDir) 1426 c.Assert(err, jc.ErrorIsNil) 1427 c.Check(fileInfos, gc.HasLen, len(files)+1) 1428 for _, fileInfo := range fileInfos { 1429 for i, fileName := range files { 1430 // original file is moved to backup 1431 c.Check(fileInfo.Name(), gc.Not(gc.Equals), fileName) 1432 // backup file has the proper content 1433 if strings.HasPrefix(fileInfo.Name(), fmt.Sprintf("%s.bak.", fileName)) { 1434 data, err := ioutil.ReadFile(path.Join(tempDir, fileInfo.Name())) 1435 c.Assert(err, jc.ErrorIsNil) 1436 c.Check(data, gc.DeepEquals, contents[i]) 1437 } 1438 } 1439 } 1440 1441 data, err := ioutil.ReadFile(generatedFile) 1442 c.Check(string(data), gc.Equals, expected) 1443 1444 err = np.Rollback() 1445 c.Assert(err, jc.ErrorIsNil) 1446 1447 fileInfos, err = ioutil.ReadDir(tempDir) 1448 c.Assert(err, jc.ErrorIsNil) 1449 c.Check(fileInfos, gc.HasLen, len(files)) 1450 foundFiles := 0 1451 for _, fileInfo := range fileInfos { 1452 for i, fileName := range files { 1453 if fileInfo.Name() == fileName { 1454 data, err := ioutil.ReadFile(path.Join(tempDir, fileInfo.Name())) 1455 c.Assert(err, jc.ErrorIsNil) 1456 c.Check(data, gc.DeepEquals, contents[i]) 1457 foundFiles++ 1458 } 1459 } 1460 } 1461 c.Check(foundFiles, gc.Equals, len(files)) 1462 1463 // After rollback we should be able to write and move yamls to backup again 1464 // We also check if writing to an explicit file works 1465 myPath := path.Join(tempDir, "my-own-path.yaml") 1466 outPath, err := np.Write(myPath) 1467 c.Assert(err, jc.ErrorIsNil) 1468 c.Check(outPath, gc.Equals, myPath) 1469 data, err = ioutil.ReadFile(outPath) 1470 c.Check(string(data), gc.Equals, expected) 1471 1472 err = np.MoveYamlsToBak() 1473 c.Assert(err, jc.ErrorIsNil) 1474 } 1475 1476 func (s *NetplanSuite) TestReadDirectoryMissing(c *gc.C) { 1477 coretesting.SkipIfWindowsBug(c, "lp:1771077") 1478 // On Windows the error is something like: "The system cannot find the file specified" 1479 tempDir := c.MkDir() 1480 os.RemoveAll(tempDir) 1481 _, err := netplan.ReadDirectory(tempDir) 1482 c.Check(err, gc.ErrorMatches, "open .* no such file or directory") 1483 } 1484 1485 func (s *NetplanSuite) TestReadDirectoryAccessDenied(c *gc.C) { 1486 coretesting.SkipIfWindowsBug(c, "lp:1771077") 1487 tempDir := c.MkDir() 1488 err := ioutil.WriteFile(path.Join(tempDir, "00-file.yaml"), []byte("network:\n"), 00000) 1489 _, err = netplan.ReadDirectory(tempDir) 1490 c.Check(err, gc.ErrorMatches, "open .*/00-file.yaml: permission denied") 1491 } 1492 1493 func (s *NetplanSuite) TestReadDirectoryBrokenYaml(c *gc.C) { 1494 tempDir := c.MkDir() 1495 err := ioutil.WriteFile(path.Join(tempDir, "00-file.yaml"), []byte("I am not a yaml file!\nreally!\n"), 0644) 1496 _, err = netplan.ReadDirectory(tempDir) 1497 c.Check(err, gc.ErrorMatches, "yaml: unmarshal errors:\n.*") 1498 } 1499 1500 func (s *NetplanSuite) TestWritePermissionDenied(c *gc.C) { 1501 coretesting.SkipIfWindowsBug(c, "lp:1771077") 1502 tempDir := c.MkDir() 1503 np, err := netplan.ReadDirectory(tempDir) 1504 c.Assert(err, jc.ErrorIsNil) 1505 os.Chmod(tempDir, 00000) 1506 _, err = np.Write(path.Join(tempDir, "99-juju-netplan.yaml")) 1507 c.Check(err, gc.ErrorMatches, "open .* permission denied") 1508 } 1509 1510 func (s *NetplanSuite) TestWriteCantGenerateName(c *gc.C) { 1511 tempDir := c.MkDir() 1512 for i := 0; i < 100; i++ { 1513 filePath := path.Join(tempDir, fmt.Sprintf("%0.2d-juju.yaml", i)) 1514 ioutil.WriteFile(filePath, []byte{}, 0644) 1515 } 1516 np, err := netplan.ReadDirectory(tempDir) 1517 c.Assert(err, jc.ErrorIsNil) 1518 _, err = np.Write("") 1519 c.Check(err, gc.ErrorMatches, "Can't generate a filename for netplan YAML") 1520 } 1521 1522 func (s *NetplanSuite) TestProperReadingOrder(c *gc.C) { 1523 var header = ` 1524 network: 1525 version: 2 1526 renderer: NetworkManager 1527 ethernets: 1528 `[1:] 1529 var template = ` 1530 id%d: 1531 set-name: foo.%d.%d 1532 `[1:] 1533 tempDir := c.MkDir() 1534 1535 for _, n := range rand.Perm(100) { 1536 content := header 1537 for i := 0; i < (100 - n); i++ { 1538 content += fmt.Sprintf(template, i, i, n) 1539 } 1540 ioutil.WriteFile(path.Join(tempDir, fmt.Sprintf("%0.2d-test.yaml", n)), []byte(content), 0644) 1541 } 1542 1543 np, err := netplan.ReadDirectory(tempDir) 1544 c.Assert(err, jc.ErrorIsNil) 1545 1546 fileName, err := np.Write("") 1547 1548 writtenContent, err := ioutil.ReadFile(fileName) 1549 c.Assert(err, jc.ErrorIsNil) 1550 1551 content := header 1552 for n := 0; n < 100; n++ { 1553 content += fmt.Sprintf(template, n, n, 100-n-1) 1554 } 1555 c.Check(string(writtenContent), gc.Equals, content) 1556 } 1557 1558 type Example struct { 1559 filename string 1560 content string 1561 } 1562 1563 func readExampleStrings(c *gc.C) []Example { 1564 fileInfos, err := ioutil.ReadDir("testdata/examples") 1565 c.Assert(err, jc.ErrorIsNil) 1566 var examples []Example 1567 for _, finfo := range fileInfos { 1568 if finfo.IsDir() { 1569 continue 1570 } 1571 if strings.HasSuffix(finfo.Name(), ".yaml") { 1572 f, err := os.Open("testdata/examples/" + finfo.Name()) 1573 c.Assert(err, jc.ErrorIsNil) 1574 content, err := ioutil.ReadAll(f) 1575 f.Close() 1576 c.Assert(err, jc.ErrorIsNil) 1577 examples = append(examples, Example{ 1578 filename: finfo.Name(), 1579 content: string(content), 1580 }) 1581 } 1582 } 1583 // Make sure we find all the example files, if we change the count, update this number, but we don't allow the test 1584 // suite to find the wrong number of files. 1585 c.Assert(len(examples), gc.Equals, 13) 1586 return examples 1587 } 1588 1589 func (s *NetplanSuite) TestNetplanExamples(c *gc.C) { 1590 // these are the examples shipped by netplan, we should be able to read all of them 1591 examples := readExampleStrings(c) 1592 for _, example := range examples { 1593 c.Logf("example: %s", example.filename) 1594 var orig map[interface{}]interface{} 1595 err := yaml.UnmarshalStrict([]byte(example.content), &orig) 1596 c.Assert(err, jc.ErrorIsNil, gc.Commentf("failed to unmarshal as map %s", example.filename)) 1597 var np netplan.Netplan 1598 err = netplan.Unmarshal([]byte(example.content), &np) 1599 c.Check(err, jc.ErrorIsNil, gc.Commentf("failed to unmarshal %s", example.filename)) 1600 // We don't assert that we exactly match the serialized form (we may output fields in a different order), 1601 // but we do check that if we Marshal and then Unmarshal again, we get the same map contents. 1602 // (We might also change boolean 'no' to 'false', etc. 1603 out, err := netplan.Marshal(&np) 1604 c.Check(err, jc.ErrorIsNil, gc.Commentf("failed to marshal %s", example.filename)) 1605 var roundtripped map[interface{}]interface{} 1606 err = yaml.UnmarshalStrict(out, &roundtripped) 1607 if !reflect.DeepEqual(orig, roundtripped) { 1608 pretty.Ldiff(c, orig, roundtripped) 1609 c.Errorf("marshalling and unmarshalling %s did not contain the same content", example.filename) 1610 } 1611 } 1612 }