]>
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 @contextlib.contextmanager
96 def install_packages(ctx
, config
):
98 Download the packaged dependencies of Keystone.
99 Remove install packages upon exit.
101 The context passed in should be identical to the context
102 passed in to the main task.
104 assert isinstance(config
, dict)
105 log
.info('Installing packages for Keystone...')
108 for (client
, _
) in config
.items():
109 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
110 # use bindep to read which dependencies we need from keystone/bindep.txt
111 toxvenv_sh(ctx
, remote
, ['pip', 'install', 'bindep'])
112 packages
[client
] = toxvenv_sh(ctx
, remote
,
113 ['bindep', '--brief', '--file', '{}/bindep.txt'.format(get_keystone_dir(ctx
))],
114 check_status
=False).splitlines() # returns 1 on success?
115 for dep
in packages
[client
]:
116 install_package(dep
, remote
)
120 log
.info('Removing packaged dependencies of Keystone...')
122 for (client
, _
) in config
.items():
123 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
124 for dep
in packages
[client
]:
125 remove_package(dep
, remote
)
127 @contextlib.contextmanager
128 def setup_venv(ctx
, config
):
130 Setup the virtualenv for Keystone using tox.
132 assert isinstance(config
, dict)
133 log
.info('Setting up virtualenv for keystone...')
134 for (client
, _
) in config
.items():
135 run_in_keystone_dir(ctx
, client
,
137 '{tvdir}/bin/activate'.format(tvdir
=get_toxvenv_dir(ctx
)),
139 'tox', '-e', 'venv', '--notest'
142 run_in_keystone_venv(ctx
, client
,
143 [ 'pip', 'install', 'python-openstackclient'])
149 @contextlib.contextmanager
150 def configure_instance(ctx
, config
):
151 assert isinstance(config
, dict)
152 log
.info('Configuring keystone...')
154 keyrepo_dir
= '{kdir}/etc/fernet-keys'.format(kdir
=get_keystone_dir(ctx
))
155 for (client
, _
) in config
.items():
156 # prepare the config file
157 run_in_keystone_dir(ctx
, client
,
160 f
'{get_toxvenv_dir(ctx)}/bin/activate',
162 'tox', '-e', 'genconfig'
164 run_in_keystone_dir(ctx
, client
,
167 'etc/keystone.conf.sample',
170 run_in_keystone_dir(ctx
, client
,
173 '-e', 's^#key_repository =.*^key_repository = {kr}^'.format(kr
= keyrepo_dir
),
174 '-i', 'etc/keystone.conf'
176 # log to a file that gets archived
177 log_file
= '{p}/archive/keystone.{c}.log'.format(p
=teuthology
.get_testdir(ctx
), c
=client
)
178 run_in_keystone_dir(ctx
, client
,
181 '-e', 's^#log_file =.*^log_file = {}^'.format(log_file
),
182 '-i', 'etc/keystone.conf'
184 # copy the config to archive
185 run_in_keystone_dir(ctx
, client
, [
186 'cp', 'etc/keystone.conf',
187 '{}/archive/keystone.{}.conf'.format(teuthology
.get_testdir(ctx
), client
)
190 # prepare key repository for Fetnet token authenticator
191 run_in_keystone_dir(ctx
, client
, [ 'mkdir', '-p', keyrepo_dir
])
192 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'fernet_setup' ])
195 run_in_keystone_venv(ctx
, client
, [ 'keystone-manage', 'db_sync' ])
198 @contextlib.contextmanager
199 def run_keystone(ctx
, config
):
200 assert isinstance(config
, dict)
201 log
.info('Configuring keystone...')
203 for (client
, _
) in config
.items():
204 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
205 cluster_name
, _
, client_id
= teuthology
.split_role(client
)
207 # start the public endpoint
208 client_public_with_id
= 'keystone.public' + '.' + client_id
210 public_host
, public_port
= ctx
.keystone
.public_endpoints
[client
]
211 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-public',
212 [ '--host', public_host
, '--port', str(public_port
),
213 # Let's put the Keystone in background, wait for EOF
214 # and after receiving it, send SIGTERM to the daemon.
215 # This crazy hack is because Keystone, in contrast to
216 # our other daemons, doesn't quit on stdin.close().
217 # Teuthology relies on this behaviour.
218 run
.Raw('& { read; kill %1; }')
221 ctx
.daemons
.add_daemon(
222 remote
, 'keystone', client_public_with_id
,
223 cluster
=cluster_name
,
225 logger
=log
.getChild(client
),
227 cwd
=get_keystone_dir(ctx
),
232 # start the admin endpoint
233 client_admin_with_id
= 'keystone.admin' + '.' + client_id
235 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[client
]
236 run_cmd
= get_keystone_venved_cmd(ctx
, 'keystone-wsgi-admin',
237 [ '--host', admin_host
, '--port', str(admin_port
),
238 run
.Raw('& { read; kill %1; }')
241 ctx
.daemons
.add_daemon(
242 remote
, 'keystone', client_admin_with_id
,
243 cluster
=cluster_name
,
245 logger
=log
.getChild(client
),
247 cwd
=get_keystone_dir(ctx
),
252 # sleep driven synchronization
253 run_in_keystone_venv(ctx
, client
, [ 'sleep', '15' ])
257 log
.info('Stopping Keystone admin instance')
258 ctx
.daemons
.get_daemon('keystone', client_admin_with_id
,
261 log
.info('Stopping Keystone public instance')
262 ctx
.daemons
.get_daemon('keystone', client_public_with_id
,
266 def dict_to_args(specials
, items
):
269 [(key1, val1), (special, val_special), (key3, val3) ]
271 [ '--key1', 'val1', '--key3', 'val3', 'val_special' ]
274 special_vals
= OrderedDict((k
, '') for k
in specials
.split(','))
276 if k
in special_vals
:
279 args
.append('--{k}'.format(k
=k
))
281 args
.extend(arg
for arg
in special_vals
.values() if arg
)
284 def run_section_cmds(ctx
, cclient
, section_cmd
, specials
,
285 section_config_list
):
286 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
289 ( 'os-username', 'admin' ),
290 ( 'os-password', 'ADMIN' ),
291 ( 'os-user-domain-id', 'default' ),
292 ( 'os-project-name', 'admin' ),
293 ( 'os-project-domain-id', 'default' ),
294 ( 'os-identity-api-version', '3' ),
295 ( 'os-auth-url', 'http://{host}:{port}/v3'.format(host
=admin_host
,
299 for section_item
in section_config_list
:
300 run_in_keystone_venv(ctx
, cclient
,
301 [ 'openstack' ] + section_cmd
.split() +
302 dict_to_args(specials
, auth_section
+ list(section_item
.items())) +
305 def create_endpoint(ctx
, cclient
, service
, url
, adminurl
=None):
306 endpoint_sections
= [
307 {'service': service
, 'interface': 'public', 'url': url
},
310 endpoint_sections
.append(
311 {'service': service
, 'interface': 'admin', 'url': adminurl
}
313 run_section_cmds(ctx
, cclient
, 'endpoint create',
314 'service,interface,url',
317 @contextlib.contextmanager
318 def fill_keystone(ctx
, config
):
319 assert isinstance(config
, dict)
321 for (cclient
, cconfig
) in config
.items():
322 public_host
, public_port
= ctx
.keystone
.public_endpoints
[cclient
]
323 url
= 'http://{host}:{port}/v3'.format(host
=public_host
,
325 admin_host
, admin_port
= ctx
.keystone
.admin_endpoints
[cclient
]
326 admin_url
= 'http://{host}:{port}/v3'.format(host
=admin_host
,
328 opts
= {'password': 'ADMIN',
330 'project-name': 'admin',
331 'role-name': 'admin',
332 'service-name': 'keystone',
333 'region-id': 'RegionOne',
334 'admin-url': admin_url
,
336 bootstrap_args
= chain
.from_iterable(('--bootstrap-{}'.format(k
), v
)
337 for k
, v
in opts
.items())
338 run_in_keystone_venv(ctx
, cclient
,
339 ['keystone-manage', 'bootstrap'] +
340 list(bootstrap_args
))
342 # configure tenants/projects
343 run_section_cmds(ctx
, cclient
, 'domain create', 'name',
344 cconfig
.get('domains', []))
345 run_section_cmds(ctx
, cclient
, 'project create', 'name',
346 cconfig
.get('projects', []))
347 run_section_cmds(ctx
, cclient
, 'user create', 'name',
348 cconfig
.get('users', []))
349 run_section_cmds(ctx
, cclient
, 'role create', 'name',
350 cconfig
.get('roles', []))
351 run_section_cmds(ctx
, cclient
, 'role add', 'name',
352 cconfig
.get('role-mappings', []))
353 run_section_cmds(ctx
, cclient
, 'service create', 'type',
354 cconfig
.get('services', []))
356 # for the deferred endpoint creation; currently it's used in rgw.py
357 ctx
.keystone
.create_endpoint
= create_endpoint
359 # sleep driven synchronization -- just in case
360 run_in_keystone_venv(ctx
, cclient
, [ 'sleep', '3' ])
366 def assign_ports(ctx
, config
, initial_port
):
368 Assign port numbers starting from @initial_port
372 for remote
, roles_for_host
in ctx
.cluster
.remotes
.items():
373 for role
in roles_for_host
:
375 role_endpoints
[role
] = (remote
.name
.split('@')[1], port
)
378 return role_endpoints
380 @contextlib.contextmanager
381 def task(ctx
, config
):
383 Deploy and configure Keystone
385 Example of configuration:
395 description: Default Domain
398 description: Admin Tenant
403 roles: [ name: admin, name: Member ]
411 description: Keystone Identity Service
414 description: Swift Service
416 assert config
is None or isinstance(config
, list) \
417 or isinstance(config
, dict), \
418 "task keystone only supports a list or dictionary for configuration"
420 if not hasattr(ctx
, 'tox'):
421 raise ConfigError('keystone must run after the tox task')
423 all_clients
= ['client.{id}'.format(id=id_
)
424 for id_
in teuthology
.all_roles_of_type(ctx
.cluster
, 'client')]
427 if isinstance(config
, list):
428 config
= dict.fromkeys(config
)
430 log
.debug('Keystone config is %s', config
)
432 ctx
.keystone
= argparse
.Namespace()
433 ctx
.keystone
.public_endpoints
= assign_ports(ctx
, config
, 5000)
434 ctx
.keystone
.admin_endpoints
= assign_ports(ctx
, config
, 35357)
436 with contextutil
.nested(
437 lambda: download(ctx
=ctx
, config
=config
),
438 lambda: install_packages(ctx
=ctx
, config
=config
),
439 lambda: setup_venv(ctx
=ctx
, config
=config
),
440 lambda: configure_instance(ctx
=ctx
, config
=config
),
441 lambda: run_keystone(ctx
=ctx
, config
=config
),
442 lambda: fill_keystone(ctx
=ctx
, config
=config
),