]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/keystone.py
2 Deploy and configure Keystone for Teuthology
7 from cStringIO
import StringIO
9 from teuthology
import misc
as teuthology
10 from teuthology
import contextutil
11 from teuthology
.orchestra
import run
12 from teuthology
.packaging
import install_package
13 from teuthology
.packaging
import remove_package
14 from teuthology
.exceptions
import ConfigError
16 log
= logging
.getLogger(__name__
)
19 def get_keystone_dir(ctx
):
20 return '{tdir}/keystone'.format(tdir
=teuthology
.get_testdir(ctx
))
22 def run_in_keystone_dir(ctx
, client
, args
, **kwargs
):
23 return ctx
.cluster
.only(client
).run(
24 args
=[ 'cd', get_keystone_dir(ctx
), run
.Raw('&&'), ] + args
,
28 def get_toxvenv_dir(ctx
):
29 return ctx
.tox
.venv_path
31 def run_in_tox_venv(ctx
, remote
, args
, **kwargs
):
33 args
=[ 'source', '{}/bin/activate'.format(get_toxvenv_dir(ctx
)), run
.Raw('&&') ] + args
,
37 def run_in_keystone_venv(ctx
, client
, args
):
38 run_in_keystone_dir(ctx
, client
,
40 '.tox/venv/bin/activate',
44 def get_keystone_venved_cmd(ctx
, cmd
, args
):
45 kbindir
= get_keystone_dir(ctx
) + '/.tox/venv/bin/'
46 return [ kbindir
+ 'python', kbindir
+ cmd
] + args
48 @contextlib.contextmanager
49 def download(ctx
, config
):
51 Download the Keystone from github.
52 Remove downloaded file upon exit.
54 The context passed in should be identical to the context
55 passed in to the main task.
57 assert isinstance(config
, dict)
58 log
.info('Downloading keystone...')
59 keystonedir
= get_keystone_dir(ctx
)
61 for (client
, cconf
) in config
.items():
62 ctx
.cluster
.only(client
).run(
65 '-b', cconf
.get('force-branch', 'master'),
66 'https://github.com/openstack/keystone.git',
71 sha1
= cconf
.get('sha1')
73 run_in_keystone_dir(ctx
, client
, [
74 'git', 'reset', '--hard', sha1
,
78 # hax for http://tracker.ceph.com/issues/23659
79 run_in_keystone_dir(ctx
, client
, [
81 's/pysaml2<4.0.3,>=2.4.0/pysaml2>=4.5.0/',
88 log
.info('Removing keystone...')
90 ctx
.cluster
.only(client
).run(
91 args
=[ 'rm', '-rf', keystonedir
],
94 @contextlib.contextmanager
95 def install_packages(ctx
, config
):
97 Download the packaged dependencies of Keystone.
98 Remove install packages upon exit.
100 The context passed in should be identical to the context
101 passed in to the main task.
103 assert isinstance(config
, dict)
104 log
.info('Installing packages for Keystone...')
107 for (client
, _
) in config
.items():
108 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
109 # use bindep to read which dependencies we need from keystone/bindep.txt
110 run_in_tox_venv(ctx
, remote
, ['pip', 'install', 'bindep'])
111 r
= run_in_tox_venv(ctx
, remote
,
112 ['bindep', '--brief', '--file', '{}/bindep.txt'.format(get_keystone_dir(ctx
))],
114 check_status
=False) # returns 1 on success?
115 packages
[client
] = r
.stdout
.getvalue().splitlines()
116 for dep
in packages
[client
]:
117 install_package(dep
, remote
)
121 log
.info('Removing packaged dependencies of Keystone...')
123 for (client
, _
) in config
.items():
124 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
125 for dep
in packages
[client
]:
126 remove_package(dep
, remote
)
128 @contextlib.contextmanager
129 def setup_venv(ctx
, config
):
131 Setup the virtualenv for Keystone using tox.
133 assert isinstance(config
, dict)
134 log
.info('Setting up virtualenv for keystone...')
135 for (client
, _
) in config
.items():
136 run_in_keystone_dir(ctx
, client
,
138 '{tvdir}/bin/activate'.format(tvdir
=get_toxvenv_dir(ctx
)),
140 'tox', '-e', 'venv', '--notest'
143 run_in_keystone_venv(ctx
, client
,
144 [ 'pip', 'install', 'python-openstackclient<=3.19.0',
145 '-r', 'requirements.txt'
152 @contextlib.contextmanager
153 def configure_instance(ctx
, config
):
154 assert isinstance(config
, dict)
155 log
.info('Configuring keystone...')
157 keyrepo_dir
= '{kdir}/etc/fernet-keys'.format(kdir
=get_keystone_dir(ctx
))
158 for (client
, _
) in config
.items():
159 # prepare the config file
160 run_in_keystone_dir(ctx
, client
,
163 'etc/keystone.conf.sample',
166 run_in_keystone_dir(ctx
, client
,
169 '-e', 's/#admin_token =.*/admin_token = ADMIN/',
170 '-i', 'etc/keystone.conf'
172 run_in_keystone_dir(ctx
, client
,
175 '-e', 's^#key_repository =.*^key_repository = {kr}^'.format(kr
= keyrepo_dir
),
176 '-i', 'etc/keystone.conf'
178 # log to a file that gets archived
179 log_file
= '{p}/archive/keystone.{c}.log'.format(p
=teuthology
.get_testdir(ctx
), c
=client
)
180 run_in_keystone_dir(ctx
, client
,
183 '-e', 's^#log_file =.*^log_file = {}^'.format(log_file
),
184 '-i', 'etc/keystone.conf'
186 # copy the config to archive
187 run_in_keystone_dir(ctx
, client
, [
188 'cp', 'etc/keystone.conf',
189 '{}/archive/keystone.{}.conf'.format(teuthology
.get_testdir(ctx
), client
)
192 # prepare key repository for Fetnet token authenticator
193 run_in_keystone_dir(ctx
, client
, [ 'mkdir', '-p', keyrepo_dir
])
194 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'fernet_setup' ])
197 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'db_sync' ])
200 @contextlib.contextmanager
201 def run_keystone(ctx
, config
):
202 assert isinstance(config
, dict)
203 log
.info('Configuring keystone...')
205 for (client
, _
) in config
.items():
206 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
207 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
209 # start the public endpoint
210 client_public_with_id
= 'keystone.public' + '.' + client_id
212 public_host
, public_port
= ctx
.keystone
.public_endpoints
[client
]
213 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-public',
214 [ '--host', public_host
, '--port', str(public_port
),
215 # Let's put the Keystone in background, wait for EOF
216 # and after receiving it, send SIGTERM to the daemon.
217 # This crazy hack is because Keystone, in contrast to
218 # our other daemons, doesn't quit on stdin.close().
219 # Teuthology relies on this behaviour.
220 run
.Raw('& { read; kill %1; }')
223 ctx
.daemons
.add_daemon(
224 remote
, 'keystone', client_public_with_id
,
225 cluster
=cluster_name
,
227 logger
=log
.getChild(client
),
229 cwd
=get_keystone_dir(ctx
),
234 # start the admin endpoint
235 client_admin_with_id
= 'keystone.admin' + '.' + client_id
237 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[client
]
238 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-admin',
239 [ '--host', admin_host
, '--port', str(admin_port
),
240 run
.Raw('& { read; kill %1; }')
243 ctx
.daemons
.add_daemon(
244 remote
, 'keystone', client_admin_with_id
,
245 cluster
=cluster_name
,
247 logger
=log
.getChild(client
),
249 cwd
=get_keystone_dir(ctx
),
254 # sleep driven synchronization
255 run_in_keystone_venv(ctx
, client
, [ 'sleep', '15' ])
259 log
.info('Stopping Keystone admin instance')
260 ctx
.daemons
.get_daemon('keystone', client_admin_with_id
,
263 log
.info('Stopping Keystone public instance')
264 ctx
.daemons
.get_daemon('keystone', client_public_with_id
,
268 def dict_to_args(special
, items
):
271 [(key1, val1), (special, val_special), (key3, val3) ]
273 [ '--key1', 'val1', '--key3', 'val3', 'val_special' ]
280 args
.append('--{k}'.format(k
=k
))
283 args
.append(special_val
)
286 def run_section_cmds(ctx
, cclient
, section_cmd
, special
,
287 section_config_list
):
288 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
291 ( 'os-token', 'ADMIN' ),
292 ( 'os-identity-api-version', '2.0' ),
293 ( 'os-url', 'http://{host}:{port}/v2.0'.format(host
=admin_host
,
297 for section_item
in section_config_list
:
298 run_in_keystone_venv(ctx
, cclient
,
299 [ 'openstack' ] + section_cmd
.split() +
300 dict_to_args(special
, auth_section
+ section_item
.items()) +
303 def create_endpoint(ctx
, cclient
, service
, url
, adminurl
=None):
309 endpoint_section
.update( {
310 'adminurl': adminurl
,
312 return run_section_cmds(ctx
, cclient
, 'endpoint create', 'service',
313 [ endpoint_section
])
315 @contextlib.contextmanager
316 def fill_keystone(ctx
, config
):
317 assert isinstance(config
, dict)
319 for (cclient
, cconfig
) in config
.items():
320 # configure tenants/projects
321 run_section_cmds(ctx
, cclient
, 'project create', 'name',
323 run_section_cmds(ctx
, cclient
, 'user create', 'name',
325 run_section_cmds(ctx
, cclient
, 'role create', 'name',
327 run_section_cmds(ctx
, cclient
, 'role add', 'name',
328 cconfig
['role-mappings'])
329 run_section_cmds(ctx
, cclient
, 'service create', 'name',
332 public_host
, public_port
= ctx
.keystone
.public_endpoints
[cclient
]
333 url
= 'http://{host}:{port}/v2.0'.format(host
=public_host
,
335 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
336 admin_url
= 'http://{host}:{port}/v2.0'.format(host
=admin_host
,
338 create_endpoint(ctx
, cclient
, 'keystone', url
, admin_url
)
339 # for the deferred endpoint creation; currently it's used in rgw.py
340 ctx
.keystone
.create_endpoint
= create_endpoint
342 # sleep driven synchronization -- just in case
343 run_in_keystone_venv(ctx
, cclient
, [ 'sleep', '3' ])
349 def assign_ports(ctx
, config
, initial_port
):
351 Assign port numbers starting from @initial_port
355 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
356 for role
in roles_for_host
:
358 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
361 return role_endpoints
363 @contextlib.contextmanager
364 def task(ctx
, config
):
366 Deploy and configure Keystone
368 Example of configuration:
378 description: Admin Tenant
383 roles: [ name: admin, name: Member ]
391 description: Keystone Identity Service
394 description: Swift Service
396 assert config
is None or isinstance(config
, list) \
397 or isinstance(config
, dict), \
398 "task keystone only supports a list or dictionary for configuration"
400 if not hasattr(ctx
, 'tox'):
401 raise ConfigError('keystone must run after the tox task')
403 all_clients
= ['client.{id}'.format(id=id_
)
404 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
407 if isinstance(config
, list):
408 config
= dict.fromkeys(config
)
410 log
.debug('Keystone config is %s', config
)
412 ctx
.keystone
= argparse
.Namespace()
413 ctx
.keystone
.public_endpoints
= assign_ports(ctx
, config
, 5000)
414 ctx
.keystone
.admin_endpoints
= assign_ports(ctx
, config
, 35357)
416 with contextutil
.nested(
417 lambda: download(ctx
=ctx
, config
=config
),
418 lambda: install_packages(ctx
=ctx
, config
=config
),
419 lambda: setup_venv(ctx
=ctx
, config
=config
),
420 lambda: configure_instance(ctx
=ctx
, config
=config
),
421 lambda: run_keystone(ctx
=ctx
, config
=config
),
422 lambda: fill_keystone(ctx
=ctx
, config
=config
),