Support Windows image billing

Windows image support is a new feature to for both distil and
odoo glue script.

1. Distil Change: Distil needs to know if the instance is a
Windows instance and save it to be persistent in case the
instance or the original Windows image is deleted.

2. Odoo-glue Change: Odoo glue needs to insert new order line
for charing Windows image if we know the instance is a Windows
instance.

Change-Id: I6ee4222a492d612b83371de656e11e756f7ee84d
This commit is contained in:
Fei Long Wang
2016-03-26 00:50:16 +13:00
parent 9509113d39
commit 3efcfcdf6a
4 changed files with 87 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ from datetime import timedelta
import json
import config
import logging as log
import helpers
class Database(object):
@@ -63,6 +64,8 @@ class Database(object):
if query.count() == 0:
info = self.merge_resource_metadata({'type': resource_type},
entry, md_def)
if resource_type == 'Virtual Machine':
info['os_distro'] = self._get_os_distro(entry)
self.session.add(Resource(
id=resource_id,
info=json.dumps(info),
@@ -71,10 +74,29 @@ class Database(object):
self.session.flush() # can't assume deferred constraints.
else:
md_dict = json.loads(query[0].info)
# NOTE(flwang): The os_distro property maybe set on the image
# after some instances created. Do we really care about this case?
# This may impact the performance a bit.
if resource_type == 'Virtual Machine':
md_dict['os_distro'] = self._get_os_distro(entry)
md_dict = self.merge_resource_metadata(md_dict, entry,
md_def)
query[0].info = json.dumps(md_dict)
def _get_os_distro(self, entry):
os_distro = 'unknown'
if 'image.id' in entry['resource_metadata']:
# Boot from image
image_id = entry['resource_metadata']['image.id']
os_distro = getattr(helpers.get_image(image_id), 'os_distro', 'unknown')
if entry['resource_metadata']['image_ref'] == 'None':
# Boot from volume
image_meta = getattr(helpers.get_volume(entry['resource_id']), 'volume_image_metadata', {})
os_distro = image_meta.get('os_distro', 'unknown')
return os_distro
def insert_usage(self, tenant_id, resource_id, entries, unit,
start, end, timestamp):
"""Inserts all given entries into the database."""

View File

@@ -14,6 +14,8 @@
from novaclient import client as novaclient
from cinderclient.v2 import client as cinderclient
from glanceclient import client as glanceclient
from keystoneclient.v2_0 import client as keystoneclient
from decimal import Decimal
import config
import math
@@ -68,6 +70,58 @@ def volume_type(volume_type):
return False
def get_image(image_id):
try:
keystone = keystoneclient.Client(username=config.auth['username'],
password=config.auth['password'],
tenant_name=config.auth['default_tenant'],
auth_url=config.auth['end_point'],
region_name=config.main['region'],
insecure=config.auth['insecure'])
client_kwargs = {
'token': keystone.auth_token,
'insecure': config.auth['insecure']
}
endpoint_kwargs = {
'service_type': 'image',
'endpoint_type': 'publicURL',
}
endpoint = keystone.service_catalog.url_for(**endpoint_kwargs)
glance = glanceclient.Client('2', endpoint, **client_kwargs)
return glance.images.get(image_id)
except Exception:
return
def get_volume(instance_id):
try:
_client_class = novaclient.get_client_class(2)
nova = _client_class(
config.auth['username'],
config.auth['password'],
config.auth['default_tenant'],
config.auth['end_point'],
region_name=config.main['region'],
insecure=config.auth['insecure'])
instance = nova.servers.get(instance_id)
# Assume first volume is the root disk
volume_id = getattr(instance, 'os-extended-volumes:volumes_attached')[0]['id']
if not volume_id:
return None
cinder = cinderclient.Client(
config.auth['username'],
config.auth['password'],
config.auth['default_tenant'],
config.auth['end_point'],
region=config.main['region'],
insecure=config.auth['insecure'])
return cinder.volumes.get(volume_id)
except Exception:
return
def to_gigabytes_from_bytes(value):
"""From Bytes, unrounded."""
return ((value / Decimal(1024)) / Decimal(1024)) / Decimal(1024)

View File

@@ -426,6 +426,8 @@ def get_tenant_usage(shell, tenant, start, end):
for res_id, res in raw_usage['usage']['resources'].items():
name = res.get('name', res.get('ip address', '')) or res_id
type = res.get('type')
is_windows_instance = res.get('os_distro') == 'windows'
for service_usage in res['services']:
if service_usage['volume'] == 'unknown unit conversion':
@@ -473,6 +475,14 @@ def get_tenant_usage(shell, tenant, start, end):
'name': name,
'volume': float(service_usage['volume']),
'region': region})
# NOTE(flwang): If this usage line is for VM(instance),
# and the instance is windows image, then a new usage line
# is added.
if type == 'Virtual Machine' and is_windows_instance:
usage.append({'product': service_usage['name'] + '-windows',
'name': name,
'volume': float(service_usage['volume']),
'region': region})
# Aggregate traffic data
for type, volume in traffic.items():

View File

@@ -33,3 +33,4 @@ pbr>=0.6,!=0.7,<1.0
python-novaclient>=2.17.0
python-cinderclient>=1.0.8
keystonemiddleware!=4.1.0,>=4.0.0 # Apache-2.0
python-glanceclient>=0.18.0 # From Nova stable/liberty