Merge "Include windows vm usage in quotation"
This commit is contained in:
commit
09bddd5cdf
|
@ -14,7 +14,9 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import copy
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
import itertools
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -372,6 +374,19 @@ class OdooDriver(driver.BaseDriver):
|
||||||
|
|
||||||
return price
|
return price
|
||||||
|
|
||||||
|
def _get_entry_info(self, entry, resources_info, service_mapping):
|
||||||
|
service_name = entry.get('service')
|
||||||
|
volume = entry.get('volume')
|
||||||
|
unit = entry.get('unit')
|
||||||
|
res_id = entry.get('resource_id')
|
||||||
|
resource = resources_info.get(res_id, {})
|
||||||
|
# resource_type is the type defined in meter_mappings.yml.
|
||||||
|
resource_type = resource.get('type')
|
||||||
|
service_type = service_mapping.get(service_name, resource_type)
|
||||||
|
|
||||||
|
return (service_name, service_type, volume, unit, resource,
|
||||||
|
resource_type)
|
||||||
|
|
||||||
def get_quotations(self, region, project_id, measurements=[], resources=[],
|
def get_quotations(self, region, project_id, measurements=[], resources=[],
|
||||||
detailed=False):
|
detailed=False):
|
||||||
"""Get current month quotation.
|
"""Get current month quotation.
|
||||||
|
@ -401,7 +416,12 @@ class OdooDriver(driver.BaseDriver):
|
||||||
cost_details = {}
|
cost_details = {}
|
||||||
|
|
||||||
odoo_region = self.region_mapping.get(region, region).upper()
|
odoo_region = self.region_mapping.get(region, region).upper()
|
||||||
resources = {row.id: json.loads(row.info) for row in resources}
|
|
||||||
|
resources_info = {}
|
||||||
|
for row in resources:
|
||||||
|
info = json.loads(row.info)
|
||||||
|
info.update({'id': row.id})
|
||||||
|
resources_info[row.id] = info
|
||||||
|
|
||||||
# NOTE(flwang): For most of the cases of Distil API, the request comes
|
# NOTE(flwang): For most of the cases of Distil API, the request comes
|
||||||
# from billing panel. Billing panel sends 1 API call for /invoices and
|
# from billing panel. Billing panel sends 1 API call for /invoices and
|
||||||
|
@ -411,15 +431,25 @@ class OdooDriver(driver.BaseDriver):
|
||||||
products = self.get_products()[region]
|
products = self.get_products()[region]
|
||||||
service_mapping = self._get_service_mapping(products)
|
service_mapping = self._get_service_mapping(products)
|
||||||
|
|
||||||
|
# Find windows VM usage entries
|
||||||
|
windows_vm_entries = []
|
||||||
for entry in measurements:
|
for entry in measurements:
|
||||||
service_name = entry.get('service')
|
(service_name, service_type, _, _, resource,
|
||||||
volume = entry.get('volume')
|
resource_type) = self._get_entry_info(entry, resources_info,
|
||||||
unit = entry.get('unit')
|
service_mapping)
|
||||||
res_id = entry.get('resource_id')
|
|
||||||
|
|
||||||
# resource_type is the type defined in meter_mappings.yml.
|
if (service_type == COMPUTE_CATEGORY
|
||||||
resource_type = resources[res_id]['type']
|
and resource_type == 'Virtual Machine'
|
||||||
service_type = service_mapping.get(service_name, resource_type)
|
and resource.get('os_distro') == 'windows'):
|
||||||
|
new_entry = copy.deepcopy(entry)
|
||||||
|
setattr(new_entry, 'service', '%s-windows' % service_name)
|
||||||
|
windows_vm_entries.append(new_entry)
|
||||||
|
|
||||||
|
for entry in itertools.chain(measurements, windows_vm_entries):
|
||||||
|
(service_name, service_type, volume, unit, resource,
|
||||||
|
resource_type) = self._get_entry_info(entry, resources_info,
|
||||||
|
service_mapping)
|
||||||
|
res_id = resource['id']
|
||||||
|
|
||||||
if service_type not in cost_details:
|
if service_type not in cost_details:
|
||||||
cost_details[service_type] = {
|
cost_details[service_type] = {
|
||||||
|
@ -436,10 +466,10 @@ class OdooDriver(driver.BaseDriver):
|
||||||
price_spec = price_mapping[service_name]
|
price_spec = price_mapping[service_name]
|
||||||
|
|
||||||
# Convert volume according to unit in price definition.
|
# Convert volume according to unit in price definition.
|
||||||
volume = float(general.convert_to(volume, unit,
|
volume = float(
|
||||||
price_spec['unit']))
|
general.convert_to(volume, unit, price_spec['unit'])
|
||||||
cost = (round(volume * price_spec['rate'],
|
)
|
||||||
constants.PRICE_DIGITS)
|
cost = (round(volume * price_spec['rate'], constants.PRICE_DIGITS)
|
||||||
if price_spec['rate'] else 0)
|
if price_spec['rate'] else 0)
|
||||||
|
|
||||||
total_cost += cost
|
total_cost += cost
|
||||||
|
@ -455,7 +485,7 @@ class OdooDriver(driver.BaseDriver):
|
||||||
odoo_service_name
|
odoo_service_name
|
||||||
].append(
|
].append(
|
||||||
{
|
{
|
||||||
"resource_name": resources[res_id].get('name', ''),
|
"resource_name": resource.get('name', ''),
|
||||||
"resource_id": res_id,
|
"resource_id": res_id,
|
||||||
"cost": cost,
|
"cost": cost,
|
||||||
"quantity": round(volume, 3),
|
"quantity": round(volume, 3),
|
||||||
|
@ -465,8 +495,10 @@ class OdooDriver(driver.BaseDriver):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
result = {'total_cost': round(float(total_cost),
|
result = {
|
||||||
constants.PRICE_DIGITS)}
|
'total_cost': round(float(total_cost), constants.PRICE_DIGITS)
|
||||||
|
}
|
||||||
|
|
||||||
if detailed:
|
if detailed:
|
||||||
result.update({'details': cost_details})
|
result.update({'details': cost_details})
|
||||||
|
|
||||||
|
|
|
@ -399,6 +399,116 @@ class TestOdooDriver(base.DistilTestCase):
|
||||||
quotations
|
quotations
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock.patch('odoorpc.ODOO')
|
||||||
|
@mock.patch('distil.erp.drivers.odoo.OdooDriver.get_products')
|
||||||
|
def test_get_quotations_with_details_windows_vm(self, mock_get_products,
|
||||||
|
mock_odoo):
|
||||||
|
mock_get_products.return_value = {
|
||||||
|
'nz_1': {
|
||||||
|
'Compute': [
|
||||||
|
{
|
||||||
|
'name': 'c1.c2r16', 'description': 'c1.c2r16',
|
||||||
|
'rate': 0.01, 'unit': 'hour'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'c1.c2r16-windows',
|
||||||
|
'description': 'c1.c2r16-windows',
|
||||||
|
'rate': 0.02, 'unit': 'hour'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'Block Storage': [
|
||||||
|
{
|
||||||
|
'name': 'b1.standard', 'description': 'b1.standard',
|
||||||
|
'rate': 0.02, 'unit': 'gigabyte'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Resource(object):
|
||||||
|
def __init__(self, id, info):
|
||||||
|
self.id = id
|
||||||
|
self.info = info
|
||||||
|
|
||||||
|
resources = [
|
||||||
|
Resource(1, '{"name": "volume1", "type": "Volume"}'),
|
||||||
|
Resource(
|
||||||
|
2,
|
||||||
|
'{"name": "instance2", "type": "Virtual Machine", '
|
||||||
|
'"os_distro": "windows"}'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
class Usage(object):
|
||||||
|
def __init__(self, service, resource_id, volume, unit):
|
||||||
|
self.service = service
|
||||||
|
self.resource_id = resource_id
|
||||||
|
self.volume = volume
|
||||||
|
self.unit = unit
|
||||||
|
|
||||||
|
def get(self, attr):
|
||||||
|
return getattr(self, attr)
|
||||||
|
|
||||||
|
usage = [
|
||||||
|
Usage('b1.standard', 1, 1024 * 1024 * 1024, 'byte'),
|
||||||
|
Usage('c1.c2r16', 2, 3600, 'second')
|
||||||
|
]
|
||||||
|
|
||||||
|
odoodriver = odoo.OdooDriver(self.conf)
|
||||||
|
quotations = odoodriver.get_quotations(
|
||||||
|
'nz_1', 'fake_id', measurements=usage, resources=resources,
|
||||||
|
detailed=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
'total_cost': 0.05,
|
||||||
|
'details': {
|
||||||
|
'Compute': {
|
||||||
|
'total_cost': 0.03,
|
||||||
|
'breakdown': {
|
||||||
|
'NZ-1.c1.c2r16': [
|
||||||
|
{
|
||||||
|
"resource_name": "instance2",
|
||||||
|
"resource_id": 2,
|
||||||
|
"cost": 0.01,
|
||||||
|
"quantity": 1.0,
|
||||||
|
"rate": 0.01,
|
||||||
|
"unit": "hour",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'NZ-1.c1.c2r16-windows': [
|
||||||
|
{
|
||||||
|
"resource_name": "instance2",
|
||||||
|
"resource_id": 2,
|
||||||
|
"cost": 0.02,
|
||||||
|
"quantity": 1.0,
|
||||||
|
"rate": 0.02,
|
||||||
|
"unit": "hour",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Block Storage': {
|
||||||
|
'total_cost': 0.02,
|
||||||
|
'breakdown': {
|
||||||
|
'NZ-1.b1.standard': [
|
||||||
|
{
|
||||||
|
"resource_name": "volume1",
|
||||||
|
"resource_id": 1,
|
||||||
|
"cost": 0.02,
|
||||||
|
"quantity": 1.0,
|
||||||
|
"rate": 0.02,
|
||||||
|
"unit": "gigabyte",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
quotations
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('odoorpc.ODOO')
|
@mock.patch('odoorpc.ODOO')
|
||||||
def test_get_credits(self, mock_odoo):
|
def test_get_credits(self, mock_odoo):
|
||||||
fake_credits = [{'create_uid': [182, 'OpenStack Testing'],
|
fake_credits = [{'create_uid': [182, 'OpenStack Testing'],
|
||||||
|
|
Loading…
Reference in New Issue