Merge lp:~hazmat/pyjuju/cli-placement-restore into lp:pyjuju

Proposed by Kapil Thangavelu
Status: Work in progress
Proposed branch: lp:~hazmat/pyjuju/cli-placement-restore
Merge into: lp:pyjuju
Diff against target: 450 lines (+143/-56)
12 files modified
juju/control/add_unit.py (+13/-4)
juju/control/deploy.py (+10/-4)
juju/control/tests/test_add_unit.py (+36/-0)
juju/control/tests/test_deploy.py (+33/-1)
juju/providers/common/base.py (+4/-6)
juju/providers/common/tests/test_base.py (+2/-5)
juju/providers/dummy.py (+4/-6)
juju/providers/lxc/__init__.py (+2/-5)
juju/providers/lxc/tests/test_provider.py (+2/-7)
juju/providers/tests/test_dummy.py (+4/-7)
juju/state/placement.py (+19/-0)
juju/state/tests/test_placement.py (+14/-11)
To merge this branch: bzr merge lp:~hazmat/pyjuju/cli-placement-restore
Reviewer Review Type Date Requested Status
Gustavo Niemeyer Needs Fixing
Review via email: mp+77245@code.launchpad.net

Description of the change

Restore command line placement.

There are several folks who would like to see its restoration. Both for
flexibility down the road and more immediately as mechanism to conserve
machine resources. The alternative of modifying environment config between
deploys is suboptimal. Its being used for example in reducing the number of
machines required for openstack. Although future use of ideal policies (limited
number of machines) is suboptimal, there are other placement policies taking
into account machine resources or cross availability zone for which this option
would be ideal.

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Why is the placement option in the environment suboptimal?

I'm not trying to make things difficult gratuitously, but I feel quite unhappy about the mechanism we're introducing here. This was introduced as a hack, and I'm now hearing people depending on the hack.. soon we'll see blog posts around the hack and people believing that this is the way Ensemble is supposed to work.

I understand folks want to have better control of placement. I surely want it too.. this is just not the way to do it in Ensemble, for several reasons that I'll be happy to discuss in a better forum.

If the placement option in the environment enable the Adam and Clint to do what they have to do, I'd prefer to keep it that way.

review: Needs Information
Revision history for this message
Adam Gandelman (gandelman-a) wrote :

Hi--

Moving "--placement=local" from argv to "placement: local" in environments.yaml seems trivial enough, however, my specific use case often has me dealing with many different formulas, some of which are destroyed and re-deployed many times in one sitting. Having to to parse and modify environments.yaml many times over between 'juju deploy' to switch to and from 'local' placement policy seems needlessly tedious. Having to put together my own wrappers around 'juju deploy' and environments.yaml to do this for me and emulate what '--placement=local' once provided seems even more suboptimal.

We understand its a hack but we're using it as a workaround to Bug #806241. Without it, Juju goes from something that might be useful "sometime in the future" given our hardware constraints to something that is extremely useful *now* and *central* to our QA and integration testing of specific software stacks. IMHO, making it more difficult to use due to user facing changes like this (especially this late in the cycle) would be unfortunate.

Revision history for this message
Clint Byrum (clint-fewbar) wrote :

I share your concern that --placement local will be abused if it is available. However, without this, testing scripts for hardware deployment or against limited size clouds become incredibly complex. I'd be comfortable with a WARNING: --placement on the command line is deprecated type of message once there is an alternative that allows making use of limited resources, but until then, I think we have to have at least *some* story other than "go buy more hardware".

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Alright, so here is a review to try to get to some agreement:

[1]

Let's have the renamed the option to a boolean called

    --temporary-local-hack

With the following help message:

    help="Temporary placement hack for testing juju."

[2]

Let's not change the provider interface, or introduce pick_policy,
etc. The provider interface we have today looks nice and is going
into the direction we want.

Instead, we do something as simple as this:

    placement = provider.get_placement_policy()
    if options.temporary_local_hack:
        placement = "local"

[3]

Once we sort out making our system stable for 12.04, let's come back
and do placement correctly. It's definitely something that should be
high in our priority list.

review: Needs Fixing
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

Excerpts from Gustavo Niemeyer's message of Thu Sep 29 20:48:30 UTC 2011:
> Review: Needs Fixing
>
> Alright, so here is a review to try to get to some agreement:
>
> [1]
>
> Let's have the renamed the option to a boolean called
>
> --temporary-local-hack
>
> With the following help message:
>
> help="Temporary placement hack for testing juju."

We can even hide it from the default cli help.

>
> [2]
>
> Let's not change the provider interface, or introduce pick_policy,
> etc. The provider interface we have today looks nice and is going
> into the direction we want.
>
> Instead, we do something as simple as this:
>
> placement = provider.get_placement_policy()
> if options.temporary_local_hack:
> placement = "local"
>
>
> [3]
>
> Once we sort out making our system stable for 12.04, let's come back
> and do placement correctly. It's definitely something that should be
> high in our priority list.

sounds reasonable.

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

i thought about this some more, and i think i'd rather just drop this and adopt clint's approach of giving users direct machine placement options.

Revision history for this message
Benjamin Saller (bcsaller) wrote :

I think thats just another policy, --placement machine:1

Those were always supposed to be strategy name or plans (implied by
the above) in my mind.

On Wed, Oct 5, 2011 at 7:46 AM, Kapil Thangavelu
<email address hidden> wrote:
> i thought about this some more, and i think i'd rather just drop this and adopt clint's approach of giving users direct machine placement options.
> --
> https://code.launchpad.net/~hazmat/juju/cli-placement-restore/+merge/77245
> You are subscribed to branch lp:juju.
>

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

interesting, thanks.

Excerpts from Benjamin Saller's message of Wed Oct 05 15:38:39 UTC 2011:
> I think thats just another policy, --placement machine:1
>
> Those were always supposed to be strategy name or plans (implied by
> the above) in my mind.
>
>
> On Wed, Oct 5, 2011 at 7:46 AM, Kapil Thangavelu
> <email address hidden> wrote:
> > i thought about this some more, and i think i'd rather just drop this and adopt clint's approach of giving users direct machine placement options.
> > --
> > https://code.launchpad.net/~hazmat/juju/cli-placement-restore/+merge/77245
> > You are subscribed to branch lp:juju.
> >
>

Unmerged revisions

397. By Kapil Thangavelu

restore placement cli

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'juju/control/add_unit.py'
--- juju/control/add_unit.py 2011-09-27 02:14:56 +0000
+++ juju/control/add_unit.py 2011-09-27 22:31:25 +0000
@@ -3,7 +3,7 @@
3from twisted.internet.defer import inlineCallbacks3from twisted.internet.defer import inlineCallbacks
44
5from juju.control.utils import get_environment5from juju.control.utils import get_environment
6from juju.state.placement import place_unit6from juju.state.placement import place_unit, pick_policy
7from juju.state.service import ServiceStateManager7from juju.state.service import ServiceStateManager
88
99
@@ -16,6 +16,9 @@
16 sub_parser.add_argument(16 sub_parser.add_argument(
17 "service_name",17 "service_name",
18 help="Name of the service a unit should be added for")18 help="Name of the service a unit should be added for")
19 sub_parser.add_argument(
20 "--placement",
21 help="Placement policy used to place units of this service")
1922
20 return sub_parser23 return sub_parser
2124
@@ -23,20 +26,26 @@
23def command(options):26def command(options):
24 """Add a new service unit."""27 """Add a new service unit."""
25 environment = get_environment(options)28 environment = get_environment(options)
29
30 if not options.placement:
31 options.placement = environment.placement
32
26 return add_unit(33 return add_unit(
27 options.environments,34 options.environments,
28 environment,35 environment,
29 options.verbose,36 options.verbose,
30 options.log,37 options.log,
31 options.service_name)38 options.service_name,
39 options.placement)
3240
3341
34@inlineCallbacks42@inlineCallbacks
35def add_unit(config, environment, verbose, log, service_name):43def add_unit(
44 config, environment, verbose, log, service_name, placement_policy):
36 """Add a unit of a service to the environment.45 """Add a unit of a service to the environment.
37 """46 """
38 provider = environment.get_machine_provider()47 provider = environment.get_machine_provider()
39 placement_policy = provider.get_placement_policy()48 placement_policy = pick_policy(placement_policy, provider)
40 client = yield provider.connect()49 client = yield provider.connect()
4150
42 try:51 try:
4352
=== modified file 'juju/control/deploy.py'
--- juju/control/deploy.py 2011-09-27 02:14:56 +0000
+++ juju/control/deploy.py 2011-09-27 22:31:25 +0000
@@ -12,7 +12,7 @@
1212
13from juju.state.endpoint import RelationEndpoint13from juju.state.endpoint import RelationEndpoint
14from juju.state.environment import EnvironmentStateManager14from juju.state.environment import EnvironmentStateManager
15from juju.state.placement import place_unit15from juju.state.placement import place_unit, pick_policy
16from juju.state.relation import RelationStateManager16from juju.state.relation import RelationStateManager
17from juju.state.service import ServiceStateManager17from juju.state.service import ServiceStateManager
1818
@@ -41,6 +41,10 @@
41 "--config",41 "--config",
42 help="YAML file containing service options")42 help="YAML file containing service options")
4343
44 sub_parser.add_argument(
45 "--placement",
46 help="Placement policy used to place units of this service")
47
44 return sub_parser48 return sub_parser
4549
4650
@@ -56,7 +60,8 @@
56 options.charm,60 options.charm,
57 options.service_name,61 options.service_name,
58 options.log,62 options.log,
59 options.config)63 options.config,
64 options.placement)
6065
6166
62def parse_config_options(config_file, service_name):67def parse_config_options(config_file, service_name):
@@ -77,7 +82,7 @@
7782
78@inlineCallbacks83@inlineCallbacks
79def deploy(env_config, environment, repository_path, charm_name,84def deploy(env_config, environment, repository_path, charm_name,
80 service_name, log, config_file=None):85 service_name, log, config_file=None, placement_policy=None):
81 """Deploy a charm within an environment.86 """Deploy a charm within an environment.
8287
8388
@@ -96,7 +101,7 @@
96 charm = repository.find(charm_name)101 charm = repository.find(charm_name)
97102
98 provider = environment.get_machine_provider()103 provider = environment.get_machine_provider()
99 placement_policy = provider.get_placement_policy()104 placement_policy = pick_policy(placement_policy, provider)
100 client = yield provider.connect()105 client = yield provider.connect()
101106
102 try:107 try:
@@ -133,6 +138,7 @@
133138
134 # Create a service unit139 # Create a service unit
135 unit_state = yield service_state.add_unit_state()140 unit_state = yield service_state.add_unit_state()
141
136 yield place_unit(client, placement_policy, unit_state)142 yield place_unit(client, placement_policy, unit_state)
137143
138 # Check if we have any peer relations to establish144 # Check if we have any peer relations to establish
139145
=== modified file 'juju/control/tests/test_add_unit.py'
--- juju/control/tests/test_add_unit.py 2011-09-27 02:14:56 +0000
+++ juju/control/tests/test_add_unit.py 2011-09-27 22:31:25 +0000
@@ -87,6 +87,42 @@
87 yield self.assert_machine_assignments("mysql", [1, 2])87 yield self.assert_machine_assignments("mysql", [1, 2])
8888
89 @inlineCallbacks89 @inlineCallbacks
90 def test_add_unit_with_local_policy(self):
91 """
92 'juju add-unit <service_name>' will add a new service
93 unit of the given service.
94 """
95 # rework setUp so that service_unit1 is on machine 0
96 # to simulate local deployment
97 ms0 = yield self.machine_state_manager.get_machine_state(0)
98 yield self.service_unit1.unassign_from_machine()
99 yield self.service_unit1.assign_to_machine(ms0)
100
101 unit_names = yield self.service_state1.get_unit_names()
102 self.assertEqual(len(unit_names), 1)
103
104 finished = self.setup_cli_reactor()
105 self.setup_exit(0)
106 self.mocker.replay()
107 main(["add-unit", "mysql", "--placement", "local"])
108 yield finished
109
110 # verify the unit and its machine assignment.
111 unit_names = yield self.service_state1.get_unit_names()
112 self.assertEqual(len(unit_names), 2)
113
114 topology = yield self.get_topology()
115 unit = yield self.service_state1.get_unit_state("mysql/1")
116 machine_id = topology.get_service_unit_machine(
117 self.service_state1.internal_id, unit.internal_id)
118 self.assertNotEqual(machine_id, None)
119 self.assertIn(
120 "Unit 'mysql/1' added to service 'mysql'",
121 self.output.getvalue())
122 # adding a second unit still assigns to machine 0 with local policy
123 yield self.assert_machine_assignments("mysql", [0, 0])
124
125 @inlineCallbacks
90 def test_policy_from_environment(self):126 def test_policy_from_environment(self):
91 config = {127 config = {
92 "environments": {"firstenv": {128 "environments": {"firstenv": {
93129
=== modified file 'juju/control/tests/test_deploy.py'
--- juju/control/tests/test_deploy.py 2011-09-27 02:14:56 +0000
+++ juju/control/tests/test_deploy.py 2011-09-27 22:31:25 +0000
@@ -150,7 +150,7 @@
150 command(MATCH(match_config), MATCH(match_environment),150 command(MATCH(match_config), MATCH(match_environment),
151 self.unbundled_repo_path, "sample", None,151 self.unbundled_repo_path, "sample", None,
152 MATCH(lambda x: isinstance(x, logging.Logger)),152 MATCH(lambda x: isinstance(x, logging.Logger)),
153 None)153 None, None)
154 self.mocker.replay()154 self.mocker.replay()
155 self.mocker.result(succeed(True))155 self.mocker.result(succeed(True))
156156
@@ -301,6 +301,38 @@
301 self.assertEqual(relations[0].relation_name, "ring")301 self.assertEqual(relations[0].relation_name, "ring")
302302
303 @inlineCallbacks303 @inlineCallbacks
304 def test_deploy_with_local_policy(self):
305 environment = self.config.get("firstenv")
306 yield deploy.deploy(
307 self.config, environment, self.unbundled_repo_path,
308 "sample", "myblog", logging.getLogger("deploy"),
309 placement_policy="local")
310
311 topology = yield self.get_topology()
312 service_id = topology.find_service_with_name("myblog")
313 self.assertEqual(service_id, "service-%010d" % 0)
314 exists = yield self.client.exists("/services/%s" % service_id)
315 self.assertTrue(exists)
316
317 machine_ids = topology.get_machines()
318
319 # There should just be the one machine with local deployment
320 self.assertEqual(machine_ids, ["machine-%010d" % 0])
321 exists = yield self.client.exists("/machines/%s" % machine_ids[0])
322 self.assertTrue(exists)
323
324 # Verify the process still completes as expected
325 unit_ids = topology.get_service_units(service_id)
326 self.assertEqual(unit_ids, ["unit-%010d" % 0])
327 exists = yield self.client.exists("/units/%s" % unit_ids[0])
328 self.assertTrue(exists)
329
330 service = yield self.service_state_manager.get_service_state("myblog")
331 unit_state = yield service.get_unit_state("myblog/0")
332 machine_id = yield unit_state.get_assigned_machine_id()
333 self.assertEqual(machine_id, 0)
334
335 @inlineCallbacks
304 def test_deploy_policy_from_environment(self):336 def test_deploy_policy_from_environment(self):
305 config = {337 config = {
306 "environments": {"firstenv": {338 "environments": {"firstenv": {
307339
=== modified file 'juju/providers/common/base.py'
--- juju/providers/common/base.py 2011-09-27 02:14:56 +0000
+++ juju/providers/common/base.py 2011-09-27 22:31:25 +0000
@@ -4,7 +4,7 @@
4from twisted.internet.defer import inlineCallbacks, returnValue4from twisted.internet.defer import inlineCallbacks, returnValue
55
6from juju.environment.errors import EnvironmentsConfigError6from juju.environment.errors import EnvironmentsConfigError
7from juju.state.placement import UNASSIGNED_POLICY7from juju.state.placement import list_policies
88
99
10from .bootstrap import Bootstrap10from .bootstrap import Bootstrap
@@ -48,12 +48,10 @@
48 self.environment_name = environment_name48 self.environment_name = environment_name
49 self.config = config49 self.config = config
5050
51 def get_placement_policy(self):51 def get_placement_policies(self):
52 """Get the unit placement policy for the provider.52 """Get the available unit placement policies for the provider.
53
54 :param preference: A user specified plcaement policy preference
55 """53 """
56 return self.config.get("placement", UNASSIGNED_POLICY)54 return list_policies()
5755
58 def get_serialization_data(self):56 def get_serialization_data(self):
59 """Get provider configuration suitable for serialization."""57 """Get provider configuration suitable for serialization."""
6058
=== modified file 'juju/providers/common/tests/test_base.py'
--- juju/providers/common/tests/test_base.py 2011-09-27 02:14:56 +0000
+++ juju/providers/common/tests/test_base.py 2011-09-27 22:31:25 +0000
@@ -4,7 +4,7 @@
4from juju.lib.testing import TestCase4from juju.lib.testing import TestCase
5from juju.machine import ProviderMachine5from juju.machine import ProviderMachine
6from juju.providers.common.base import MachineProviderBase6from juju.providers.common.base import MachineProviderBase
7from juju.state.placement import UNASSIGNED_POLICY7from juju.state.placement import list_policies
88
99
10class SomeError(Exception):10class SomeError(Exception):
@@ -58,10 +58,7 @@
58 def test_get_provider_placement(self):58 def test_get_provider_placement(self):
59 provider = DummyProvider()59 provider = DummyProvider()
60 self.assertEqual(60 self.assertEqual(
61 provider.get_placement_policy(), UNASSIGNED_POLICY)61 provider.get_placement_policies(), list_policies())
62 provider = DummyProvider({"placement": "local"})
63 self.assertEqual(
64 provider.get_placement_policy(), "local")
6562
66 def test_get_machine_error(self):63 def test_get_machine_error(self):
67 provider = DummyProvider()64 provider = DummyProvider()
6865
=== modified file 'juju/providers/dummy.py'
--- juju/providers/dummy.py 2011-09-27 02:14:56 +0000
+++ juju/providers/dummy.py 2011-09-27 22:31:25 +0000
@@ -11,7 +11,7 @@
1111
1212
13from juju.machine import ProviderMachine13from juju.machine import ProviderMachine
14from juju.state.placement import UNASSIGNED_POLICY14from juju.state.placement import list_policies
15from juju.providers.common.files import FileStorage15from juju.providers.common.files import FileStorage
1616
17log = logging.getLogger("juju.providers")17log = logging.getLogger("juju.providers")
@@ -34,12 +34,10 @@
34 self._state = None34 self._state = None
35 self._storage = None35 self._storage = None
3636
37 def get_placement_policy(self):37 def get_placement_policies(self):
38 """Get the unit placement policy for the provider.38 """Get the available unit placement policies for the provider.
39
40 :param preference: A user specified plcaement policy preference
41 """39 """
42 return self.config.get("placement", UNASSIGNED_POLICY)40 return list_policies()
4341
44 @property42 @property
45 def provider_type(self):43 def provider_type(self):
4644
=== modified file 'juju/providers/lxc/__init__.py'
--- juju/providers/lxc/__init__.py 2011-09-27 02:51:59 +0000
+++ juju/providers/lxc/__init__.py 2011-09-27 22:31:25 +0000
@@ -39,12 +39,9 @@
39 self._directory = os.path.join(39 self._directory = os.path.join(
40 self.config["data-dir"], self._qualified_name)40 self.config["data-dir"], self._qualified_name)
4141
42 def get_placement_policy(self):42 def get_placement_policies(self):
43 """Local dev supports only one unit placement policy."""43 """Local dev supports only one unit placement policy."""
44 if self.config.get("placement", LOCAL_POLICY) != LOCAL_POLICY:44 return [LOCAL_POLICY]
45 raise ProviderError(
46 "Unsupported placement policy for local provider")
47 return LOCAL_POLICY
4845
49 @property46 @property
50 def provider_type(self):47 def provider_type(self):
5148
=== modified file 'juju/providers/lxc/tests/test_provider.py'
--- juju/providers/lxc/tests/test_provider.py 2011-09-27 02:51:59 +0000
+++ juju/providers/lxc/tests/test_provider.py 2011-09-27 22:31:25 +0000
@@ -41,15 +41,10 @@
41 user_name = pwd.getpwuid(os.getuid()).pw_name41 user_name = pwd.getpwuid(os.getuid()).pw_name
42 return "%s-%s" % (user_name, self.provider.environment_name)42 return "%s-%s" % (user_name, self.provider.environment_name)
4343
44 def test_get_placement_policy(self):44 def test_get_placement_policies(self):
45 """Lxc provider only supports local placement."""45 """Lxc provider only supports local placement."""
46 self.assertEqual(46 self.assertEqual(
47 self.provider.get_placement_policy(), "local")47 self.provider.get_placement_policies(), ["local"])
48 provider = MachineProvider(
49 "test", {"placement": "unassigned",
50 "data-dir": self.makeDir()})
51 self.assertRaises(
52 ProviderError, provider.get_placement_policy)
5348
54 def assertDir(self, *path_parts):49 def assertDir(self, *path_parts):
55 path = os.path.join(*path_parts)50 path = os.path.join(*path_parts)
5651
=== modified file 'juju/providers/tests/test_dummy.py'
--- juju/providers/tests/test_dummy.py 2011-09-27 02:14:56 +0000
+++ juju/providers/tests/test_dummy.py 2011-09-27 22:31:25 +0000
@@ -7,7 +7,7 @@
7from juju.errors import ProviderError7from juju.errors import ProviderError
8from juju.machine import ProviderMachine8from juju.machine import ProviderMachine
9from juju.providers.dummy import MachineProvider, DummyMachine9from juju.providers.dummy import MachineProvider, DummyMachine
10from juju.state.placement import UNASSIGNED_POLICY10from juju.state.placement import list_policies
11from juju.lib.testing import TestCase11from juju.lib.testing import TestCase
1212
1313
@@ -24,12 +24,9 @@
24 def test_provider_type(self):24 def test_provider_type(self):
25 self.assertEqual(self.provider.provider_type, "dummy")25 self.assertEqual(self.provider.provider_type, "dummy")
2626
27 def test_get_placement_policy(self):27 def test_get_placement_policies(self):
28 self.assertEqual(28 self.assertEqual(self.provider.get_placement_policies(),
29 self.provider.get_placement_policy(), UNASSIGNED_POLICY)29 list_policies())
30 self.provider = MachineProvider("foo", {"placement": "local"})
31 self.assertEqual(
32 self.provider.get_placement_policy(), "local")
3330
34 @inlineCallbacks31 @inlineCallbacks
35 def test_bootstrap(self):32 def test_bootstrap(self):
3633
=== modified file 'juju/state/placement.py'
--- juju/state/placement.py 2011-09-27 02:02:42 +0000
+++ juju/state/placement.py 2011-09-27 22:31:25 +0000
@@ -55,10 +55,29 @@
55 }55 }
5656
5757
58def list_policies():
59 """List all policies
60 """
61 # Ordering is important, and permissive providers use this
62 # function to get the policy enumeratoin, the first item is the
63 # default used in the absence of a user preference.
64 return [UNASSIGNED_POLICY, LOCAL_POLICY]
65
66
58def pick_policy(preference, provider):67def pick_policy(preference, provider):
68 """Pick a provider appropriate placement policy.
69
70 :param preference: A user expressed placement policy preference.
71 :param provider: A machine provider.
72 """
59 policies = provider.get_placement_policies()73 policies = provider.get_placement_policies()
74 # If no user policy expressed, check the environment config
75 if not preference:
76 preference = provider.config.get("placement")
77 # If there's still no preference, chooce the provider's first policy.
60 if not preference:78 if not preference:
61 return policies[0]79 return policies[0]
80 # Validate the user policy preference.
62 if preference not in policies:81 if preference not in policies:
63 raise InvalidPlacementPolicy(82 raise InvalidPlacementPolicy(
64 preference, provider.provider_type, policies)83 preference, provider.provider_type, policies)
6584
=== modified file 'juju/state/tests/test_placement.py'
--- juju/state/tests/test_placement.py 2011-09-27 02:02:42 +0000
+++ juju/state/tests/test_placement.py 2011-09-27 22:31:25 +0000
@@ -4,6 +4,7 @@
4from juju.errors import InvalidPlacementPolicy4from juju.errors import InvalidPlacementPolicy
5from juju.state.placement import place_unit, pick_policy5from juju.state.placement import place_unit, pick_policy
6from juju.state.tests.test_service import ServiceStateManagerTestBase6from juju.state.tests.test_service import ServiceStateManagerTestBase
7from juju.providers.dummy import MachineProvider
78
89
9class TestPlacement(ServiceStateManagerTestBase):10class TestPlacement(ServiceStateManagerTestBase):
@@ -16,26 +17,28 @@
16 self.unit_state = yield self.service.add_unit_state()17 self.unit_state = yield self.service.add_unit_state()
1718
18 def test_pick_policy(self):19 def test_pick_policy(self):
19 mock_provider = self.mocker.mock()20 provider = MachineProvider("foobar", {})
20 mock_provider.get_placement_policies()
21 self.mocker.result(["unassigned", "local", "new"])
22 self.mocker.count(3)
23 mock_provider.provider_type
24 self.mocker.result("dummy")
25 self.mocker.replay()
26
27 # No selection gets first listed provider policy21 # No selection gets first listed provider policy
28 self.assertEqual(22 self.assertEqual(
29 pick_policy(None, mock_provider), "unassigned")23 pick_policy(None, provider), "unassigned")
3024
31 # If the user selection doesn't match we get an error25 # If the user selection doesn't match we get an error
32 self.assertRaises(26 self.assertRaises(
33 InvalidPlacementPolicy,27 InvalidPlacementPolicy,
34 pick_policy, "smart", mock_provider)28 pick_policy, "smart", provider)
3529
36 # The user choice is respected if its available30 # The user choice is respected if its available
37 self.assertEqual(31 self.assertEqual(
38 pick_policy("new", mock_provider), "new")32 pick_policy("local", provider), "local")
33
34 # Invalid environment placement setting is an error
35 provider = MachineProvider("foobar", {"placement": "smart"})
36 self.assertRaises(
37 InvalidPlacementPolicy, pick_policy, None, provider)
38
39 # User selection takes precedence over environment
40 provider = MachineProvider("foobar", {"placement": "unassigned"})
41 self.assertEqual(pick_policy("local", provider), "local")
3942
40 @inlineCallbacks43 @inlineCallbacks
41 def test_unassign_placement(self):44 def test_unassign_placement(self):

Subscribers

People subscribed via source and target branches

to status/vote changes: