]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/keystone.py
2 Deploy and configure Keystone for Teuthology
8 # still need this for python3.6
9 from collections
import OrderedDict
10 from itertools
import chain
12 from teuthology
import misc
as teuthology
13 from teuthology
import contextutil
14 from teuthology
.orchestra
import run
15 from teuthology
.packaging
import install_package
16 from teuthology
.packaging
import remove_package
17 from teuthology
.exceptions
import ConfigError
19 log
= logging
.getLogger(__name__
)
22 def get_keystone_dir(ctx
):
23 return '{tdir}/keystone'.format(tdir
=teuthology
.get_testdir(ctx
))
25 def run_in_keystone_dir(ctx
, client
, args
, **kwargs
):
26 return ctx
.cluster
.only(client
).run(
27 args
=[ 'cd', get_keystone_dir(ctx
), run
.Raw('&&'), ] + args
,
31 def get_toxvenv_dir(ctx
):
32 return ctx
.tox
.venv_path
34 def toxvenv_sh(ctx
, remote
, args
, **kwargs
):
35 activate
= get_toxvenv_dir(ctx
) + '/bin/activate'
36 return remote
.sh(['source', activate
, run
.Raw('&&')] + args
, **kwargs
)
38 def run_in_keystone_venv(ctx
, client
, args
):
39 run_in_keystone_dir(ctx
, client
,
41 '.tox/venv/bin/activate',
45 def get_keystone_venved_cmd(ctx
, cmd
, args
):
46 kbindir
= get_keystone_dir(ctx
) + '/.tox/venv/bin/'
47 return [ kbindir
+ 'python', kbindir
+ cmd
] + args
49 @contextlib.contextmanager
50 def download(ctx
, config
):
52 Download the Keystone from github.
53 Remove downloaded file upon exit.
55 The context passed in should be identical to the context
56 passed in to the main task.
58 assert isinstance(config
, dict)
59 log
.info('Downloading keystone...')
60 keystonedir
= get_keystone_dir(ctx
)
62 for (client
, cconf
) in config
.items():
63 ctx
.cluster
.only(client
).run(
66 '-b', cconf
.get('force-branch', 'master'),
67 'https://github.com/openstack/keystone.git',
72 sha1
= cconf
.get('sha1')
74 run_in_keystone_dir(ctx
, client
, [
75 'git', 'reset', '--hard', sha1
,
79 # hax for http://tracker.ceph.com/issues/23659
80 run_in_keystone_dir(ctx
, client
, [
82 's/pysaml2<4.0.3,>=2.4.0/pysaml2>=4.5.0/',
89 log
.info('Removing keystone...')
91 ctx
.cluster
.only(client
).run(
92 args
=[ 'rm', '-rf', keystonedir
],
95 patch_bindep_template
= """\
100 os.chdir("{keystone_dir}")
101 for line in fileinput.input("bindep.txt", inplace=True):
102 if line == "python34-devel [platform:centos]\\n":
103 line="python34-devel [platform:centos-7]\\npython36-devel [platform:centos-8]\\n"
107 print("Fixed line" if fixed else "No fix necessary", file=sys.stderr)
111 @contextlib.contextmanager
112 def install_packages(ctx
, config
):
114 Download the packaged dependencies of Keystone.
115 Remove install packages upon exit.
117 The context passed in should be identical to the context
118 passed in to the main task.
120 assert isinstance(config
, dict)
121 log
.info('Installing packages for Keystone...')
123 patch_bindep
= patch_bindep_template \
124 .replace("{keystone_dir}", get_keystone_dir(ctx
))
126 for (client
, _
) in config
.items():
127 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
128 toxvenv_sh(ctx
, remote
, ['python'], stdin
=patch_bindep
)
129 # use bindep to read which dependencies we need from keystone/bindep.txt
130 toxvenv_sh(ctx
, remote
, ['pip', 'install', 'bindep'])
131 packages
[client
] = toxvenv_sh(ctx
, remote
,
132 ['bindep', '--brief', '--file', '{}/bindep.txt'.format(get_keystone_dir(ctx
))],
133 check_status
=False).splitlines() # returns 1 on success?
134 for dep
in packages
[client
]:
135 install_package(dep
, remote
)
139 log
.info('Removing packaged dependencies of Keystone...')
141 for (client
, _
) in config
.items():
142 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
143 for dep
in packages
[client
]:
144 remove_package(dep
, remote
)
146 @contextlib.contextmanager
147 def setup_venv(ctx
, config
):
149 Setup the virtualenv for Keystone using tox.
151 assert isinstance(config
, dict)
152 log
.info('Setting up virtualenv for keystone...')
153 for (client
, _
) in config
.items():
154 run_in_keystone_dir(ctx
, client
,
156 '{tvdir}/bin/activate'.format(tvdir
=get_toxvenv_dir(ctx
)),
158 'tox', '-e', 'venv', '--notest'
161 run_in_keystone_venv(ctx
, client
,
163 'python-openstackclient==5.2.1',
171 @contextlib.contextmanager
172 def configure_instance(ctx
, config
):
173 assert isinstance(config
, dict)
174 log
.info('Configuring keystone...')
176 keyrepo_dir
= '{kdir}/etc/fernet-keys'.format(kdir
=get_keystone_dir(ctx
))
177 for (client
, _
) in config
.items():
178 # prepare the config file
179 run_in_keystone_dir(ctx
, client
,
182 f
'{get_toxvenv_dir(ctx)}/bin/activate',
184 'tox', '-e', 'genconfig'
186 run_in_keystone_dir(ctx
, client
,
189 'etc/keystone.conf.sample',
192 run_in_keystone_dir(ctx
, client
,
195 '-e', 's^#key_repository =.*^key_repository = {kr}^'.format(kr
= keyrepo_dir
),
196 '-i', 'etc/keystone.conf'
198 # log to a file that gets archived
199 log_file
= '{p}/archive/keystone.{c}.log'.format(p
=teuthology
.get_testdir(ctx
), c
=client
)
200 run_in_keystone_dir(ctx
, client
,
203 '-e', 's^#log_file =.*^log_file = {}^'.format(log_file
),
204 '-i', 'etc/keystone.conf'
206 # copy the config to archive
207 run_in_keystone_dir(ctx
, client
, [
208 'cp', 'etc/keystone.conf',
209 '{}/archive/keystone.{}.conf'.format(teuthology
.get_testdir(ctx
), client
)
212 # prepare key repository for Fetnet token authenticator
213 run_in_keystone_dir(ctx
, client
, [ 'mkdir', '-p', keyrepo_dir
])
214 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'fernet_setup' ])
217 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'db_sync' ])
220 @contextlib.contextmanager
221 def run_keystone(ctx
, config
):
222 assert isinstance(config
, dict)
223 log
.info('Configuring keystone...')
225 for (client
, _
) in config
.items():
226 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
227 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
229 # start the public endpoint
230 client_public_with_id
= 'keystone.public' + '.' + client_id
232 public_host
, public_port
= ctx
.keystone
.public_endpoints
[client
]
233 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-public',
234 [ '--host', public_host
, '--port', str(public_port
),
235 # Let's put the Keystone in background, wait for EOF
236 # and after receiving it, send SIGTERM to the daemon.
237 # This crazy hack is because Keystone, in contrast to
238 # our other daemons, doesn't quit on stdin.close().
239 # Teuthology relies on this behaviour.
240 run
.Raw('& { read; kill %1; }')
243 ctx
.daemons
.add_daemon(
244 remote
, 'keystone', client_public_with_id
,
245 cluster
=cluster_name
,
247 logger
=log
.getChild(client
),
249 cwd
=get_keystone_dir(ctx
),
254 # start the admin endpoint
255 client_admin_with_id
= 'keystone.admin' + '.' + client_id
257 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[client
]
258 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-admin',
259 [ '--host', admin_host
, '--port', str(admin_port
),
260 run
.Raw('& { read; kill %1; }')
263 ctx
.daemons
.add_daemon(
264 remote
, 'keystone', client_admin_with_id
,
265 cluster
=cluster_name
,
267 logger
=log
.getChild(client
),
269 cwd
=get_keystone_dir(ctx
),
274 # sleep driven synchronization
275 run_in_keystone_venv(ctx
, client
, [ 'sleep', '15' ])
279 log
.info('Stopping Keystone admin instance')
280 ctx
.daemons
.get_daemon('keystone', client_admin_with_id
,
283 log
.info('Stopping Keystone public instance')
284 ctx
.daemons
.get_daemon('keystone', client_public_with_id
,
288 def dict_to_args(specials
, items
):
291 [(key1, val1), (special, val_special), (key3, val3) ]
293 [ '--key1', 'val1', '--key3', 'val3', 'val_special' ]
296 special_vals
= OrderedDict((k
, '') for k
in specials
.split(','))
298 if k
in special_vals
:
301 args
.append('--{k}'.format(k
=k
))
303 args
.extend(arg
for arg
in special_vals
.values() if arg
)
306 def run_section_cmds(ctx
, cclient
, section_cmd
, specials
,
307 section_config_list
):
308 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
311 ( 'os-username', 'admin' ),
312 ( 'os-password', 'ADMIN' ),
313 ( 'os-user-domain-id', 'default' ),
314 ( 'os-project-name', 'admin' ),
315 ( 'os-project-domain-id', 'default' ),
316 ( 'os-identity-api-version', '3' ),
317 ( 'os-auth-url', 'http://{host}:{port}/v3'.format(host
=admin_host
,
321 for section_item
in section_config_list
:
322 run_in_keystone_venv(ctx
, cclient
,
323 [ 'openstack' ] + section_cmd
.split() +
324 dict_to_args(specials
, auth_section
+ list(section_item
.items())) +
327 def create_endpoint(ctx
, cclient
, service
, url
, adminurl
=None):
328 endpoint_sections
= [
329 {'service': service
, 'interface': 'public', 'url': url
},
332 endpoint_sections
.append(
333 {'service': service
, 'interface': 'admin', 'url': adminurl
}
335 run_section_cmds(ctx
, cclient
, 'endpoint create',
336 'service,interface,url',
339 @contextlib.contextmanager
340 def fill_keystone(ctx
, config
):
341 assert isinstance(config
, dict)
343 for (cclient
, cconfig
) in config
.items():
344 public_host
, public_port
= ctx
.keystone
.public_endpoints
[cclient
]
345 url
= 'http://{host}:{port}/v3'.format(host
=public_host
,
347 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
348 admin_url
= 'http://{host}:{port}/v3'.format(host
=admin_host
,
350 opts
= {'password': 'ADMIN',
351 'region-id': 'RegionOne',
353 'admin-url': admin_url
,
355 bootstrap_args
= chain
.from_iterable(('--bootstrap-{}'.format(k
), v
)
356 for k
, v
in opts
.items())
357 run_in_keystone_venv(ctx
, cclient
,
358 ['keystone-manage', 'bootstrap'] +
359 list(bootstrap_args
))
361 # configure tenants/projects
362 run_section_cmds(ctx
, cclient
, 'domain create', 'name',
363 cconfig
.get('domains', []))
364 run_section_cmds(ctx
, cclient
, 'project create', 'name',
365 cconfig
.get('projects', []))
366 run_section_cmds(ctx
, cclient
, 'user create', 'name',
367 cconfig
.get('users', []))
368 run_section_cmds(ctx
, cclient
, 'role create', 'name',
369 cconfig
.get('roles', []))
370 run_section_cmds(ctx
, cclient
, 'role add', 'name',
371 cconfig
.get('role-mappings', []))
372 run_section_cmds(ctx
, cclient
, 'service create', 'type',
373 cconfig
.get('services', []))
375 # for the deferred endpoint creation; currently it's used in rgw.py
376 ctx
.keystone
.create_endpoint
= create_endpoint
378 # sleep driven synchronization -- just in case
379 run_in_keystone_venv(ctx
, cclient
, [ 'sleep', '3' ])
385 def assign_ports(ctx
, config
, initial_port
):
387 Assign port numbers starting from @initial_port
391 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
392 for role
in roles_for_host
:
394 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
397 return role_endpoints
399 @contextlib.contextmanager
400 def task(ctx
, config
):
402 Deploy and configure Keystone
404 Example of configuration:
414 description: Default Domain
417 description: Admin Tenant
422 roles: [ name: admin, name: Member ]
430 description: Keystone Identity Service
433 description: Swift Service
435 assert config
is None or isinstance(config
, list) \
436 or isinstance(config
, dict), \
437 "task keystone only supports a list or dictionary for configuration"
439 if not hasattr(ctx
, 'tox'):
440 raise ConfigError('keystone must run after the tox task')
442 all_clients
= ['client.{id}'.format(id=id_
)
443 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
446 if isinstance(config
, list):
447 config
= dict.fromkeys(config
)
449 log
.debug('Keystone config is %s', config
)
451 ctx
.keystone
= argparse
.Namespace()
452 ctx
.keystone
.public_endpoints
= assign_ports(ctx
, config
, 5000)
453 ctx
.keystone
.admin_endpoints
= assign_ports(ctx
, config
, 35357)
455 with contextutil
.nested(
456 lambda: download(ctx
=ctx
, config
=config
),
457 lambda: install_packages(ctx
=ctx
, config
=config
),
458 lambda: setup_venv(ctx
=ctx
, config
=config
),
459 lambda: configure_instance(ctx
=ctx
, config
=config
),
460 lambda: run_keystone(ctx
=ctx
, config
=config
),
461 lambda: fill_keystone(ctx
=ctx
, config
=config
),