]>
Commit | Line | Data |
---|---|---|
20effc67 | 1 | from mgr_module import MgrModule, CommandResult, Option, NotifyType |
3efd9988 FG |
2 | import json |
3 | import threading | |
20effc67 TL |
4 | from typing import cast, Any |
5 | ||
3efd9988 FG |
6 | |
7 | class Module(MgrModule): | |
11fdf7f2 TL |
8 | |
9 | MODULE_OPTIONS = [ | |
20effc67 TL |
10 | Option( |
11 | name='subtree', | |
12 | type='str', | |
13 | default='rack', | |
14 | desc='CRUSH level for which to create a local pool', | |
15 | long_desc='which CRUSH subtree type the module should create a pool for.', | |
16 | runtime=True), | |
17 | Option( | |
18 | name='failure_domain', | |
19 | type='str', | |
20 | default='host', | |
21 | desc='failure domain for any created local pool', | |
22 | long_desc='what failure domain we should separate data replicas across.', | |
23 | runtime=True), | |
24 | Option( | |
25 | name='min_size', | |
26 | type='int', | |
27 | desc='default min_size for any created local pool', | |
28 | long_desc='value to set min_size to (unchanged from Ceph\'s default if this option is not set)', | |
29 | runtime=True), | |
30 | Option( | |
31 | name='num_rep', | |
32 | type='int', | |
33 | default=3, | |
34 | desc='default replica count for any created local pool', | |
35 | runtime=True), | |
36 | Option( | |
37 | name='pg_num', | |
38 | type='int', | |
39 | default=128, | |
40 | desc='default pg_num for any created local pool', | |
41 | runtime=True), | |
42 | Option( | |
43 | name='prefix', | |
44 | type='str', | |
45 | default='', | |
46 | desc='name prefix for any created local pool', | |
47 | runtime=True), | |
11fdf7f2 | 48 | ] |
20effc67 | 49 | NOTIFY_TYPES = [NotifyType.osd_map] |
11fdf7f2 | 50 | |
20effc67 | 51 | def __init__(self, *args: Any, **kwargs: Any) -> None: |
3efd9988 FG |
52 | super(Module, self).__init__(*args, **kwargs) |
53 | self.serve_event = threading.Event() | |
54 | ||
20effc67 TL |
55 | def notify(self, notify_type: NotifyType, notify_id: str) -> None: |
56 | if notify_type == NotifyType.osd_map: | |
3efd9988 FG |
57 | self.handle_osd_map() |
58 | ||
20effc67 | 59 | def handle_osd_map(self) -> None: |
3efd9988 FG |
60 | """ |
61 | Check pools on each OSDMap change | |
62 | """ | |
20effc67 | 63 | subtree_type = cast(str, self.get_module_option('subtree')) |
11fdf7f2 TL |
64 | failure_domain = self.get_module_option('failure_domain') |
65 | pg_num = self.get_module_option('pg_num') | |
66 | num_rep = self.get_module_option('num_rep') | |
67 | min_size = self.get_module_option('min_size') | |
20effc67 | 68 | prefix = cast(str, self.get_module_option('prefix')) or 'by-' + subtree_type + '-' |
3efd9988 FG |
69 | |
70 | osdmap = self.get("osd_map") | |
71 | lpools = [] | |
72 | for pool in osdmap['pools']: | |
73 | if pool['pool_name'].find(prefix) == 0: | |
74 | lpools.append(pool['pool_name']) | |
75 | ||
76 | self.log.debug('localized pools = %s', lpools) | |
77 | subtrees = [] | |
78 | tree = self.get('osd_map_tree') | |
79 | for node in tree['nodes']: | |
80 | if node['type'] == subtree_type: | |
81 | subtrees.append(node['name']) | |
82 | pool_name = prefix + node['name'] | |
83 | if pool_name not in lpools: | |
84 | self.log.info('Creating localized pool %s', pool_name) | |
85 | # | |
86 | result = CommandResult("") | |
87 | self.send_command(result, "mon", "", json.dumps({ | |
88 | "prefix": "osd crush rule create-replicated", | |
89 | "format": "json", | |
90 | "name": pool_name, | |
91 | "root": node['name'], | |
92 | "type": failure_domain, | |
93 | }), "") | |
94 | r, outb, outs = result.wait() | |
95 | ||
96 | result = CommandResult("") | |
97 | self.send_command(result, "mon", "", json.dumps({ | |
98 | "prefix": "osd pool create", | |
99 | "format": "json", | |
100 | "pool": pool_name, | |
101 | 'rule': pool_name, | |
3efd9988 | 102 | "pool_type": 'replicated', |
11fdf7f2 | 103 | 'pg_num': pg_num, |
3efd9988 FG |
104 | }), "") |
105 | r, outb, outs = result.wait() | |
106 | ||
107 | result = CommandResult("") | |
108 | self.send_command(result, "mon", "", json.dumps({ | |
109 | "prefix": "osd pool set", | |
110 | "format": "json", | |
111 | "pool": pool_name, | |
112 | 'var': 'size', | |
113 | "val": str(num_rep), | |
114 | }), "") | |
115 | r, outb, outs = result.wait() | |
116 | ||
117 | if min_size: | |
118 | result = CommandResult("") | |
119 | self.send_command(result, "mon", "", json.dumps({ | |
120 | "prefix": "osd pool set", | |
121 | "format": "json", | |
122 | "pool": pool_name, | |
123 | 'var': 'min_size', | |
124 | "val": str(min_size), | |
125 | }), "") | |
126 | r, outb, outs = result.wait() | |
127 | ||
128 | # TODO remove pools for hosts that don't exist? | |
129 | ||
20effc67 | 130 | def serve(self) -> None: |
3efd9988 FG |
131 | self.handle_osd_map() |
132 | self.serve_event.wait() | |
133 | self.serve_event.clear() | |
134 | ||
20effc67 | 135 | def shutdown(self) -> None: |
3efd9988 | 136 | self.serve_event.set() |