Merge "Add command to generate Purchase order for Windows"

This commit is contained in:
Jenkins 2016-05-03 22:14:54 +00:00 committed by Gerrit Code Review
commit 690ecec82c
2 changed files with 126 additions and 33 deletions

View File

@ -33,31 +33,13 @@ collection:
max_windows_per_cycle: 4
# defines which meter is mapped to which transformer
meter_mappings:
# -
# # meter name as seen in ceilometer
# meter: instance
# # type of resource it maps to (seen on sales order)
# type: Virtual Machine
# # which transformer to use
# transformer: InstanceUptime
# # what unit type is coming in via the meter
# unit: second
# metadata:
# name:
# sources:
# # which keys to search for in the ceilometer entry metadata
# # this can be more than one as metadata is inconsistent between
# # source types
# - display_name
# availability zone:
# sources:
# - OS-EXT-AZ:availability_zone
-
meter: state
# type of resource it maps to (seen on sales order)
# meter name as seen in ceilometer
meter: instance
# type of resource it maps to (seen on sales order)
type: Virtual Machine
# which transformer to use
transformer: Uptime
transformer: InstanceUptime
# what unit type is coming in via the meter
unit: second
metadata:
@ -70,6 +52,9 @@ collection:
availability zone:
sources:
- OS-EXT-AZ:availability_zone
host:
sources:
- instance_host
-
meter: ip.floating
service: n1.ipv4

View File

@ -415,7 +415,6 @@ def get_tenant_usage(shell, tenant, start, end):
for region in REGION_MAPPING.keys():
distil_client = getattr(shell, 'distil' + region.replace('-', '_'))
raw_usage = distil_client.get_usage(tenant, start, end)
if not raw_usage:
return None
@ -428,6 +427,7 @@ def get_tenant_usage(shell, tenant, start, end):
name = res.get('name', res.get('ip address', '')) or res_id
type = res.get('type')
is_windows_instance = res.get('os_distro') == 'windows'
instance_host = res.get('host')
for service_usage in res['services']:
if service_usage['volume'] == 'unknown unit conversion':
@ -442,20 +442,18 @@ def get_tenant_usage(shell, tenant, start, end):
if service_usage['unit'] == 'byte':
v = Decimal(service_usage['volume'])
service_usage['unit'] = 'gigabyte'
service_usage['volume'] = str(v /
Decimal(1024 * 1024 * 1024))
service_usage['volume'] = str(v / Decimal(1024 * 1024 * 1024))
if service_usage['unit'] == 'second':
# convert seconds to hours, rounding up.
v = Decimal(service_usage['volume'])
service_usage['unit'] = 'hour'
service_usage['volume'] = str(math.ceil(v /
Decimal(60 * 60)))
service_usage['volume'] = str(math.ceil(v / Decimal(60 * 60)))
# drop zero usages.
if not Decimal(service_usage['volume']):
print('WARNING: Dropping 0-volume line: %s' %
(service_usage,))
log(shell.debug,'WARNING: Dropping 0-volume line: %s' %
(service_usage,))
continue
if Decimal(service_usage['volume']) <= 0.00001:
@ -474,7 +472,8 @@ def get_tenant_usage(shell, tenant, start, end):
usage.append({'product': service_usage['name'],
'name': name,
'volume': float(service_usage['volume']),
'region': region})
'region': region,
'resource_id': res_id})
# NOTE(flwang): If this usage line is for VM(instance),
# and the instance is windows image, then a new usage line
# is added.
@ -482,12 +481,14 @@ def get_tenant_usage(shell, tenant, start, end):
usage.append({'product': service_usage['name'] + '-windows',
'name': name,
'volume': float(service_usage['volume']),
'region': region})
'region': region,
'resource_id': res_id,
'host': instance_host})
# Aggregate traffic data
for type, volume in traffic.items():
print('Region: %s, traffic type: %s, volume: %s' %
(region, type, str(volume)))
log(shell.debug,'Region: %s, traffic type: %s, volume: %s' %
(region, type, str(volume)))
usage.append({'product': type,
'name': TRAFFIC_MAPPING[type],
'volume': math.floor(volume),
@ -682,6 +683,8 @@ def login_odoo(shell):
shell.Partner = shell.oerp.env['res.partner']
shell.Pricelist = shell.oerp.env['product.pricelist']
shell.Product = shell.oerp.env['product.product']
shell.PurchaseOrder = shell.oerp.env['purchase.order']
shell.PurchaseOrderline = shell.oerp.env['purchase.order.line']
def check_duplicate(order):
@ -749,6 +752,111 @@ def do_update_quote(shell, args):
print('Failed to update order: %s' % id)
@arg('--start', type=str, metavar='START',
dest='START', required=True,
help='Start date for quote.')
@arg('--end', type=str, metavar='END',
dest='END', required=True,
help='End date for quote.')
@arg('--create-purchase-order', type=bool, metavar='CREATE_PURCHASE_ORDER',
dest='CREATE_PURCHASE_ORDER', required=False, default=False,
help='If or not create a purchase order based on usage for given time'
' range, the default value is False.')
def do_windows(shell, args):
"""
By default, this sub command will print the windows hosts list based on
the usage for given time range. If the 'create-purchase-order' is set to
'True' then a purchase order based on the monthly Windows instances usage
will be created on Odoo.
"""
user_roles = shell.keystone.session.auth.auth_ref['user']['roles']
if {u'name': u'admin'} not in user_roles:
print('Admin permission is required.')
return
login_odoo(shell)
end_timestamp = datetime.datetime.strptime(
args.END,
'%Y-%m-%dT%H:%M:%S'
)
billing_date = str((end_timestamp - datetime.timedelta(days=1)).date())
windows_usage = []
windows_hosts = set()
tenants = shell.keystone.tenants.list()
pricelist = None
for t in tenants:
if t.name == 'openstack':
# NOTE(flwang): Using a default tenant to find parter and
# pricelist of root parter
partner = find_oerp_partner_for_tenant(shell, t)
root_partner = find_root_partner(shell, partner)
pricelist, _ = root_partner['property_product_pricelist']
usages = get_tenant_usage(shell, t.id, args.START, args.END)
for u in usages:
# TODO(flwang): Using regex to match 'c1.c%dr%d-windows'
if u['product'].endswith('-windows'):
windows_usage.append(u)
if u['host'] not in windows_hosts:
windows_hosts.add(u['host'])
print(u['host'])
if args.CREATE_PURCHASE_ORDER:
generate_purchase_order(shell, args, windows_usage,
billing_date, pricelist)
def generate_purchase_order(shell, args, usage, billing_date, pricelist):
"""
Call odoo API to create a new purchase order
"""
# TODO(flwang): Partner id is hardcoded for Windows license provider
# location id is hardcoded since we don't care the delivery location
partner_id = 5618
localtion_id = 10
try:
order_dict = {
'partner_id': partner_id,
'pricelist_id': pricelist,
'partner_invoice_id': partner_id,
'partner_shipping_id': partner_id,
'date_order': billing_date,
'location_id': localtion_id,
}
order = shell.PurchaseOrder.create(order_dict)
for m in sorted(usage, key=lambda m: m['product']):
prod = find_oerp_product(shell, m['region'], m['product'])
usage_dict = {
'order_id': order,
'date_planned': billing_date,
'account_analytic_id': '',
'product_id': prod['id'],
'product_uom': prod['uom_id'][0],
'product_qty': math.fabs(m['volume']),
'name': m['resource_id'], # Hide the instance name
'price_unit': get_price(shell, pricelist, prod, m['volume'])
}
if usage_dict['product_qty'] < 0.005:
print(
'%s is too small for %s.' %
(str(usage_dict['product_qty']), m['name'])
)
continue
shell.PurchaseOrderline.create(usage_dict)
except odoorpc.error.RPCError as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback.print_exception(exc_type, exc_value, exc_traceback,
limit=2, file=sys.stdout)
raise e
except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback.print_exception(exc_type, exc_value, exc_traceback,
limit=2, file=sys.stdout)
raise e
def print_dict(d, max_column_width=80):
pt = prettytable.PrettyTable(['Property', 'Value'], caching=False)
pt.align = 'l'