diff options
author | Sundar Nadathur <sundar.nadathur@intel.com> | 2019-07-30 18:54:48 -0700 |
---|---|---|
committer | Sundar Nadathur <sundar.nadathur@intel.com> | 2020-03-21 12:03:37 -0700 |
commit | 0c52730f6a138ce3b40efd6a0bd2809b2c41dada (patch) | |
tree | 8ed6b8e05503a66c0d7943757b13fb4072094ecb /nova/accelerator | |
parent | c071741d565950ba0a6b43f7b66aad0bdbaf1dff (diff) | |
download | nova-0c52730f6a138ce3b40efd6a0bd2809b2c41dada.tar.gz |
Add Cyborg device profile groups to request spec.
Find the name of the device profile, if any, in flavor extra specs.
Get its profile groups (equiv to flavor request groups) from Cyborg.
Parse/validate them similar to extra_specs.
Generate RequestGroup objects and add them to the request spec
(in requested_resources field, following precedent).
Change-Id: Icd2ee9024dd4af0a7eb105eca14df8e458e9de77
Blueprint: nova-cyborg-interaction
Diffstat (limited to 'nova/accelerator')
-rw-r--r-- | nova/accelerator/cyborg.py | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/nova/accelerator/cyborg.py b/nova/accelerator/cyborg.py index dca3688665..61aa78c86f 100644 --- a/nova/accelerator/cyborg.py +++ b/nova/accelerator/cyborg.py @@ -12,11 +12,47 @@ # License for the specific language governing permissions and limitations # under the License. +import six + from oslo_log import log as logging +from keystoneauth1 import exceptions as ks_exc + +from nova import exception +from nova.i18n import _ +from nova import objects +from nova.scheduler import utils as schedutils from nova import service_auth from nova import utils +""" + Note on object relationships: + 1 device profile (DP) has D >= 1 request groups (just as a flavor + has many request groups). + Each DP request group corresponds to exactly 1 numbered request + group (RG) in the request spec. + Each numbered RG corresponds to exactly one resource provider (RP). + A DP request group may request A >= 1 accelerators, and so result + in the creation of A ARQs. + Each ARQ corresponds to exactly 1 DP request group. + + A device profile is a dictionary: + { "name": "mydpname", + "uuid": <uuid>, + "groups": [ <device_profile_request_group> ] + } + + A device profile group is a dictionary too: + { "resources:CUSTOM_ACCELERATOR_FPGA": "2", + "resources:CUSTOM_LOCAL_MEMORY": "1", + "trait:CUSTOM_INTEL_PAC_ARRIA10": "required", + "trait:CUSTOM_FUNCTION_NAME_FALCON_GZIP_1_1": "required", + # 0 or more Cyborg properties + "accel:bitstream_id": "FB021995_BF21_4463_936A_02D49D4DB5E5" + } + + See cyborg/cyborg/objects/device_profile.py for more details. +""" LOG = logging.getLogger(__name__) @@ -25,8 +61,87 @@ def get_client(context): return _CyborgClient(context) +def get_device_profile_group_requester_id(dp_group_id): + """Return the value to use in objects.RequestGroup.requester_id. + + The requester_id is used to match device profile groups from + Cyborg to the request groups in request spec. + + :param dp_group_id: The index of the request group in the device profile. + """ + req_id = "device_profile_" + str(dp_group_id) + return req_id + + +def get_device_profile_request_groups(context, dp_name): + cyclient = get_client(context) + return cyclient.get_device_profile_groups(dp_name) + + class _CyborgClient(object): + DEVICE_PROFILE_URL = "/device_profiles" def __init__(self, context): auth = service_auth.get_auth_plugin(context) self._client = utils.get_ksa_adapter('accelerator', ksa_auth=auth) + + def _call_cyborg(self, func, *args, **kwargs): + resp = err_msg = None + try: + resp = func(*args, **kwargs) + if not resp: + msg = _('Invalid response from Cyborg: ') + err_msg = msg + str(resp) + except ks_exc.ClientException as exc: + err_msg = _('Could not communicate with Cyborg.') + LOG.exception('%s: %s', err_msg, six.text_type(exc)) + + return resp, err_msg + + def _get_device_profile_list(self, dp_name): + query = {"name": dp_name} + err_msg = None + + resp, err_msg = self._call_cyborg(self._client.get, + self.DEVICE_PROFILE_URL, params=query) + + if err_msg: + raise exception.DeviceProfileError(name=dp_name, msg=err_msg) + + return resp.json().get('device_profiles') + + def get_device_profile_groups(self, dp_name): + """Get list of profile group objects from the device profile. + + Cyborg API returns: {"device_profiles": [<device_profile>]} + See module notes above for further details. + + :param dp_name: string: device profile name + Expected to be valid, not None or ''. + :returns: [objects.RequestGroup] + :raises: DeviceProfileError + """ + dp_list = self._get_device_profile_list(dp_name) + if not dp_list: + msg = _('Expected 1 device profile but got nothing.') + raise exception.DeviceProfileError(name=dp_name, msg=msg) + if len(dp_list) != 1: + err = _('Expected 1 device profile but got %s.') % len(dp_list) + raise exception.DeviceProfileError(name=dp_name, msg=err) + + dp_groups = dp_list[0]['groups'] + request_groups = [] + for dp_group_id, dp_group in enumerate(dp_groups): + req_id = get_device_profile_group_requester_id(dp_group_id) + rg = objects.RequestGroup(requester_id=req_id) + for key, val in dp_group.items(): + match = schedutils.ResourceRequest.XS_KEYPAT.match(key) + if not match: + continue # could be 'accel:foo=bar', skip it + prefix, _ignore, name = match.groups() + if prefix == schedutils.ResourceRequest.XS_RES_PREFIX: + rg.add_resource(rclass=name, amount=val) + elif prefix == schedutils.ResourceRequest.XS_TRAIT_PREFIX: + rg.add_trait(trait_name=name, trait_type=val) + request_groups.append(rg) + return request_groups |