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})
|