~ubuntu-branches/ubuntu/quantal/maas/quantal-proposed

« back to all changes in this revision

Viewing changes to src/provisioningserver/tests/test_api.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-04-17 23:44:46 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20120417234446-y3212quny2x1gft8
Tags: 0.1+bzr482+dfsg-0ubuntu1
* New upstream release (Fixes LP: #981103)
* debian/maas.postinst:
  - Make sure rabbitmq and postgresql are started on upgrade (LP: #981282)
  - Handle upgrades from any lower than 0.1+bzr462+dfsg-0ubuntu1 to
    correctly re-generate passwords, and not have db sync/migrate issues
    as config has changed upstream.
  - Correctly set Passwords for PSERV, otherwise it won't set new passwords.
* Allow MAAS_DEFAULT_URL reconfiguration. (LP: #980970)
  - debian/maas.config: Add reconfigure validation to correctly allow it,
    and ask a question.
  - debian/maas.postinst: Reconfigure DEFAULT_MAAS_URL as well as cobbler
    server and next_server for PXE/Provisioning.
  - debian/maas.templates: Add debconf question and update info.
* Do not lose MAAS_DEFAULT_URL settings on upgrade (LP: #984309)
* debian/maas.postinst:
  - Set cobbler password in between quotes (LP: #984427)
  - Do not change permissions to maas.log (LP: #980915)
* no longer use maas-cloudimg2ephemeral, but rather use premade images 
  at http://maas.ubuntu.com

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
"""
8
8
 
9
9
from __future__ import (
 
10
    absolute_import,
10
11
    print_function,
11
12
    unicode_literals,
12
13
    )
19
20
    abstractmethod,
20
21
    )
21
22
from base64 import b64decode
22
 
from unittest import skipIf
 
23
from contextlib import contextmanager
23
24
 
24
25
from maasserver.testing.enum import map_enum
25
26
from maastesting.factory import factory
40
41
from provisioningserver.testing.realcobbler import RealCobbler
41
42
from testtools import TestCase
42
43
from testtools.deferredruntest import AsynchronousDeferredRunTest
 
44
from testtools.matchers import (
 
45
    FileExists,
 
46
    Not,
 
47
    )
 
48
from testtools.monkey import patch
43
49
from twisted.internet.defer import inlineCallbacks
44
50
from zope.interface.verify import verifyObject
45
51
 
607
613
        self.assertEqual(
608
614
            preseed_data, b64decode(attrs['ks_meta']['MAAS_PRESEED']))
609
615
 
 
616
    @contextmanager
 
617
    def expected_sync(self, papi, times=1):
 
618
        """Context where # calls to `papi.sync` must match `times`."""
 
619
        sync_calls = []
 
620
        orig_sync = papi.sync
 
621
        fake_sync = lambda: orig_sync().addCallback(sync_calls.append)
 
622
        unpatch = patch(papi, "sync", fake_sync)
 
623
        try:
 
624
            yield
 
625
        finally:
 
626
            unpatch()
 
627
        self.assertEqual(times, len(sync_calls))
 
628
 
 
629
    @inlineCallbacks
 
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)
 
635
 
 
636
    @inlineCallbacks
 
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)
 
643
 
 
644
    @inlineCallbacks
 
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)
 
651
 
 
652
    @inlineCallbacks
 
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({})
 
658
 
 
659
    @inlineCallbacks
 
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({})
 
665
 
 
666
    @inlineCallbacks
 
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({})
 
672
 
 
673
    @inlineCallbacks
 
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([])
 
679
 
 
680
    @inlineCallbacks
 
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([])
 
686
 
 
687
    @inlineCallbacks
 
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([])
 
693
 
610
694
 
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())
633
717
 
 
718
    def test_sync(self):
 
719
        """`ProvisioningAPI.sync` issues an authenticated ``sync`` call.
 
720
 
 
721
        It is not exported - i.e. it is not part of :class:`IProvisioningAPI`
 
722
        - but is used heavily by other methods in `IProvisioningAPI`.
 
723
        """
 
724
        papi = self.get_provisioning_api()
 
725
        calls = []
 
726
        self.patch(
 
727
            papi.session, "call",
 
728
            lambda *args: calls.append(args))
 
729
        papi.sync()
 
730
        self.assertEqual(
 
731
            [("sync", papi.session.token_placeholder)],
 
732
            calls)
 
733
 
634
734
 
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.
642
742
 
643
 
    Includes by inheritance all the tests in :class:`ProvisioningAPITests`.
 
743
    Includes by inheritance all the tests in :class:`ProvisioningAPITests` and
 
744
    :class:`ProvisioningAPITestsWithCobbler`.
644
745
    """
645
746
 
646
747
    real_cobbler = RealCobbler()
647
748
 
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())
 
753
 
 
754
    @real_cobbler.skip_unless_local
 
755
    @inlineCallbacks
 
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()))