~hazmat/pyjuju/proposed-support

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from twisted.internet.defer import fail, inlineCallbacks, returnValue

from juju.errors import ProviderError

from .cloudinit import CloudInit
from .utils import get_user_authorized_keys


class LaunchMachine(object):
    """Abstract class with generic instance-launching logic.

    To create your own subclass, you will certainly need to override
    :meth:`start_machine`, which will very probably want to use the incomplete
    :class:`juju.providers.common.cloudinit.CloudInit` returned by
    :meth:`_create_cloud_init`.

    :param provider: the `MachineProvider` that will administer the machine

    :param bool master: if True, the machine will run a zookeeper and a
        provisioning agent, in addition to the machine agent that every
        machine runs automatically.

    :param dict constraints: specifies the underlying OS and hardware (where
        available)

    .. automethod:: _create_cloud_init
    """

    def __init__(self, provider, constraints, master=False):
        self._provider = provider
        self._constraints = constraints
        self._master = master

    @classmethod
    def launch(cls, provider, machine_data, master):
        """Create and run a machine launch operation.

        Exists for the convenience of the `MachineProvider` implementations
        which actually use the "constraints" key in machine_data, which would
        otherwise duplicate code.
        """
        if "machine-id" not in machine_data:
            return fail(ProviderError(
                "Cannot launch a machine without specifying a machine-id"))
        if "constraints" not in machine_data:
            return fail(ProviderError(
                "Cannot launch a machine without specifying constraints"))
        launcher = cls(provider, machine_data["constraints"], master)
        return launcher.run(machine_data["machine-id"])

    @inlineCallbacks
    def run(self, machine_id):
        """Launch an instance node within the machine provider environment.

        :param str machine_id: the juju machine ID to assign
        """
        # XXX at some point, we'll want to start multiple zookeepers
        # that know about each other; for now, this'll do
        if self._master:
            zookeepers = []
        else:
            zookeepers = yield self._provider.get_zookeeper_machines()

        machines = yield self.start_machine(machine_id, zookeepers)
        if self._master:
            yield self._on_new_zookeepers(machines)
        returnValue(machines)

    def start_machine(self, machine_id, zookeepers):
        """Actually launch a machine for the appropriate provider.

        :param str machine_id: the juju machine ID to assign

        :param zookeepers: the machines currently running zookeeper, to which
            the new machine will need to connect
        :type zookeepers: list of :class:`juju.machine.ProviderMachine`

        :return: a singe-entry list containing a provider-specific
            :class:`juju.machine.ProviderMachine` representing the newly-
            launched machine
        :rtype: :class:`twisted.internet.defer.Deferred`
        """
        raise NotImplementedError()

    def _create_cloud_init(self, machine_id, zookeepers):
        """Construct a provider-independent but incomplete :class:`CloudInit`.

        :return: a :class:`juju.providers.common.cloudinit.cloudInit`; it
            will not be ready to render, but will be configured to the greatest
            extent possible with the available information.
        :rtype: :class:`twisted.internet.defer.Deferred`
        """
        config = self._provider.config
        cloud_init = CloudInit()
        cloud_init.add_ssh_key(get_user_authorized_keys(config))
        cloud_init.set_machine_id(machine_id)
        cloud_init.set_zookeeper_machines(zookeepers)
        origin = config.get("juju-origin")
        if origin:
            if origin.startswith("lp:"):
                cloud_init.set_juju_source(branch=origin)
            elif origin == "ppa":
                cloud_init.set_juju_source(ppa=True)
            elif origin == "proposed":
                cloud_init.set_juju_source(proposed=True)
            else:
                # Ignore other values, just use the distro for sanity
                cloud_init.set_juju_source(distro=True)
        if self._master:
            cloud_init.enable_bootstrap()
            cloud_init.set_zookeeper_secret(config["admin-secret"])
            cloud_init.set_constraints(self._constraints)
        return cloud_init

    def _on_new_zookeepers(self, machines):
        instance_ids = [m.instance_id for m in machines]
        return self._provider.save_state({"zookeeper-instances": instance_ids})