/usr/lib/python3/dist-packages/libcloud/pricing.py is in python3-libcloud 0.20.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | # Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import with_statement
"""
A class which handles loading the pricing files.
"""
import os.path
from os.path import join as pjoin
try:
import simplejson as json
try:
JSONDecodeError = json.JSONDecodeError
except AttributeError:
# simplejson < 2.1.0 does not have the JSONDecodeError exception class
JSONDecodeError = ValueError
except ImportError:
import json
JSONDecodeError = ValueError
from libcloud.utils.connection import get_response_object
__all__ = [
'get_pricing',
'get_size_price',
'set_pricing',
'clear_pricing_data',
'download_pricing_file'
]
# Default URL to the pricing file
DEFAULT_FILE_URL = 'https://git-wip-us.apache.org/repos/asf?p=libcloud.git;a=blob_plain;f=libcloud/data/pricing.json' # NOQA
CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
DEFAULT_PRICING_FILE_PATH = pjoin(CURRENT_DIRECTORY, 'data/pricing.json')
CUSTOM_PRICING_FILE_PATH = os.path.expanduser('~/.libcloud/pricing.json')
# Pricing data cache
PRICING_DATA = {
'compute': {},
'storage': {}
}
VALID_PRICING_DRIVER_TYPES = ['compute', 'storage']
def get_pricing_file_path(file_path=None):
if os.path.exists(CUSTOM_PRICING_FILE_PATH) and \
os.path.isfile(CUSTOM_PRICING_FILE_PATH):
# Custom pricing file is available, use it
return CUSTOM_PRICING_FILE_PATH
return DEFAULT_PRICING_FILE_PATH
def get_pricing(driver_type, driver_name, pricing_file_path=None):
"""
Return pricing for the provided driver.
:type driver_type: ``str``
:param driver_type: Driver type ('compute' or 'storage')
:type driver_name: ``str``
:param driver_name: Driver name
:type pricing_file_path: ``str``
:param pricing_file_path: Custom path to a price file. If not provided
it uses a default path.
:rtype: ``dict``
:return: Dictionary with pricing where a key name is size ID and
the value is a price.
"""
if driver_type not in VALID_PRICING_DRIVER_TYPES:
raise AttributeError('Invalid driver type: %s', driver_type)
if driver_name in PRICING_DATA[driver_type]:
return PRICING_DATA[driver_type][driver_name]
if not pricing_file_path:
pricing_file_path = get_pricing_file_path(file_path=pricing_file_path)
with open(pricing_file_path) as fp:
content = fp.read()
pricing_data = json.loads(content)
size_pricing = pricing_data[driver_type][driver_name]
for driver_type in VALID_PRICING_DRIVER_TYPES:
# pylint: disable=maybe-no-member
pricing = pricing_data.get(driver_type, None)
if pricing:
PRICING_DATA[driver_type] = pricing
return size_pricing
def set_pricing(driver_type, driver_name, pricing):
"""
Populate the driver pricing dictionary.
:type driver_type: ``str``
:param driver_type: Driver type ('compute' or 'storage')
:type driver_name: ``str``
:param driver_name: Driver name
:type pricing: ``dict``
:param pricing: Dictionary where a key is a size ID and a value is a price.
"""
PRICING_DATA[driver_type][driver_name] = pricing
def get_size_price(driver_type, driver_name, size_id):
"""
Return price for the provided size.
:type driver_type: ``str``
:param driver_type: Driver type ('compute' or 'storage')
:type driver_name: ``str``
:param driver_name: Driver name
:type size_id: ``str`` or ``int``
:param size_id: Unique size ID (can be an integer or a string - depends on
the driver)
:rtype: ``float``
:return: Size price.
"""
pricing = get_pricing(driver_type=driver_type, driver_name=driver_name)
price = float(pricing[size_id])
return price
def invalidate_pricing_cache():
"""
Invalidate pricing cache for all the drivers.
"""
PRICING_DATA['compute'] = {}
PRICING_DATA['storage'] = {}
def clear_pricing_data():
"""
Invalidate pricing cache for all the drivers.
Note: This method does the same thing as invalidate_pricing_cache and is
here for backward compatibility reasons.
"""
invalidate_pricing_cache()
def invalidate_module_pricing_cache(driver_type, driver_name):
"""
Invalidate the cache for the specified driver.
:type driver_type: ``str``
:param driver_type: Driver type ('compute' or 'storage')
:type driver_name: ``str``
:param driver_name: Driver name
"""
if driver_name in PRICING_DATA[driver_type]:
del PRICING_DATA[driver_type][driver_name]
def download_pricing_file(file_url=DEFAULT_FILE_URL,
file_path=CUSTOM_PRICING_FILE_PATH):
"""
Download pricing file from the file_url and save it to file_path.
:type file_url: ``str``
:param file_url: URL pointing to the pricing file.
:type file_path: ``str``
:param file_path: Path where a download pricing file will be saved.
"""
dir_name = os.path.dirname(file_path)
if not os.path.exists(dir_name):
# Verify a valid path is provided
msg = ('Can\'t write to %s, directory %s, doesn\'t exist' %
(file_path, dir_name))
raise ValueError(msg)
if os.path.exists(file_path) and os.path.isdir(file_path):
msg = ('Can\'t write to %s file path because it\'s a'
' directory' % (file_path))
raise ValueError(msg)
response = get_response_object(file_url)
body = response.body
# Verify pricing file is valid
try:
data = json.loads(body)
except JSONDecodeError:
msg = 'Provided URL doesn\'t contain valid pricing data'
raise Exception(msg)
# pylint: disable=maybe-no-member
if not data.get('updated', None):
msg = 'Provided URL doesn\'t contain valid pricing data'
raise Exception(msg)
# No need to stream it since file is small
with open(file_path, 'w') as file_handle:
file_handle.write(body)
|