keystone  18.0.0
About: OpenStack Keystone (Core Service: Identity) provides an authentication and authorization service for other OpenStack services. Provides a catalog of endpoints for all OpenStack services.
The "Victoria" series (maintained release).
  Fossies Dox: keystone-18.0.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

bootstrap.py
Go to the documentation of this file.
1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
11 # under the License.
12 
13 import uuid
14 
15 from oslo_log import log
16 
17 from keystone.common import driver_hints
18 from keystone.common import provider_api
19 import keystone.conf
20 from keystone import exception
21 from keystone.server import backends
22 
23 CONF = keystone.conf.CONF
24 LOG = log.getLogger(__name__)
25 PROVIDERS = provider_api.ProviderAPIs
26 
27 
28 class Bootstrapper(object):
29 
30  def __init__(self):
31  backends.load_backends()
32 
33  self.admin_password = None
34  self.admin_username = None
35 
36  self.project_id = None
37  self.project_name = None
38 
39  self.reader_role_id = None
40  self.reader_role_name = 'reader'
41 
42  self.member_role_id = None
43  self.member_role_name = 'member'
44 
45  self.admin_role_id = None
46  self.admin_role_name = None
47 
48  self.region_id = None
49 
50  self.service_name = None
51  self.public_url = None
52  self.internal_url = None
53  self.admin_url = None
54  self.endpoints = {}
55 
56  self.default_domain_id = None
57  self.admin_user_id = None
58 
59  self.immutable_roles = False
60 
61  def bootstrap(self):
62  # NOTE(morganfainberg): Ensure the default domain is in-fact created
64  self._bootstrap_project()
71  self._bootstrap_region()
72  self._bootstrap_catalog()
73 
75  default_domain = {
76  'id': CONF.identity.default_domain_id,
77  'name': 'Default',
78  'enabled': True,
79  'description': 'The default domain'
80  }
81  try:
82  PROVIDERS.resource_api.create_domain(
83  domain_id=default_domain['id'],
84  domain=default_domain)
85  LOG.info('Created domain %s', default_domain['id'])
86  except exception.Conflict:
87  # NOTE(morganfainberg): Domain already exists, continue on.
88  LOG.info('Domain %s already exists, skipping creation.',
89  default_domain['id'])
90 
91  self.default_domain_id = default_domain['id']
92 
93  def _bootstrap_project(self):
94  try:
95  project_id = uuid.uuid4().hex
96  project = {
97  'enabled': True,
98  'id': project_id,
99  'domain_id': self.default_domain_id,
100  'description': 'Bootstrap project for initializing the cloud.',
101  'name': self.project_name
102  }
103  PROVIDERS.resource_api.create_project(project_id, project)
104  LOG.info('Created project %s', self.project_name)
105  except exception.Conflict:
106  LOG.info('Project %s already exists, skipping creation.',
107  self.project_name)
108  project = PROVIDERS.resource_api.get_project_by_name(
110  )
111 
112  self.project_id = project['id']
113 
114  def _ensure_role_exists(self, role_name):
115  # NOTE(morganfainberg): Do not create the role if it already exists.
116  try:
117  role_id = uuid.uuid4().hex
118  role = {'name': role_name, 'id': role_id}
119  if self.immutable_roles:
120  role['options'] = {'immutable': True}
121  role = PROVIDERS.role_api.create_role(role_id, role)
122  LOG.info('Created role %s', role_name)
123  if not self.immutable_roles:
124  LOG.warning("Role %(role)s was created as a mutable role. It "
125  "is recommended to make this role immutable by "
126  "adding the 'immutable' resource option to this "
127  "role, or re-running this command without "
128  "--no-immutable-role.", {'role': role_name})
129  return role
130  except exception.Conflict:
131  LOG.info('Role %s exists, skipping creation.', role_name)
132  # NOTE(davechen): There is no backend method to get the role
133  # by name, so build the hints to list the roles and filter by
134  # name instead.
135  hints = driver_hints.Hints()
136  hints.add_filter('name', role_name)
137  # Only return global roles, domain-specific roles can't be used in
138  # system assignments and bootstrap isn't designed to work with
139  # domain-specific roles.
140  hints.add_filter('domain_id', None)
141 
142  # NOTE(lbragstad): Global roles are unique based on name. At this
143  # point we should be safe to return the first, and only, element in
144  # the list.
145  return PROVIDERS.role_api.list_roles(hints)[0]
146 
147  def _ensure_implied_role(self, prior_role_id, implied_role_id):
148  try:
149  PROVIDERS.role_api.create_implied_role(prior_role_id,
150  implied_role_id)
151  LOG.info(
152  'Created implied role where %s implies %s',
153  prior_role_id,
154  implied_role_id
155  )
156  except exception.Conflict:
157  LOG.info(
158  'Implied role where %s implies %s exists, skipping creation.',
159  prior_role_id,
160  implied_role_id
161  )
162 
164  role = self._ensure_role_exists(self.reader_role_name)
165  self.reader_role_id = role['id']
166 
168  role = self._ensure_role_exists(self.member_role_name)
169  self.member_role_id = role['id']
171 
173  role = self._ensure_role_exists(self.admin_role_name)
174  self.admin_role_id = role['id']
176 
178  # NOTE(morganfainberg): Do not create the user if it already exists.
179  try:
180  user = PROVIDERS.identity_api.get_user_by_name(
182  )
183  LOG.info('User %s already exists, skipping creation.',
184  self.admin_username)
185 
186  # If the user is not enabled, re-enable them. This also helps
187  # provide some useful logging output later.
188  update = {}
189  enabled = user['enabled']
190  if not enabled:
191  update['enabled'] = True
192 
193  try:
194  PROVIDERS.identity_api.driver.authenticate(
195  user['id'], self.admin_password
196  )
197  except AssertionError:
198  # This means that authentication failed and that we need to
199  # update the user's password. This is going to persist a
200  # revocation event that will make all previous tokens for the
201  # user invalid, which is OK because it falls within the scope
202  # of revocation. If a password changes, we shouldn't be able to
203  # use tokens obtained with an old password.
204  update['password'] = self.admin_password
205 
206  # Only make a call to update the user if the password has changed
207  # or the user was previously disabled. This allows bootstrap to act
208  # as a recovery tool, without having to create a new user.
209  if update:
210  user = PROVIDERS.identity_api.update_user(
211  user['id'], update
212  )
213  LOG.info('Reset password for user %s.', self.admin_username)
214  if not enabled and user['enabled']:
215  # Although we always try to enable the user, this log
216  # message only makes sense if we know that the user was
217  # previously disabled.
218  LOG.info('Enabled user %s.', self.admin_username)
219  except exception.UserNotFound:
220  user = PROVIDERS.identity_api.create_user(
221  user_ref={
222  'name': self.admin_username,
223  'enabled': True,
224  'domain_id': self.default_domain_id,
225  'password': self.admin_password
226  }
227  )
228  LOG.info('Created user %s', self.admin_username)
229 
230  self.admin_user_id = user['id']
231 
233  try:
234  PROVIDERS.assignment_api.add_role_to_user_and_project(
235  user_id=self.admin_user_id,
236  project_id=self.project_id,
237  role_id=self.admin_role_id
238  )
239  LOG.info('Granted role %(role)s on project %(project)s to '
240  'user %(username)s.',
241  {'role': self.admin_role_name,
242  'project': self.project_name,
243  'username': self.admin_username})
244  except exception.Conflict:
245  LOG.info('User %(username)s already has role %(role)s on '
246  'project %(project)s.',
247  {'username': self.admin_username,
248  'role': self.admin_role_name,
249  'project': self.project_name})
250 
252  # NOTE(lbragstad): We need to make sure a user has at least one role on
253  # the system. Otherwise it's possible for administrators to lock
254  # themselves out of system-level APIs in their deployment. This is
255  # considered backwards compatible because even if the assignment
256  # exists, it needs to be enabled through oslo.policy configuration
257  # options to be enforced.
258  try:
259  PROVIDERS.assignment_api.create_system_grant_for_user(
260  self.admin_user_id, self.admin_role_id
261  )
262  LOG.info('Granted role %(role)s on the system to user'
263  ' %(username)s.',
264  {'role': self.admin_role_name,
265  'username': self.admin_username})
266  except exception.Conflict:
267  LOG.info('User %(username)s already has role %(role)s on '
268  'the system.',
269  {'username': self.admin_username,
270  'role': self.admin_role_name})
271 
272  def _bootstrap_region(self):
273  if self.region_id:
274  try:
275  PROVIDERS.catalog_api.create_region(
276  region_ref={'id': self.region_id}
277  )
278  LOG.info('Created region %s', self.region_id)
279  except exception.Conflict:
280  LOG.info('Region %s exists, skipping creation.',
281  self.region_id)
282 
284  if self.public_url or self.admin_url or self.internal_url:
285  hints = driver_hints.Hints()
286  hints.add_filter('type', 'identity')
287  services = PROVIDERS.catalog_api.list_services(hints)
288 
289  if services:
290  service = services[0]
291 
292  hints = driver_hints.Hints()
293  hints.add_filter('service_id', service['id'])
294  if self.region_id:
295  hints.add_filter('region_id', self.region_id)
296 
297  endpoints = PROVIDERS.catalog_api.list_endpoints(hints)
298  else:
299  service_id = uuid.uuid4().hex
300  service = {
301  'id': service_id, 'name': self.service_name,
302  'type': 'identity', 'enabled': True
303  }
304 
305  PROVIDERS.catalog_api.create_service(service_id, service)
306  endpoints = []
307 
308  self.service_id = service['id']
309  available_interfaces = {e['interface']: e for e in endpoints}
310  expected_endpoints = {'public': self.public_url,
311  'internal': self.internal_url,
312  'admin': self.admin_url}
313 
314  for interface, url in expected_endpoints.items():
315  if not url:
316  # not specified to bootstrap command
317  continue
318 
319  try:
320  endpoint_ref = available_interfaces[interface]
321  except KeyError:
322  endpoint_ref = {'id': uuid.uuid4().hex,
323  'interface': interface,
324  'url': url,
325  'service_id': self.service_id,
326  'enabled': True}
327 
328  if self.region_id:
329  endpoint_ref['region_id'] = self.region_id
330 
331  PROVIDERS.catalog_api.create_endpoint(
332  endpoint_id=endpoint_ref['id'],
333  endpoint_ref=endpoint_ref)
334 
335  LOG.info('Created %(interface)s endpoint %(url)s',
336  {'interface': interface, 'url': url})
337  else:
338  endpoint_ref['url'] = url
339  PROVIDERS.catalog_api.update_endpoint(
340  endpoint_id=endpoint_ref['id'],
341  endpoint_ref=endpoint_ref)
342  LOG.info('%s endpoint updated', interface)
343 
344  self.endpoints[interface] = endpoint_ref['id']
keystone.cmd.bootstrap.Bootstrapper._bootstrap_system_role_assignment
def _bootstrap_system_role_assignment(self)
Definition: bootstrap.py:251
keystone.cmd.bootstrap.Bootstrapper._bootstrap_admin_user
def _bootstrap_admin_user(self)
Definition: bootstrap.py:177
keystone.cmd.bootstrap.Bootstrapper.bootstrap
def bootstrap(self)
Definition: bootstrap.py:61
keystone.exception.UserNotFound
Definition: exception.py:469
keystone.cmd.bootstrap.Bootstrapper._bootstrap_reader_role
def _bootstrap_reader_role(self)
Definition: bootstrap.py:163
keystone.cmd.bootstrap.Bootstrapper._ensure_implied_role
def _ensure_implied_role(self, prior_role_id, implied_role_id)
Definition: bootstrap.py:147
keystone.cmd.bootstrap.Bootstrapper.region_id
region_id
Definition: bootstrap.py:48
keystone.cmd.bootstrap.Bootstrapper.project_name
project_name
Definition: bootstrap.py:37
keystone.cmd.bootstrap.Bootstrapper.__init__
def __init__(self)
Definition: bootstrap.py:30
keystone.cmd.bootstrap.Bootstrapper._bootstrap_catalog
def _bootstrap_catalog(self)
Definition: bootstrap.py:283
keystone.cmd.bootstrap.Bootstrapper.reader_role_name
reader_role_name
Definition: bootstrap.py:40
keystone.cmd.bootstrap.Bootstrapper.member_role_name
member_role_name
Definition: bootstrap.py:43
keystone.cmd.bootstrap.Bootstrapper._bootstrap_member_role
def _bootstrap_member_role(self)
Definition: bootstrap.py:167
keystone.cmd.bootstrap.Bootstrapper.service_id
service_id
Definition: bootstrap.py:308
keystone.cmd.bootstrap.Bootstrapper.immutable_roles
immutable_roles
Definition: bootstrap.py:59
keystone.cmd.bootstrap.Bootstrapper._bootstrap_admin_role
def _bootstrap_admin_role(self)
Definition: bootstrap.py:172
keystone.cmd.bootstrap.Bootstrapper.default_domain_id
default_domain_id
Definition: bootstrap.py:56
keystone.cmd.bootstrap.Bootstrapper.endpoints
endpoints
Definition: bootstrap.py:54
keystone.cmd.bootstrap.Bootstrapper._ensure_role_exists
def _ensure_role_exists(self, role_name)
Definition: bootstrap.py:114
keystone.cmd.bootstrap.Bootstrapper._bootstrap_project
def _bootstrap_project(self)
Definition: bootstrap.py:93
keystone.exception.Conflict
Definition: exception.py:559
keystone.cmd.bootstrap.Bootstrapper.project_id
project_id
Definition: bootstrap.py:36
keystone.cmd.bootstrap.Bootstrapper.admin_username
admin_username
Definition: bootstrap.py:34
keystone.cmd.bootstrap.Bootstrapper._bootstrap_region
def _bootstrap_region(self)
Definition: bootstrap.py:272
keystone.server
Definition: __init__.py:1
keystone.cmd.bootstrap.Bootstrapper.admin_role_name
admin_role_name
Definition: bootstrap.py:46
keystone.conf
Definition: __init__.py:1
keystone.cmd.bootstrap.Bootstrapper.admin_password
admin_password
Definition: bootstrap.py:33
keystone.cmd.bootstrap.Bootstrapper.admin_role_id
admin_role_id
Definition: bootstrap.py:45
keystone.cmd.bootstrap.Bootstrapper.member_role_id
member_role_id
Definition: bootstrap.py:42
keystone.common
Definition: __init__.py:1
keystone.cmd.bootstrap.Bootstrapper.internal_url
internal_url
Definition: bootstrap.py:52
keystone.cmd.bootstrap.Bootstrapper.service_name
service_name
Definition: bootstrap.py:50
keystone.cmd.bootstrap.Bootstrapper.reader_role_id
reader_role_id
Definition: bootstrap.py:39
keystone.cmd.bootstrap.Bootstrapper.admin_user_id
admin_user_id
Definition: bootstrap.py:57
keystone.cmd.bootstrap.Bootstrapper
Definition: bootstrap.py:28
keystone.cmd.bootstrap.Bootstrapper.public_url
public_url
Definition: bootstrap.py:51
keystone.cmd.bootstrap.Bootstrapper._bootstrap_project_role_assignment
def _bootstrap_project_role_assignment(self)
Definition: bootstrap.py:232
keystone.cmd.bootstrap.Bootstrapper.admin_url
admin_url
Definition: bootstrap.py:53
keystone.cmd.bootstrap.Bootstrapper._bootstrap_default_domain
def _bootstrap_default_domain(self)
Definition: bootstrap.py:74