summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Wagner <sebastian.wagner@suse.com>2020-03-09 17:08:57 +0100
committerSebastian Wagner <sebastian.wagner@suse.com>2020-03-10 13:28:22 +0100
commit3d3e1f69a3aefbda3eacebf55ff337228a8f564f (patch)
tree2aeb59f49d8685bd0ffc75de166ee37832df40d6
parentcephadm: add host_pattern to supported scheduling (diff)
downloadceph-3d3e1f69a3aefbda3eacebf55ff337228a8f564f.tar.xz
ceph-3d3e1f69a3aefbda3eacebf55ff337228a8f564f.zip
python-common: Add `host_pattern` to `PlacementSpec.from_string()`
Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
-rw-r--r--src/python-common/ceph/deployment/service_spec.py42
-rw-r--r--src/python-common/ceph/tests/test_service_spec.py18
-rw-r--r--src/python-common/tox.ini4
3 files changed, 47 insertions, 17 deletions
diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py
index 481bad7254c..35d01facc4b 100644
--- a/src/python-common/ceph/deployment/service_spec.py
+++ b/src/python-common/ceph/deployment/service_spec.py
@@ -159,7 +159,18 @@ class PlacementSpec(object):
return ' '.join(kv)
def __repr__(self):
- return "PlacementSpec(%s)" % self.pretty_str()
+ kv = []
+ if self.count:
+ kv.append('count=%d' % self.count)
+ if self.label:
+ kv.append('label=%s' % repr(self.label))
+ if self.hosts:
+ kv.append('hosts={!r}'.format(self.hosts))
+ if self.all_hosts:
+ kv.append('all_hosts=True')
+ if self.host_pattern:
+ kv.append('host_pattern={!r}'.format(self.host_pattern))
+ return "PlacementSpec(%s)" % ', '.join(kv)
@classmethod
def from_json(cls, data):
@@ -196,18 +207,29 @@ class PlacementSpec(object):
A single integer is parsed as a count:
>>> PlacementSpec.from_string('3')
PlacementSpec(count=3)
+
A list of names is parsed as host specifications:
>>> PlacementSpec.from_string('host1 host2')
- PlacementSpec(label=[HostSpec(hostname='host1', network='', name=''), HostSpec(hostname='host2', network='', name='')])
+ PlacementSpec(hosts=[HostPlacementSpec(hostname='host1', network='', name=''), HostPlacemen\
+tSpec(hostname='host2', network='', name='')])
+
You can also prefix the hosts with a count as follows:
>>> PlacementSpec.from_string('2 host1 host2')
- PlacementSpec(label=[HostSpec(hostname='host1', network='', name=''), HostSpec(hostname='host2', network='', name='')], count=2)
+ PlacementSpec(count=2, hosts=[HostPlacementSpec(hostname='host1', network='', name=''), Hos\
+tPlacementSpec(hostname='host2', network='', name='')])
+
You can spefify labels using `label:<label>`
>>> PlacementSpec.from_string('label:mon')
- PlacementSpec(label='label:mon')
+ PlacementSpec(label='mon')
+
Labels als support a count:
>>> PlacementSpec.from_string('3 label:mon')
- PlacementSpec(label='label:mon', count=3)
+ PlacementSpec(count=3, label='mon')
+
+ fnmatch is also supported:
+ >>> PlacementSpec.from_string('host_pattern:data[1-3]')
+ PlacementSpec(host_pattern='data[1-3]')
+
>>> PlacementSpec.from_string(None)
PlacementSpec()
"""
@@ -253,15 +275,21 @@ class PlacementSpec(object):
all_hosts = True
strings.remove('all:true')
- hosts = [x for x in strings if x != '*' and 'label:' not in x]
+ hosts = [x for x in strings
+ if x != '*' and 'label:' not in x and not x.startswith('host_pattern:')]
labels = [x[6:] for x in strings if 'label:' in x]
if len(labels) > 1:
raise ServiceSpecValidationError('more than one label provided: {}'.format(labels))
+ host_patterns = [x[13:] for x in strings if x.startswith('host_pattern:')]
+ if len(host_patterns) > 1:
+ raise ServiceSpecValidationError('more than one host_patterns provided: {}'.format(
+ host_patterns))
ps = PlacementSpec(count=count,
hosts=hosts,
label=labels[0] if labels else None,
- all_hosts=all_hosts)
+ all_hosts=all_hosts,
+ host_pattern=host_patterns[0] if host_patterns else None)
ps.validate()
return ps
diff --git a/src/python-common/ceph/tests/test_service_spec.py b/src/python-common/ceph/tests/test_service_spec.py
index be33fd2da7e..19553e9d6aa 100644
--- a/src/python-common/ceph/tests/test_service_spec.py
+++ b/src/python-common/ceph/tests/test_service_spec.py
@@ -27,15 +27,15 @@ def test_parse_host_placement_specs(test_input, expected, require_network):
"test_input,expected",
[
('', "PlacementSpec()"),
- ("count:2", "PlacementSpec(count:2)"),
- ("3", "PlacementSpec(count:3)"),
- ("host1 host2", "PlacementSpec(host1,host2)"),
- ("host1=a host2=b", "PlacementSpec(host1=a,host2=b)"),
- ("host1:1.2.3.4=a host2:1.2.3.5=b", "PlacementSpec(host1:1.2.3.4=a,host2:1.2.3.5=b)"),
- ('2 host1 host2', "PlacementSpec(count:2 host1,host2)"),
- ('label:foo', "PlacementSpec(label:foo)"),
- ('3 label:foo', "PlacementSpec(count:3 label:foo)"),
- ('*', 'PlacementSpec(all:true)'),
+ ("count:2", "PlacementSpec(count=2)"),
+ ("3", "PlacementSpec(count=3)"),
+ ("host1 host2", "PlacementSpec(hosts=[HostPlacementSpec(hostname='host1', network='', name=''), HostPlacementSpec(hostname='host2', network='', name='')])"),
+ ("host1=a host2=b", "PlacementSpec(hosts=[HostPlacementSpec(hostname='host1', network='', name='a'), HostPlacementSpec(hostname='host2', network='', name='b')])"),
+ ("host1:1.2.3.4=a host2:1.2.3.5=b", "PlacementSpec(hosts=[HostPlacementSpec(hostname='host1', network='1.2.3.4', name='a'), HostPlacementSpec(hostname='host2', network='1.2.3.5', name='b')])"),
+ ('2 host1 host2', "PlacementSpec(count=2, hosts=[HostPlacementSpec(hostname='host1', network='', name=''), HostPlacementSpec(hostname='host2', network='', name='')])"),
+ ('label:foo', "PlacementSpec(label='foo')"),
+ ('3 label:foo', "PlacementSpec(count=3, label='foo')"),
+ ('*', 'PlacementSpec(all_hosts=True)'),
])
def test_parse_placement_specs(test_input, expected):
ret = PlacementSpec.from_string(test_input)
diff --git a/src/python-common/tox.ini b/src/python-common/tox.ini
index 47b280f3369..da7037b2d81 100644
--- a/src/python-common/tox.ini
+++ b/src/python-common/tox.ini
@@ -5,7 +5,9 @@ skip_missing_interpreters = true
[testenv:py3]
deps=
-rrequirements.txt
-commands=pytest --mypy --mypy-ignore-missing-imports {posargs}
+commands=
+ pytest --doctest-modules ceph/deployment/service_spec.py
+ pytest --mypy --mypy-ignore-missing-imports {posargs}
[tool:pytest]
norecursedirs = .* _* virtualenv