607
613
self.assertEqual(
608
614
preseed_data, b64decode(attrs['ks_meta']['MAAS_PRESEED']))
617
def expected_sync(self, papi, times=1):
618
"""Context where # calls to `papi.sync` must match `times`."""
620
orig_sync = papi.sync
621
fake_sync = lambda: orig_sync().addCallback(sync_calls.append)
622
unpatch = patch(papi, "sync", fake_sync)
627
self.assertEqual(times, len(sync_calls))
630
def test_add_distro_syncs(self):
631
# add_distro ensures that Cobbler syncs.
632
papi = self.get_provisioning_api()
633
with self.expected_sync(papi):
634
yield self.add_distro(papi)
637
def test_add_profile_syncs(self):
638
# add_profile ensures that Cobbler syncs.
639
papi = self.get_provisioning_api()
640
distro_name = yield self.add_distro(papi)
641
with self.expected_sync(papi):
642
yield self.add_profile(papi, distro_name=distro_name)
645
def test_add_node_syncs(self):
646
# add_node ensures that Cobbler syncs.
647
papi = self.get_provisioning_api()
648
profile_name = yield self.add_profile(papi)
649
with self.expected_sync(papi):
650
yield self.add_node(papi, profile_name=profile_name)
653
def test_modify_distros_syncs(self):
654
# modify_distros ensures that Cobbler syncs.
655
papi = self.get_provisioning_api()
656
with self.expected_sync(papi):
657
yield papi.modify_distros({})
660
def test_modify_profiles_syncs(self):
661
# modify_profiles ensures that Cobbler syncs.
662
papi = self.get_provisioning_api()
663
with self.expected_sync(papi):
664
yield papi.modify_profiles({})
667
def test_modify_nodes_syncs(self):
668
# modify_nodes ensures that Cobbler syncs.
669
papi = self.get_provisioning_api()
670
with self.expected_sync(papi):
671
yield papi.modify_nodes({})
674
def test_delete_distros_by_name_syncs(self):
675
# delete_distros_by_name ensures that Cobbler syncs.
676
papi = self.get_provisioning_api()
677
with self.expected_sync(papi):
678
yield papi.delete_distros_by_name([])
681
def test_delete_profiles_by_name_syncs(self):
682
# delete_profiles_by_name ensures that Cobbler syncs.
683
papi = self.get_provisioning_api()
684
with self.expected_sync(papi):
685
yield papi.delete_profiles_by_name([])
688
def test_delete_nodes_by_name_syncs(self):
689
# delete_nodes_by_name ensures that Cobbler syncs.
690
papi = self.get_provisioning_api()
691
with self.expected_sync(papi):
692
yield papi.delete_nodes_by_name([])
611
695
class TestFakeProvisioningAPI(ProvisioningAPITests, TestCase):
612
696
"""Test :class:`FakeAsynchronousProvisioningAPI`.
631
715
"""Return a real ProvisioningAPI, but using a fake Cobbler session."""
632
716
return ProvisioningAPI(make_fake_cobbler_session())
719
"""`ProvisioningAPI.sync` issues an authenticated ``sync`` call.
721
It is not exported - i.e. it is not part of :class:`IProvisioningAPI`
722
- but is used heavily by other methods in `IProvisioningAPI`.
724
papi = self.get_provisioning_api()
727
papi.session, "call",
728
lambda *args: calls.append(args))
731
[("sync", papi.session.token_placeholder)],
635
735
class TestProvisioningAPIWithRealCobbler(ProvisioningAPITests,
636
736
ProvisioningAPITestsWithCobbler,
640
740
The URL for the Cobbler instance must be provided in the
641
741
`PSERV_TEST_COBBLER_URL` environment variable.
643
Includes by inheritance all the tests in :class:`ProvisioningAPITests`.
743
Includes by inheritance all the tests in :class:`ProvisioningAPITests` and
744
:class:`ProvisioningAPITestsWithCobbler`.
646
747
real_cobbler = RealCobbler()
648
@skipIf(not real_cobbler.is_available(), RealCobbler.help_text)
749
@real_cobbler.skip_unless_available
649
750
def get_provisioning_api(self):
650
751
"""Return a connected :class:`ProvisioningAPI`."""
651
752
return ProvisioningAPI(self.real_cobbler.get_session())
754
@real_cobbler.skip_unless_local
756
def test_sync_after_modify(self):
757
# When MAAS modifies the MAC addresses of a node it triggers a sync of
758
# Cobbler. This is to ensure that netboot files are up-to-date, or
759
# removed as appropriate.
760
papi = self.get_provisioning_api()
761
node_name = yield self.add_node(papi)
762
mac_address = factory.getRandomMACAddress()
763
yield papi.modify_nodes(
764
{node_name: {"mac_addresses": [mac_address]}})
765
# The PXE file corresponding to the node's MAC address is present.
766
pxe_filename = "/var/lib/tftpboot/pxelinux.cfg/01-%s" % (
767
mac_address.replace(":", "-"),)
768
self.assertThat(pxe_filename, FileExists())
769
# Remove the MAC address again.
770
yield papi.modify_nodes(
771
{node_name: {"mac_addresses": []}})
772
# The PXE file has been removed too.
773
self.assertThat(pxe_filename, Not(FileExists()))