* usr/lib/byobu/include/ec2instancespricing.py: LP: #1286367

- merged in updated instance pricing script from
    https://github.com/erans/ec2instancespricing
This commit is contained in:
Dustin Kirkland 2014-02-28 16:11:51 -06:00
commit 0c5b466c9c
2 changed files with 485 additions and 331 deletions

4
debian/changelog vendored
View file

@ -1,6 +1,8 @@
byobu (5.74) unreleased; urgency=low
* UNRELEASED
* usr/lib/byobu/include/ec2instancespricing.py: LP: #1286367
- merged in updated instance pricing script from
https://github.com/erans/ec2instancespricing
-- Dustin Kirkland <kirkland@ubuntu.com> Mon, 17 Feb 2014 15:07:01 -0600

View file

@ -1,7 +1,6 @@
#!/usr/bin/python
#
# Copyright (c) 2012 Eran Sandler (eran@sandler.co.il), http://eran.sandler.co.il, http://forecastcloudy.net
# Copyright (C) 2012-2013 Dustin Kirkland <kirkland@byobu.co>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@ -24,6 +23,7 @@
#
import urllib2
import argparse
import datetime
try:
import simplejson as json
except ImportError:
@ -58,12 +58,17 @@ EC2_INSTANCE_TYPES = [
"m3.xlarge",
"m3.2xlarge",
"hi1.4xlarge",
"hs1.8xlarge"
"hs1.8xlarge",
"g2.2xlarge"
]
EC2_OS_TYPES = [
"linux",
"mswin"
"linux", # api platform name = "linux"
"mswin", # api platform name = "windows"
"rhel", # api platform name = ""
"sles", # api platform name = ""
"mswinSQL", # api platform name = "windows"
"mswinSQLWeb", # api platform name = "windows"
]
JSON_NAME_TO_EC2_REGIONS_API = {
@ -94,30 +99,80 @@ EC2_REGIONS_API_TO_JSON_NAME = {
"sa-east-1" : "sa-east-1"
}
INSTANCES_ON_DEMAND_URL = "http://aws.amazon.com/ec2/pricing/pricing-on-demand-instances.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/ri-light-linux.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/ri-light-mswin.json"
INSTNACES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/ri-medium-linux.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/ri-medium-mswin.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/ri-heavy-linux.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/ri-heavy-mswin.json"
INSTANCES_ON_DEMAND_LINUX_URL = "http://aws.amazon.com/ec2/pricing/json/linux-od.json"
INSTANCES_ON_DEMAND_RHEL_URL = "http://aws.amazon.com/ec2/pricing/json/rhel-od.json"
INSTANCES_ON_DEMAND_SLES_URL = "http://aws.amazon.com/ec2/pricing/json/sles-od.json"
INSTANCES_ON_DEMAND_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/json/mswin-od.json"
INSTANCES_ON_DEMAND_WINSQL_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQL-od.json"
INSTANCES_ON_DEMAND_WINSQLWEB_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQLWeb-od.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/json/linux-ri-light.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_RHEL_URL = "http://aws.amazon.com/ec2/pricing/json/rhel-ri-light.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_SLES_URL = "http://aws.amazon.com/ec2/pricing/json/sles-ri-light.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/json/mswin-ri-light.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQL_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQL-ri-light.json"
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQLWEB_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQLWeb-ri-light.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/json/linux-ri-medium.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_RHEL_URL = "http://aws.amazon.com/ec2/pricing/json/rhel-ri-medium.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_SLES_URL = "http://aws.amazon.com/ec2/pricing/json/sles-ri-medium.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/json/mswin-ri-medium.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQL_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQL-ri-medium.json"
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQLWEB_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQLWeb-ri-medium.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_LINUX_URL = "http://aws.amazon.com/ec2/pricing/json/linux-ri-heavy.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_RHEL_URL = "http://aws.amazon.com/ec2/pricing/json/rhel-ri-heavy.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_SLES_URL = "http://aws.amazon.com/ec2/pricing/json/sles-ri-heavy.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL = "http://aws.amazon.com/ec2/pricing/json/mswin-ri-heavy.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQL_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQL-ri-heavy.json"
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQLWEB_URL = "http://aws.amazon.com/ec2/pricing/json/mswinSQLWeb-ri-heavy.json"
INSTANCES_ONDEMAND_OS_TYPE_BY_URL = {
INSTANCES_ON_DEMAND_LINUX_URL : "linux",
INSTANCES_ON_DEMAND_RHEL_URL : "rhel",
INSTANCES_ON_DEMAND_SLES_URL : "sles",
INSTANCES_ON_DEMAND_WINDOWS_URL : "mswin",
INSTANCES_ON_DEMAND_WINSQL_URL : "mswinSQL",
INSTANCES_ON_DEMAND_WINSQLWEB_URL : "mswinSQLWeb",
}
INSTANCES_RESERVED_OS_TYPE_BY_URL = {
INSTANCES_RESERVED_LIGHT_UTILIZATION_LINUX_URL : "linux",
INSTANCES_RESERVED_LIGHT_UTILIZATION_RHEL_URL : "rhel",
INSTANCES_RESERVED_LIGHT_UTILIZATION_SLES_URL : "sles",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINDOWS_URL : "mswin",
INSTNACES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL : "linux",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQL_URL : "mswinSQL",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQLWEB_URL : "mswinSQLWeb",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL : "linux",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_RHEL_URL : "rhel",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_SLES_URL : "sles",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINDOWS_URL : "mswin",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQL_URL : "mswinSQL",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQLWEB_URL : "mswinSQLWeb",
INSTANCES_RESERVED_HEAVY_UTILIZATION_LINUX_URL : "linux",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL : "mswin"
INSTANCES_RESERVED_HEAVY_UTILIZATION_RHEL_URL : "rhel",
INSTANCES_RESERVED_HEAVY_UTILIZATION_SLES_URL : "sles",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL : "mswin",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQL_URL : "mswinSQL",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQLWEB_URL : "mswinSQLWeb",
}
INSTANCES_RESERVED_UTILIZATION_TYPE_BY_URL = {
INSTANCES_RESERVED_LIGHT_UTILIZATION_LINUX_URL : "light",
INSTANCES_RESERVED_LIGHT_UTILIZATION_RHEL_URL : "light",
INSTANCES_RESERVED_LIGHT_UTILIZATION_SLES_URL : "light",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINDOWS_URL : "light",
INSTNACES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL : "medium",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQL_URL : "light",
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQLWEB_URL : "light",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL : "medium",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_RHEL_URL : "medium",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_SLES_URL : "medium",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINDOWS_URL : "medium",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQL_URL : "medium",
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQLWEB_URL : "medium",
INSTANCES_RESERVED_HEAVY_UTILIZATION_LINUX_URL : "heavy",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL : "heavy"
INSTANCES_RESERVED_HEAVY_UTILIZATION_RHEL_URL : "heavy",
INSTANCES_RESERVED_HEAVY_UTILIZATION_SLES_URL : "heavy",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL : "heavy",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQL_URL : "heavy",
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQLWEB_URL : "heavy",
}
DEFAULT_CURRENCY = "USD"
@ -158,11 +213,82 @@ INSTANCE_SIZE_MAPPING = {
"xxxxxxxxl" : "8xlarge"
}
def _load_data(url):
f = urllib2.urlopen(url)
return json.loads(f.read())
class ResultsCacheBase(object):
_instance = None
def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=None, filter_os_type=None):
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(ResultsCacheBase, cls).__new__(cls, *args, **kwargs)
return cls._instance
def get(self, key):
pass
def set(self, key, value):
pass
class SimpleResultsCache(ResultsCacheBase):
_cache = {}
def get(self, key):
if key in self._cache:
return self._cache[key]
return None
def set(self, key, value):
self._cache[key] = value
class TimeBasedResultsCache(ResultsCacheBase):
_cache = {}
_cache_expiration = {}
# If you wish to chance this expiration use the following (a bit ugly) code:
#
# TimeBasedResultsCache()._default_expiration_in_seconds = 86400 # 1 day
#
# Since all cache classes inherit from ResultsCacheBase and are singletons that should set it correctly.
#
_default_expiration_in_seconds = 3600 # 1 hour
def get(self, key):
if key not in self._cache or key not in self._cache_expiration:
return None
# If key has expired return None
if self._cache_expiration[key] < datetime.datetime.utcnow():
if key in self._cache: del self._cache[key]
if key in self._cache_expiration: del self._cache_expiration[key]
return None
return self._cache[key]
def set(self, key, value):
self._cache[key] = value
self._cache_expiration[key] = datetime.datetime.utcnow() + datetime.timedelta(seconds=self._default_expiration_in_seconds)
def _load_data(url, use_cache=False, cache_class=SimpleResultsCache):
cache_object = None
if use_cache:
cache_object = cache_class()
result = cache_object.get(url)
if result is not None:
return result
f = urllib2.urlopen(url)
result = json.loads(f.read())
if use_cache:
cache_object.set(url, result)
return result
def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=None, filter_os_type=None, use_cache=False, cache_class=SimpleResultsCache):
""" Get EC2 reserved instances prices. Results can be filtered by region """
get_specific_region = (filter_region is not None)
@ -175,11 +301,23 @@ def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=N
urls = [
INSTANCES_RESERVED_LIGHT_UTILIZATION_LINUX_URL,
INSTANCES_RESERVED_LIGHT_UTILIZATION_RHEL_URL,
INSTANCES_RESERVED_LIGHT_UTILIZATION_SLES_URL,
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINDOWS_URL,
INSTNACES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL,
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQL_URL,
INSTANCES_RESERVED_LIGHT_UTILIZATION_WINSQLWEB_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_LINUX_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_RHEL_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_SLES_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINDOWS_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQL_URL,
INSTANCES_RESERVED_MEDIUM_UTILIZATION_WINSQLWEB_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_LINUX_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL
INSTANCES_RESERVED_HEAVY_UTILIZATION_RHEL_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_SLES_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINDOWS_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQL_URL,
INSTANCES_RESERVED_HEAVY_UTILIZATION_WINSQLWEB_URL,
]
result_regions = []
@ -193,8 +331,10 @@ def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=N
for u in urls:
os_type = INSTANCES_RESERVED_OS_TYPE_BY_URL[u]
if get_specific_os_type and os_type != filter_os_type:
continue
utilization_type = INSTANCES_RESERVED_UTILIZATION_TYPE_BY_URL[u]
data = _load_data(u)
data = _load_data(u, use_cache=use_cache, cache_class=cache_class)
if "config" in data and data["config"] and "regions" in data["config"] and data["config"]["regions"]:
for r in data["config"]["regions"]:
if "region" in r and r["region"]:
@ -214,10 +354,10 @@ def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=N
if "instanceTypes" in r:
for it in r["instanceTypes"]:
instance_type = INSTANCE_TYPE_MAPPING[it["type"]]
instance_type = it["type"]
if "sizes" in it:
for s in it["sizes"]:
instance_size = INSTANCE_SIZE_MAPPING[s["size"]]
instance_size = s["size"]
prices = {
"1year" : {
@ -230,7 +370,7 @@ def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=N
}
}
_type = "%s.%s" % (instance_type, instance_size)
_type = instance_size
if _type == "cc1.8xlarge":
# Fix conflict where cc1 and cc2 share the same type
_type = "cc2.8xlarge"
@ -266,7 +406,7 @@ def get_ec2_reserved_instances_prices(filter_region=None, filter_instance_type=N
return result
def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=None, filter_os_type=None):
def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=None, filter_os_type=None, use_cache=False, cache_class=SimpleResultsCache):
""" Get EC2 on-demand instances prices. Results can be filtered by region """
get_specific_region = (filter_region is not None)
@ -278,6 +418,15 @@ def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=N
currency = DEFAULT_CURRENCY
urls = [
INSTANCES_ON_DEMAND_LINUX_URL,
INSTANCES_ON_DEMAND_RHEL_URL,
INSTANCES_ON_DEMAND_SLES_URL,
INSTANCES_ON_DEMAND_WINDOWS_URL,
INSTANCES_ON_DEMAND_WINSQL_URL,
INSTANCES_ON_DEMAND_WINSQLWEB_URL
]
result_regions = []
result = {
"config" : {
@ -287,7 +436,11 @@ def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=N
"regions" : result_regions
}
data = _load_data(INSTANCES_ON_DEMAND_URL)
for u in urls:
if get_specific_os_type and INSTANCES_ONDEMAND_OS_TYPE_BY_URL[u] != filter_os_type:
continue
data = _load_data(u, use_cache=use_cache, cache_class=cache_class)
if "config" in data and data["config"] and "regions" in data["config"] and data["config"]["regions"]:
for r in data["config"]["regions"]:
if "region" in r and r["region"]:
@ -298,10 +451,10 @@ def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=N
instance_types = []
if "instanceTypes" in r:
for it in r["instanceTypes"]:
instance_type = INSTANCE_TYPE_MAPPING[it["type"]]
instance_type = it["type"]
if "sizes" in it:
for s in it["sizes"]:
instance_size = INSTANCE_SIZE_MAPPING[s["size"]]
instance_size = s["size"]
for price_data in s["valueColumns"]:
price = None
@ -310,7 +463,7 @@ def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=N
except ValueError:
price = None
_type = "%s.%s" % (instance_type, instance_size)
_type = instance_size
if _type == "cc1.8xlarge":
# Fix conflict where cc1 and cc2 share the same type
_type = "cc2.8xlarge"
@ -334,7 +487,6 @@ def get_ec2_ondemand_instances_prices(filter_region=None, filter_instance_type=N
return result
return None
if __name__ == "__main__":
def none_as_string(v):