Merge "Add command to generate Purchase order for Windows"
This commit is contained in:
commit
690ecec82c
@ -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
|
||||
|
@ -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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user