From bdcb2027368bad2853c338cef6d227bac9c8ca8a Mon Sep 17 00:00:00 2001 From: Adrian Turjak Date: Fri, 13 Dec 2019 09:49:00 +1300 Subject: [PATCH] Fix an issue with product naming and swift policy names Change-Id: Ib05c9c0302189c3d28ee10a6b789b8588d5a9d8c --- distil/config.py | 6 -- distil/erp/drivers/odoo.py | 112 ++++++++++++++------- distil/tests/unit/erp/drivers/test_odoo.py | 14 ++- 3 files changed, 89 insertions(+), 43 deletions(-) diff --git a/distil/config.py b/distil/config.py index 75e2ed6..5fa2007 100644 --- a/distil/config.py +++ b/distil/config.py @@ -90,12 +90,6 @@ ODOO_OPTS = [ help='Region name mappings between Keystone and Odoo. For ' 'example, ' 'region_mapping=region1:RegionOne,region2:RegionTwo'), - cfg.StrOpt('object_storage_product_name', - default='NZ.o1.standard', - help='Product name in Odoo for object storage.'), - cfg.StrOpt('object_storage_service_name', - default='o1.standard', - help='Service name for object storage.'), cfg.ListOpt('extra_product_category_list', default=[], help='Additional product categories which should be easily ' diff --git a/distil/erp/drivers/odoo.py b/distil/erp/drivers/odoo.py index b86fd44..1ae2f84 100644 --- a/distil/erp/drivers/odoo.py +++ b/distil/erp/drivers/odoo.py @@ -157,41 +157,59 @@ class OdooDriver(driver.BaseDriver): prices[actual_region][category.lower()].append( {'name': name, + 'full_name': product['name_template'], 'rate': rate, 'unit': unit, 'description': desc} ) - # Handle object storage product that does not belong to any - # region in odoo. - obj_p_name = self.conf.odoo.object_storage_product_name - obj_s_name = self.conf.odoo.object_storage_service_name + # Handle object storage products + c = self.category.search([('name', '=', OBJECTSTORAGE_CATEGORY)]) + product_ids = self.product.search([('categ_id', 'in', c), + ('sale_ok', '=', True), + ('active', '=', True)]) + products = self.product.read(product_ids, fields=product_fields) - obj_pids = self.product.search( - [('name_template', '=', obj_p_name), - ('sale_ok', '=', True), - ('active', '=', True)] - ) + for product in products: + product_region = None + for region in odoo_regions: + if region.upper() in product['name_template']: + product_region = region - if len(obj_pids) > 0: - obj_p = self.product.read(obj_pids[0], - fields=["lst_price", "default_code", - "description"]) - self.product_category_mapping[obj_pids[0]] = \ - OBJECTSTORAGE_CATEGORY - for region in regions: - # Ensure returned region name is same with what user see - # from Keystone. + category = product['categ_id'][1].split('/')[-1].strip() + + name = product['name_template'].lower() + + rate = round(product['lst_price'], constants.RATE_DIGITS) + # NOTE(flwang): default_code is Internal Reference on + # Odoo GUI + unit = product['default_code'] + desc = product['description'] + self.product_unit_mapping[product['id']] = unit + + product_dict = { + 'name': name, + 'full_name': product['name_template'], + 'rate': rate, + 'unit': unit, + 'description': desc + } + + if product_region: + # add it to just the one region actual_region = self.reverse_region_mapping.get( - region, region) + product_region, product_region) - prices[actual_region]['object storage'].append( - {'name': obj_s_name, - 'rate': round(obj_p["lst_price"], - constants.RATE_DIGITS), - 'unit': obj_p["default_code"], - 'description': obj_p["description"]} - ) + prices[actual_region][category.lower()].append( + product_dict) + else: + # add it to all regions + for region in odoo_regions: + actual_region = self.reverse_region_mapping.get( + region, region) + + prices[actual_region][category.lower()].append( + product_dict) except odoorpc.error.Error as e: LOG.exception(e) return {} @@ -392,26 +410,52 @@ class OdooDriver(driver.BaseDriver): """Get service price information from price definitions.""" price = {'service_name': service_name} + # NOTE(adriant): We do this to handle the object storage policy + # name to product translation + formatted_name = service_name.lower().replace("--", ".") + if service_type in products: for s in products[service_type]: - if s['name'] == service_name: - price.update({'rate': s['rate'], 'unit': s['unit']}) + if s['name'] == formatted_name: + price.update({ + 'rate': s['rate'], 'unit': s['unit'], + 'product_name': s['full_name']}) break else: found = False for category, services in products.items(): for s in services: - if s['name'] == service_name: - price.update({'rate': s['rate'], 'unit': s['unit']}) + if s['name'] == formatted_name: + price.update({ + 'rate': s['rate'], 'unit': s['unit'], + 'product_name': s['full_name']}) found = True break + if not found: + for category, services in products.items(): + for s in services: + # NOTE(adriant): this will find a partial match like: + # 'o1.standard' in 'NZ.o1.standard' + if formatted_name in s['name']: + price.update({ + 'rate': s['rate'], 'unit': s['unit'], + 'product_name': s['full_name']}) + found = True + break + if not found: raise exceptions.NotFoundException( 'Price not found, service name: %s, service type: %s' % - (service_name, service_type) + (formatted_name, service_type) ) + if 'unit' in price and not price['unit']: + raise exceptions.ERPException( + "Product: %s is missing 'unit' definition." % + formatted_name + ) + return price def _get_entry_info(self, entry, resources_info, service_mapping): @@ -455,8 +499,6 @@ class OdooDriver(driver.BaseDriver): price_mapping = {} cost_details = {} - odoo_region = self.region_mapping.get(region, region).upper() - resources_info = {} for row in resources: info = json.loads(row.info) @@ -517,14 +559,12 @@ class OdooDriver(driver.BaseDriver): total_cost += cost if detailed: - odoo_service_name = "%s.%s" % (odoo_region, service_name) - cost_details[service_type]['total_cost'] = round( (cost_details[service_type]['total_cost'] + cost), constants.PRICE_DIGITS ) cost_details[service_type]['breakdown'][ - odoo_service_name + price_spec['product_name'] ].append( { "resource_name": resource.get('name', ''), diff --git a/distil/tests/unit/erp/drivers/test_odoo.py b/distil/tests/unit/erp/drivers/test_odoo.py index d30d42a..2633aed 100644 --- a/distil/tests/unit/erp/drivers/test_odoo.py +++ b/distil/tests/unit/erp/drivers/test_odoo.py @@ -58,7 +58,7 @@ class TestOdooDriver(base.DistilTestCase): def test_get_products(self, mock_odoo): odoodriver = odoo.OdooDriver(self.conf) odoodriver.product.search.return_value = [] - odoodriver.product.read.return_value = PRODUCTS + odoodriver.product.read.side_effect = [PRODUCTS, []] products = odoodriver.get_products(regions=['nz_1']) @@ -68,14 +68,17 @@ class TestOdooDriver(base.DistilTestCase): 'block storage': [{'description': 'Block storage', 'rate': 0.00035, 'name': 'b1.volume', + 'full_name': 'NZ-1.b1.volume', 'unit': 'hour'}], 'compute': [{'description': '1 CPU, 1GB RAM', 'rate': 0.00015, 'name': 'c1.c1r1', + 'full_name': 'NZ-1.c1.c1r1', 'unit': 'hour'}], 'network': [{'description': 'Router', 'rate': 0.00025, 'name': 'n1.router', + 'full_name': 'NZ-1.n1.router', 'unit': 'hour'}] } }, @@ -270,12 +273,14 @@ class TestOdooDriver(base.DistilTestCase): 'Compute': [ { 'name': 'c1.c2r16', 'description': 'c1.c2r16', + 'full_name': 'NZ-1.c1.c2r16', 'rate': 0.01, 'unit': 'hour' } ], 'Block Storage': [ { 'name': 'b1.standard', 'description': 'b1.standard', + 'full_name': 'NZ-1.b1.standard', 'rate': 0.02, 'unit': 'gigabyte' } ] @@ -326,12 +331,14 @@ class TestOdooDriver(base.DistilTestCase): 'Compute': [ { 'name': 'c1.c2r16', 'description': 'c1.c2r16', + 'full_name': 'NZ-1.c1.c2r16', 'rate': 0.01, 'unit': 'hour' } ], 'Block Storage': [ { 'name': 'b1.standard', 'description': 'b1.standard', + 'full_name': 'NZ-1.b1.standard', 'rate': 0.02, 'unit': 'gigabyte' } ] @@ -417,19 +424,23 @@ class TestOdooDriver(base.DistilTestCase): 'Compute': [ { 'name': 'c1.c2r16', 'description': 'c1.c2r16', + 'full_name': 'NZ-1.c1.c2r16', 'rate': 0.01, 'unit': 'hour' }, { 'name': 'c1.c4r32', 'description': 'c1.c4r32', + 'full_name': 'NZ-1.c1.c4r32', 'rate': 0.04, 'unit': 'hour' }, { 'name': 'c1.c2r16-windows', + 'full_name': 'NZ-1.c1.c2r16-windows', 'description': 'c1.c2r16-windows', 'rate': 0.02, 'unit': 'hour' }, { 'name': 'c1.c4r32-sql-server-standard-windows', + 'full_name': 'NZ-1.c1.c4r32-sql-server-standard-windows', 'description': 'c1.c4r32-sql-server-standard-windows', 'rate': 0.04, 'unit': 'hour' } @@ -437,6 +448,7 @@ class TestOdooDriver(base.DistilTestCase): 'Block Storage': [ { 'name': 'b1.standard', 'description': 'b1.standard', + 'full_name': 'NZ-1.b1.standard', 'rate': 0.02, 'unit': 'gigabyte' } ]