diff --git a/devstack/lib/magnum b/devstack/lib/magnum index 76ec9dee67..ed80ab2d97 100644 --- a/devstack/lib/magnum +++ b/devstack/lib/magnum @@ -216,8 +216,8 @@ function create_magnum_conf { --os-identity-api-version 3 role add \ --user $trustee_domain_admin_id --domain $trustee_domain_id \ admin - iniset $MAGNUM_CONF trust trustee_domain_id $trustee_domain_id - iniset $MAGNUM_CONF trust trustee_domain_admin_id $trustee_domain_admin_id + iniset $MAGNUM_CONF trust trustee_domain_name magnum + iniset $MAGNUM_CONF trust trustee_domain_admin_name trustee_domain_admin iniset $MAGNUM_CONF trust trustee_domain_admin_password $MAGNUM_TRUSTEE_DOMAIN_ADMIN_PASSWORD iniset $MAGNUM_CONF cinder_client region_name $REGION_NAME diff --git a/doc/source/dev/manual-devstack.rst b/doc/source/dev/manual-devstack.rst index a0b7bdb5a8..fef1d09c58 100644 --- a/doc/source/dev/manual-devstack.rst +++ b/doc/source/dev/manual-devstack.rst @@ -206,11 +206,11 @@ Configure magnum:: /etc/magnum/magnum.conf # set trustee domain id - sudo sed -i "s/#trustee_domain_id\s*=.*/trustee_domain_id=${TRUSTEE_DOMAIN_ID}/" \ + sudo sed -i "s/#trustee_domain_name\s*=.*/trustee_domain_name=magnum/" \ /etc/magnum/magnum.conf # set trustee domain admin id - sudo sed -i "s/#trustee_domain_admin_id\s*=.*/trustee_domain_admin_id=${TRUSTEE_DOMAIN_ADMIN_ID}/" \ + sudo sed -i "s/#trustee_domain_admin_name\s*=.*/trustee_domain_admin_name=trustee_domain_admin/" \ /etc/magnum/magnum.conf # set trustee domain admin password diff --git a/magnum/common/keystone.py b/magnum/common/keystone.py index 869761cb60..4195f30a33 100644 --- a/magnum/common/keystone.py +++ b/magnum/common/keystone.py @@ -33,9 +33,20 @@ LOG = logging.getLogger(__name__) trust_opts = [ cfg.StrOpt('trustee_domain_id', help=_('Id of the domain to create trustee for bays')), + cfg.StrOpt('trustee_domain_name', + help=_('Name of the domain to create trustee for bays')), cfg.StrOpt('trustee_domain_admin_id', help=_('Id of the admin with roles sufficient to manage users' ' in the trustee_domain')), + cfg.StrOpt('trustee_domain_admin_name', + help=_('Name of the admin with roles sufficient to manage users' + ' in the trustee_domain')), + cfg.StrOpt('trustee_domain_admin_domain_id', + help=_('Id of the domain admin user\'s domain.' + ' trustee_domain_id is used by default')), + cfg.StrOpt('trustee_domain_admin_domain_name', + help=_('Name of the domain admin user\'s domain.' + ' trustee_domain_name is used by default')), cfg.StrOpt('trustee_domain_admin_password', secret=True, help=_('Password of trustee_domain_admin')), cfg.ListOpt('roles', @@ -71,7 +82,10 @@ class KeystoneClientV3(object): def __init__(self, context): self.context = context self._client = None + self._domain_admin_auth = None + self._domain_admin_session = None self._domain_admin_client = None + self._trustee_domain_id = None self._session = None @property @@ -151,22 +165,62 @@ class KeystoneClientV3(object): return client @property - def domain_admin_client(self): - if not self._domain_admin_client: - auth = ka_v3.Password( + def domain_admin_auth(self): + user_domain_id = ( + CONF.trust.trustee_domain_admin_domain_id or + CONF.trust.trustee_domain_id + ) + user_domain_name = ( + CONF.trust.trustee_domain_admin_domain_name or + CONF.trust.trustee_domain_name + ) + if not self._domain_admin_auth: + self._domain_admin_auth = ka_v3.Password( auth_url=self.auth_url, user_id=CONF.trust.trustee_domain_admin_id, + username=CONF.trust.trustee_domain_admin_name, + user_domain_id=user_domain_id, + user_domain_name=user_domain_name, domain_id=CONF.trust.trustee_domain_id, + domain_name=CONF.trust.trustee_domain_name, password=CONF.trust.trustee_domain_admin_password) + return self._domain_admin_auth + + @property + def domain_admin_session(self): + if not self._domain_admin_session: session = ka_loading.session.Session().load_from_options( - auth=auth, + auth=self.domain_admin_auth, insecure=CONF[CFG_LEGACY_GROUP].insecure, cacert=CONF[CFG_LEGACY_GROUP].cafile, key=CONF[CFG_LEGACY_GROUP].keyfile, cert=CONF[CFG_LEGACY_GROUP].certfile) - self._domain_admin_client = kc_v3.Client(session=session) + self._domain_admin_session = session + return self._domain_admin_session + + @property + def domain_admin_client(self): + if not self._domain_admin_client: + self._domain_admin_client = kc_v3.Client( + session=self.domain_admin_session + ) return self._domain_admin_client + @property + def trustee_domain_id(self): + if not self._trustee_domain_id: + try: + access = self.domain_admin_auth.get_access( + self.domain_admin_session + ) + except kc_exception.Unauthorized: + LOG.error(_LE("Keystone client authentication failed")) + raise exception.AuthorizationFailure() + + self._trustee_domain_id = access.domain_id + + return self._trustee_domain_id + def create_trust(self, trustee_user): trustor_user_id = self.session.get_user_id() trustor_project_id = self.session.get_project_id() @@ -221,7 +275,8 @@ class KeystoneClientV3(object): LOG.exception(_LE('Failed to delete trust')) raise exception.TrustDeleteFailed(trust_id=bay.trust_id) - def create_trustee(self, username, password, domain_id): + def create_trustee(self, username, password): + domain_id = self.trustee_domain_id try: user = self.domain_admin_client.users.create( name=username, diff --git a/magnum/conductor/handlers/common/trust_manager.py b/magnum/conductor/handlers/common/trust_manager.py index 000220ccca..f5b4d42478 100644 --- a/magnum/conductor/handlers/common/trust_manager.py +++ b/magnum/conductor/handlers/common/trust_manager.py @@ -10,17 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg from oslo_log import log as logging from magnum.common import exception from magnum.common import utils from magnum.i18n import _LE -CONF = cfg.CONF -CONF.import_opt('trustee_domain_id', 'magnum.common.keystone', - group='trust') - LOG = logging.getLogger(__name__) @@ -30,7 +25,7 @@ def create_trustee_and_trust(osc, bay): trustee = osc.keystone().create_trustee( bay.uuid, password, - CONF.trust.trustee_domain_id) + ) bay.trustee_username = trustee.name bay.trustee_user_id = trustee.id bay.trustee_password = password diff --git a/magnum/conductor/template_definition.py b/magnum/conductor/template_definition.py index bc4280ed05..95f13f3aa7 100644 --- a/magnum/conductor/template_definition.py +++ b/magnum/conductor/template_definition.py @@ -76,7 +76,6 @@ docker_registry_opts = [ CONF = cfg.CONF CONF.register_opts(template_def_opts, group='bay') CONF.register_opts(docker_registry_opts, group='docker_registry') -CONF.import_opt('trustee_domain_id', 'magnum.common.keystone', group='trust') class ParameterMapping(object): @@ -339,6 +338,8 @@ class TemplateDefinition(object): class BaseTemplateDefinition(TemplateDefinition): def __init__(self): super(BaseTemplateDefinition, self).__init__() + self._osc = None + self.add_parameter('ssh_key_name', baymodel_attr='keypair_id', required=True) @@ -359,9 +360,16 @@ class BaseTemplateDefinition(TemplateDefinition): def template_path(self): pass + def get_osc(self, context): + if not self._osc: + self._osc = clients.OpenStackClients(context) + return self._osc + def get_params(self, context, baymodel, bay, **kwargs): + osc = self.get_osc(context) + extra_params = kwargs.pop('extra_params', {}) - extra_params['trustee_domain_id'] = CONF.trust.trustee_domain_id + extra_params['trustee_domain_id'] = osc.keystone().trustee_domain_id extra_params['trustee_user_id'] = bay.trustee_user_id extra_params['trustee_username'] = bay.trustee_username extra_params['trustee_password'] = bay.trustee_password @@ -484,7 +492,7 @@ class K8sTemplateDefinition(BaseTemplateDefinition): scale_mgr.get_removal_nodes(hosts)) extra_params['discovery_url'] = self.get_discovery_url(bay) - osc = clients.OpenStackClients(context) + osc = self.get_osc(context) extra_params['magnum_url'] = osc.magnum_url() if baymodel.tls_disabled: @@ -526,7 +534,7 @@ class AtomicK8sTemplateDefinition(K8sTemplateDefinition): extra_params['username'] = context.user_name extra_params['tenant_name'] = context.tenant - osc = clients.OpenStackClients(context) + osc = self.get_osc(context) extra_params['region_name'] = osc.cinder_region_name() return super(AtomicK8sTemplateDefinition, @@ -601,7 +609,7 @@ class AtomicSwarmTemplateDefinition(BaseTemplateDefinition): # HACK(apmelton) - This uses the user's bearer token, ideally # it should be replaced with an actual trust token with only # access to do what the template needs it to do. - osc = clients.OpenStackClients(context) + osc = self.get_osc(context) extra_params['magnum_url'] = osc.magnum_url() label_list = ['flannel_network_cidr', 'flannel_backend', @@ -664,7 +672,7 @@ class UbuntuMesosTemplateDefinition(BaseTemplateDefinition): # HACK(apmelton) - This uses the user's bearer token, ideally # it should be replaced with an actual trust token with only # access to do what the template needs it to do. - osc = clients.OpenStackClients(context) + osc = self.get_osc(context) extra_params['auth_url'] = context.auth_url extra_params['username'] = context.user_name extra_params['tenant_name'] = context.tenant diff --git a/magnum/tests/unit/common/test_keystone.py b/magnum/tests/unit/common/test_keystone.py index 1c90af2ade..e4211089fb 100644 --- a/magnum/tests/unit/common/test_keystone.py +++ b/magnum/tests/unit/common/test_keystone.py @@ -156,6 +156,43 @@ class KeystoneClientTest(base.TestCase): trustee_user='888888', role_names=['role3'], impersonation=True) + @mock.patch('magnum.common.keystone.KeystoneClientV3.trustee_domain_id') + def test_create_trustee(self, mock_tdi, mock_ks): + expected_username = '_username' + expected_password = '_password' + expected_domain = '_expected_trustee_domain_id' + mock_tdi.__get__ = mock.MagicMock(return_value=expected_domain) + + ks_client = keystone.KeystoneClientV3(self.ctx) + ks_client.create_trustee( + username=expected_username, + password=expected_password, + ) + mock_ks.return_value.users.create.assert_called_once_with( + name=expected_username, + password=expected_password, + domain=expected_domain, + ) + + @mock.patch('magnum.common.keystone.KeystoneClientV3.domain_admin_auth') + @mock.patch('magnum.common.keystone.KeystoneClientV3.domain_admin_session') + def test_trustee_domain_id(self, mock_session, mock_auth, mock_ks): + expected_domain_id = '_expected_domain_id' + _mock_session = mock.MagicMock() + mock_session.__get__ = mock.MagicMock(return_value=_mock_session) + _mock_auth = mock.MagicMock() + mock_auth.__get__ = mock.MagicMock(return_value=_mock_auth) + mock_access = mock.MagicMock() + mock_access.domain_id = expected_domain_id + _mock_auth.get_access.return_value = mock_access + + ks_client = keystone.KeystoneClientV3(self.ctx) + self.assertEqual(expected_domain_id, ks_client.trustee_domain_id) + + _mock_auth.get_access.assert_called_once_with( + _mock_session + ) + def test_get_validate_region_name(self, mock_ks): key = 'region_name' val = 'RegionOne' diff --git a/magnum/tests/unit/conductor/handlers/common/test_trust_manager.py b/magnum/tests/unit/conductor/handlers/common/test_trust_manager.py index f28ac76272..1ad1afe90b 100644 --- a/magnum/tests/unit/conductor/handlers/common/test_trust_manager.py +++ b/magnum/tests/unit/conductor/handlers/common/test_trust_manager.py @@ -14,7 +14,6 @@ import mock from mock import patch -from oslo_config import fixture from magnum.common import exception from magnum.conductor.handlers.common import trust_manager @@ -35,7 +34,6 @@ class TrustManagerTestCase(base.BaseTestCase): @patch('magnum.common.utils.generate_password') def test_create_trustee_and_trust(self, mock_generate_password): mock_password = "password_mock" - mock_trustee_domain_id = 'trustee_domain_id_mock' mock_generate_password.return_value = mock_password mock_bay = mock.MagicMock() mock_bay.uuid = 'mock_bay_uuid' @@ -47,8 +45,6 @@ class TrustManagerTestCase(base.BaseTestCase): mock_trust.id = 'mock_trust_id' self.osc.keystone.return_value = mock_keystone - fixture.Config().config(group='trust', - trustee_domain_id=mock_trustee_domain_id) mock_keystone.create_trustee.return_value = mock_trustee mock_keystone.create_trust.return_value = mock_trust @@ -58,7 +54,6 @@ class TrustManagerTestCase(base.BaseTestCase): mock_keystone.create_trustee.assert_called_once_with( mock_bay.uuid, mock_password, - mock_trustee_domain_id, ) mock_keystone.create_trust.assert_called_once_with( mock_trustee.id, diff --git a/magnum/tests/unit/conductor/handlers/test_k8s_bay_conductor.py b/magnum/tests/unit/conductor/handlers/test_k8s_bay_conductor.py index 5bed7172bc..91a74825c3 100644 --- a/magnum/tests/unit/conductor/handlers/test_k8s_bay_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_k8s_bay_conductor.py @@ -65,9 +65,6 @@ class TestBayConductorWithK8s(base.TestCase): 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', 'trust_id': 'bd11efc5-d4e2-4dac-bbce-25e348ddf7de', } - cfg.CONF.set_override('trustee_domain_id', - '3527620c-b220-4f37-9ebc-6e63a81a9b2f', - group='trust') self.context.auth_url = 'http://192.168.10.10:5000/v3' self.context.user_name = 'fake_user' self.context.tenant = 'fake_tenant' @@ -77,6 +74,9 @@ class TestBayConductorWithK8s(base.TestCase): self.mock_osc = mock.MagicMock() self.mock_osc.magnum_url.return_value = 'http://127.0.0.1:9511/v1' self.mock_osc.cinder_region_name.return_value = 'RegionOne' + self.mock_keystone = mock.MagicMock() + self.mock_keystone.trustee_domain_id = 'trustee_domain_id' + self.mock_osc.keystone.return_value = self.mock_keystone self.mock_osc_class.return_value = self.mock_osc @patch('magnum.objects.BayModel.get_by_uuid') @@ -150,7 +150,7 @@ class TestBayConductorWithK8s(base.TestCase): 'region_name': self.mock_osc.cinder_region_name.return_value, 'tls_disabled': False, 'registry_enabled': False, - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -207,7 +207,7 @@ class TestBayConductorWithK8s(base.TestCase): 'tenant_name': 'fake_tenant', 'tls_disabled': False, 'trust_id': 'bd11efc5-d4e2-4dac-bbce-25e348ddf7de', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', 'trustee_username': 'fake_trustee', @@ -250,7 +250,7 @@ class TestBayConductorWithK8s(base.TestCase): 'flannel_backend': 'vxlan', 'tls_disabled': False, 'registry_enabled': False, - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -299,7 +299,7 @@ class TestBayConductorWithK8s(base.TestCase): 'flannel_backend': 'vxlan', 'tls_disabled': False, 'registry_enabled': False, - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -422,7 +422,7 @@ class TestBayConductorWithK8s(base.TestCase): 'region_name': self.mock_osc.cinder_region_name.return_value, 'tls_disabled': False, 'registry_enabled': False, - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', diff --git a/magnum/tests/unit/conductor/handlers/test_mesos_bay_conductor.py b/magnum/tests/unit/conductor/handlers/test_mesos_bay_conductor.py index eed40ff726..86a5a1ab92 100644 --- a/magnum/tests/unit/conductor/handlers/test_mesos_bay_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_mesos_bay_conductor.py @@ -14,7 +14,6 @@ import mock from mock import patch -from oslo_config import cfg from oslo_service import loopingcall from magnum.conductor.handlers import bay_conductor @@ -63,9 +62,6 @@ class TestBayConductorWithMesos(base.TestCase): 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', 'trust_id': 'bd11efc5-d4e2-4dac-bbce-25e348ddf7de', } - cfg.CONF.set_override('trustee_domain_id', - '3527620c-b220-4f37-9ebc-6e63a81a9b2f', - group='trust') self.context.auth_url = 'http://192.168.10.10:5000/v3' self.context.user_name = 'mesos_user' self.context.tenant = 'admin' @@ -75,6 +71,9 @@ class TestBayConductorWithMesos(base.TestCase): self.addCleanup(osc_patcher.stop) self.mock_osc = mock.MagicMock() self.mock_osc.cinder_region_name.return_value = 'RegionOne' + self.mock_keystone = mock.MagicMock() + self.mock_keystone.trustee_domain_id = 'trustee_domain_id' + self.mock_osc.keystone.return_value = self.mock_keystone self.mock_osc_class.return_value = self.mock_osc @patch('magnum.objects.BayModel.get_by_uuid') @@ -102,7 +101,7 @@ class TestBayConductorWithMesos(base.TestCase): 'https_proxy': 'https_proxy', 'no_proxy': 'no_proxy', 'cluster_name': 'bay1', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -145,7 +144,7 @@ class TestBayConductorWithMesos(base.TestCase): 'number_of_slaves': 1, 'number_of_masters': 1, 'cluster_name': 'bay1', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', diff --git a/magnum/tests/unit/conductor/handlers/test_swarm_bay_conductor.py b/magnum/tests/unit/conductor/handlers/test_swarm_bay_conductor.py index ade0ec2fb6..0b028ae53b 100644 --- a/magnum/tests/unit/conductor/handlers/test_swarm_bay_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_swarm_bay_conductor.py @@ -63,14 +63,14 @@ class TestBayConductorWithSwarm(base.TestCase): 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', 'trust_id': 'bd11efc5-d4e2-4dac-bbce-25e348ddf7de' } - cfg.CONF.set_override('trustee_domain_id', - '3527620c-b220-4f37-9ebc-6e63a81a9b2f', - group='trust') osc_patcher = mock.patch('magnum.common.clients.OpenStackClients') self.mock_osc_class = osc_patcher.start() self.addCleanup(osc_patcher.stop) self.mock_osc = mock.MagicMock() self.mock_osc.magnum_url.return_value = 'http://127.0.0.1:9511/v1' + self.mock_keystone = mock.MagicMock() + self.mock_keystone.trustee_domain_id = 'trustee_domain_id' + self.mock_osc.keystone.return_value = self.mock_keystone self.mock_osc_class.return_value = self.mock_osc self.context.auth_url = 'http://192.168.10.10:5000/v3' @@ -108,7 +108,7 @@ class TestBayConductorWithSwarm(base.TestCase): 'flannel_network_cidr': '10.101.0.0/16', 'flannel_network_subnetlen': '26', 'flannel_backend': 'vxlan', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -158,7 +158,7 @@ class TestBayConductorWithSwarm(base.TestCase): 'flannel_network_cidr': '10.101.0.0/16', 'flannel_network_subnetlen': '26', 'flannel_backend': 'vxlan', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656', @@ -201,7 +201,7 @@ class TestBayConductorWithSwarm(base.TestCase): 'flannel_network_cidr': u'10.101.0.0/16', 'flannel_network_subnetlen': u'26', 'flannel_backend': u'vxlan', - 'trustee_domain_id': '3527620c-b220-4f37-9ebc-6e63a81a9b2f', + 'trustee_domain_id': self.mock_keystone.trustee_domain_id, 'trustee_username': 'fake_trustee', 'trustee_password': 'fake_trustee_password', 'trustee_user_id': '7b489f04-b458-4541-8179-6a48a553e656',