def sync_from_callback(self, context, operation, res_type, res_id, resource_dict, **kwrags): object_type = odl_utils.neutronify(res_type.plural) try: if operation == odl_const.ODL_DELETE: self.out_of_sync |= not self.client.try_delete( object_type + '/' + res_id) else: if operation == odl_const.ODL_CREATE: urlpath = object_type method = 'post' elif operation == odl_const.ODL_UPDATE: urlpath = object_type + '/' + res_id method = 'put' self.client.sendjson(method, urlpath, resource_dict) except Exception: with excutils.save_and_reraise_exception(): LOG.error("Unable to perform %(operation)s on " "%(object_type)s %(res_id)s %(resource_dict)s", {'operation': operation, 'object_type': object_type, 'res_id': res_id, 'resource_dict': resource_dict}) self.out_of_sync = True # NOTE(yamahata) when security group is created, default rules # are also created. if (operation == odl_const.ODL_CREATE and res_type.singular == odl_const.ODL_SG): for rule in resource_dict[odl_const.ODL_SG][ odl_const.ODL_SG_RULES]: self.sync_from_callback( context, odl_const.ODL_CREATE, odl_call._RESOURCE_MAPPING[resources.SECURITY_GROUP_RULE], rule['id'], {odl_const.ODL_SG_RULE: rule})
def sync_resources(self, plugin, dbcontext, collection_name): """Sync objects from Neutron over to OpenDaylight. This will handle syncing networks, subnets, and ports from Neutron to OpenDaylight. It also filters out the requisite items which are not valid for create API operations. """ filter_cls = self.FILTER_MAP[collection_name] to_be_synced = [] obj_getter = getattr(plugin, 'get_%s' % collection_name) if collection_name == odl_const.ODL_SGS: resources = obj_getter(dbcontext, default_sg=True) else: resources = obj_getter(dbcontext) for resource in resources: try: # Convert underscores to dashes in the URL for ODL collection_name_url = odl_utils.neutronify(collection_name) urlpath = collection_name_url + '/' + resource['id'] self.client.sendjson('get', urlpath, None) except requests.exceptions.HTTPError as e: with excutils.save_and_reraise_exception() as ctx: if e.response.status_code == requests.codes.not_found: filter_cls.filter_create_attributes_with_plugin( resource, plugin, dbcontext) to_be_synced.append(resource) ctx.reraise = False else: # sync failed, indicate DB sync failure. # If other failures were raised by self.client.json, # then they would be alarmed first self.client.set_audit_failure('dbsync_failure') else: # TODO(yamahata): compare result with resource. # If they don't match, update it below pass if to_be_synced: key = collection_name[:-1] if len(to_be_synced) == 1 else ( collection_name) # Convert underscores to dashes in the URL for ODL collection_name_url = odl_utils.neutronify(collection_name) self.client.sendjson('post', collection_name_url, {key: to_be_synced})
def _test_thread_processing(self, operation, object_type, expected_calls=1): http_requests = { odl_const.ODL_CREATE: 'post', odl_const.ODL_UPDATE: 'put', odl_const.ODL_DELETE: 'delete' } status_codes = { odl_const.ODL_CREATE: requests.codes.created, odl_const.ODL_UPDATE: requests.codes.ok, odl_const.ODL_DELETE: requests.codes.no_content } http_request = http_requests[operation] status_code = status_codes[operation] self._call_operation_object(operation, object_type) context = self._get_mock_operation_context(object_type) url_object_type = utils.neutronify(object_type) if operation in [odl_const.ODL_UPDATE, odl_const.ODL_DELETE]: if object_type in [odl_const.ODL_SG, odl_const.ODL_SG_RULE]: uuid = context[object_type]['id'] else: uuid = context.current['id'] url = '%s/%ss/%s' % (cfg.CONF.ml2_odl.url, url_object_type, uuid) else: url = '%s/%ss' % (cfg.CONF.ml2_odl.url, url_object_type) if (object_type == odl_const.ODL_SG and operation == odl_const.ODL_CREATE): context = copy.deepcopy(context) context[odl_const.ODL_SG].pop('rules') if operation in [odl_const.ODL_CREATE, odl_const.ODL_UPDATE]: kwargs = { 'url': url, 'data': DataMatcher(operation, object_type, context) } else: kwargs = {'url': url, 'data': None} with mock.patch.object(self.thread.event, 'wait', return_value=False): self._test_operation(self.thread.run_sync_thread, status_code, expected_calls, http_request, **kwargs)
def sync_single_resource(self, operation, object_type, context): """Sync over a single resource from Neutron to OpenDaylight. Handle syncing a single operation over to OpenDaylight, and correctly filter attributes out which are not required for the requisite operation (create or update) being handled. """ # Convert underscores to dashes in the URL for ODL object_type_url = odl_utils.neutronify(object_type) try: obj_id = context.current['id'] if operation == odl_const.ODL_DELETE: self.out_of_sync |= not self.client.try_delete( object_type_url + '/' + obj_id) else: filter_cls = self.FILTER_MAP[object_type] if operation == odl_const.ODL_CREATE: urlpath = object_type_url method = 'post' attr_filter = filter_cls.filter_create_attributes elif operation == odl_const.ODL_UPDATE: urlpath = object_type_url + '/' + obj_id method = 'put' attr_filter = filter_cls.filter_update_attributes resource = copy.deepcopy(context.current) attr_filter(resource, context) self.client.sendjson(method, urlpath, {object_type_url[:-1]: resource}) except Exception: with excutils.save_and_reraise_exception(): LOG.error( "Unable to perform %(operation)s on " "%(object_type)s %(object_id)s", { 'operation': operation, 'object_type': object_type, 'object_id': obj_id }) self.out_of_sync = True # sync failed, indicate DB sync failure. # If other failures were raised by self.client.json, # then they would be alarmed first self.client.set_audit_failure('dbsync_failure')
def send_resource(self, operation, object_type, data): """Send over a single resource from Neutron to OpenDaylight. Prepare a rest call and send a single resource to ODL NB """ # Convert underscores to dashes in the URL for ODL object_type_url = utils.neutronify(object_type) obj_id = data['id'] if operation == odl_const.ODL_DELETE: self.client.try_delete(object_type_url + '/' + obj_id) else: if operation == odl_const.ODL_CREATE: urlpath = object_type_url method = 'post' elif operation == odl_const.ODL_UPDATE: urlpath = object_type_url + '/' + obj_id method = 'put' policy_data = qos_utils.convert_rules_format(data) self.client.sendjson(method, urlpath, {odl_const.ODL_QOS_POLICY: policy_data})
def _test_thread_processing(self, operation, object_type, expected_calls=1): http_requests = {odl_const.ODL_CREATE: 'post', odl_const.ODL_UPDATE: 'put', odl_const.ODL_DELETE: 'delete'} status_codes = {odl_const.ODL_CREATE: requests.codes.created, odl_const.ODL_UPDATE: requests.codes.ok, odl_const.ODL_DELETE: requests.codes.no_content} http_request = http_requests[operation] status_code = status_codes[operation] self._call_operation_object(operation, object_type) context = self._get_mock_operation_context(object_type) url_object_type = utils.neutronify(object_type) if operation in [odl_const.ODL_UPDATE, odl_const.ODL_DELETE]: if object_type in [odl_const.ODL_SG, odl_const.ODL_SG_RULE]: uuid = context[object_type]['id'] else: uuid = context.current['id'] url = '%s/%ss/%s' % (cfg.CONF.ml2_odl.url, url_object_type, uuid) else: url = '%s/%ss' % (cfg.CONF.ml2_odl.url, url_object_type) if (object_type == odl_const.ODL_SG and operation == odl_const.ODL_CREATE): context = copy.deepcopy(context) if operation in [odl_const.ODL_CREATE, odl_const.ODL_UPDATE]: kwargs = { 'url': url, 'data': DataMatcher(operation, object_type, context)} else: kwargs = {'url': url, 'data': None} self._test_operation(status_code, expected_calls, http_request, **kwargs)
def test_neutronify_empty(self): self.assertEqual('', utils.neutronify(''))
def test_neutronify(self): self.assertEqual('a-b-c', utils.neutronify('a_b_c'))