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:
@@ -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."""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user