Repository: libcloud Updated Branches: refs/heads/trunk 23127e387 -> c07dbe8c3
[google compute] add pricing data update script Also used the script to update `pricing.json` with current prices on a per-region basis. Added numeric sorting to the JSON output; as a result, all other pricing is now sorted numerically, e.g., "12GB" sorts after "3GB" and "150" sorts after "20" whereas previously, it would be the opposite. Closes #464 Signed-off-by: Eric Johnson <erjoh...@google.com> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c07dbe8c Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c07dbe8c Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c07dbe8c Branch: refs/heads/trunk Commit: c07dbe8c359b8a1ac760c2b168ffbd4f00dca952 Parents: 23127e3 Author: Misha Brukman <mbruk...@google.com> Authored: Tue Feb 17 12:00:16 2015 -0500 Committer: Eric Johnson <erjoh...@google.com> Committed: Mon Nov 9 15:34:11 2015 +0000 ---------------------------------------------------------------------- CHANGES.rst | 4 + contrib/update_google_prices.py | 89 ++++++++++++++++++++ contrib/utils.py | 48 +++++++++++ contrib/utils_test.py | 68 +++++++++++++++ libcloud/data/pricing.json | 158 ++++++++++++++++++++++++++++++----- 5 files changed, 345 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/c07dbe8c/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index ebc76e3..39db15a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -26,6 +26,10 @@ General Compute ~~~~~~~ +- [google compute] add pricing data update script + (GITHUB-464) + [Misha Brukman] + - Fix a bug in the ``list_volumes`` method in the CloudStack driver so it returns an empty list if no volumes are found. (GITHUB-617) http://git-wip-us.apache.org/repos/asf/libcloud/blob/c07dbe8c/contrib/update_google_prices.py ---------------------------------------------------------------------- diff --git a/contrib/update_google_prices.py b/contrib/update_google_prices.py new file mode 100755 index 0000000..e69ffe1 --- /dev/null +++ b/contrib/update_google_prices.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# +# 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. +""" +Loads Google Cloud Platform prices and updates the `pricing.json` data file. +""" + +import os +try: + import simplejson as json +except ImportError: + import json +import sys +import time +import urllib2 +import utils + +BASE_PATH = os.path.dirname(os.path.abspath(__file__)) +PRICING_FILE_PATH = os.path.join(BASE_PATH, '../libcloud/data/pricing.json') +PRICING_FILE_PATH = os.path.abspath(PRICING_FILE_PATH) + +GOOGLE_CLOUD_PRICES = 'https://cloudpricingcalculator.appspot.com/static/data/pricelist.json' + + +def main(argv): + # Read the current pricing data. + libcloud_data = {} + with open(PRICING_FILE_PATH, 'r') as libcloud_in: + libcloud_data = json.loads(libcloud_in.read()) + + # Download the current Google Cloud Platform pricing. + req = urllib2.Request(GOOGLE_CLOUD_PRICES, '') + google_ext_prices = json.loads(urllib2.urlopen(req).read()) + if 'gcp_price_list' not in google_ext_prices: + sys.stderr.write('Google Cloud pricing data missing "gcp_price_list" node\n') + sys.exit(1) + + # This is a map from regions used in the pricing JSON file to the regions as + # reflected in the Google Cloud Platform documentation and APIs. + pricing_to_region = { + 'us': 'us', + 'eu': 'europe', # alias for 'europe' + 'europe': 'europe', + 'apac': 'asia', # alias for 'asia' + 'asia': 'asia' + } + + # Initialize Google Cloud Platform regions. + for _, region in pricing_to_region.iteritems(): + libcloud_data['compute']['google_%s' % region] = {} + + # Update Google Compute Engine pricing. + gcp_price_list = google_ext_prices['gcp_price_list'] + gce_vm_prefix = 'CP-COMPUTEENGINE-VMIMAGE-' + for name, prices in gcp_price_list.iteritems(): + if not name.startswith(gce_vm_prefix): + continue + short_name = name[len(gce_vm_prefix):] + machine_type = short_name.lower() + for key, price in prices.iteritems(): + if key in pricing_to_region: + region = pricing_to_region[key] + libcloud_data['compute']['google_%s' % region][machine_type] = price + + # Update last-modified timestamp. + libcloud_data['updated'] = int(time.time()) + + # Write updated price list. + with open(PRICING_FILE_PATH, 'w') as libcloud_out: + json_str = json.dumps(libcloud_data, indent=4 * ' ', + item_sort_key=utils.sortKeysNumerically) + libcloud_out.write(json_str) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) http://git-wip-us.apache.org/repos/asf/libcloud/blob/c07dbe8c/contrib/utils.py ---------------------------------------------------------------------- diff --git a/contrib/utils.py b/contrib/utils.py new file mode 100644 index 0000000..2028721 --- /dev/null +++ b/contrib/utils.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# +# Copyright 2015 Google Inc. +# +# Licensed 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. +# +################################################################################ + +import re + + +def splitStringWithNumbers(string): + """Splits input string into a list of items, numeric and otherwise. + + Returns a list of values, each either an interpreted number, or a substring + of the original input. + + E.g., 'abc-123-def' => ['abc-', 123, '-def'] + """ + rawParts = re.split(r'(\d+)', string) + + # Filter out empty strings. + nonEmptyParts = filter(None, rawParts) + + # Convert any numeric strings to numbers. + def splitHelper(nonEmptyParts): + for part in nonEmptyParts: + if re.match(r'\d+', part): + yield int(part) + else: + yield part + + return list(splitHelper(nonEmptyParts)) + + +def sortKeysNumerically(key_value): + key, value = key_value + return splitStringWithNumbers(key) http://git-wip-us.apache.org/repos/asf/libcloud/blob/c07dbe8c/contrib/utils_test.py ---------------------------------------------------------------------- diff --git a/contrib/utils_test.py b/contrib/utils_test.py new file mode 100644 index 0000000..98016a4 --- /dev/null +++ b/contrib/utils_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +# +# Copyright 2015 Google Inc. +# +# Licensed 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. +# +################################################################################ + +try: + import simplejson as json +except ImportError: + import json +import unittest +import utils + + +class SplitStringToAlphaNumTest(unittest.TestCase): + + def testInitial(self): + self.assertEqual(utils.splitStringWithNumbers('12-abc'), + [12, '-abc']) + + def testMiddle(self): + self.assertEqual(utils.splitStringWithNumbers('abc-345-def'), + ['abc-', 345, '-def']) + + def testFinal(self): + self.assertEqual(utils.splitStringWithNumbers('xyz-42'), + ['xyz-', 42]) + + def testMultiple(self): + self.assertEqual(utils.splitStringWithNumbers('Aaa-123-Bbb-456-Ccc'), + ['Aaa-', 123, '-Bbb-', 456, '-Ccc']) + + +class SortKeysNumericallyTest(unittest.TestCase): + + def testSimple(self): + input = { + 'a-1': 1, + 'a-12': 12, + 'a-2': 2, + } + output = """\ +{ + "a-1": 1, + "a-2": 2, + "a-12": 12 +}\ +""" + self.assertEqual( + json.dumps(input, indent=4 * ' ', + item_sort_key=utils.sortKeysNumerically), + output) + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/libcloud/blob/c07dbe8c/libcloud/data/pricing.json ---------------------------------------------------------------------- diff --git a/libcloud/data/pricing.json b/libcloud/data/pricing.json index ab85715..5cb17e3 100644 --- a/libcloud/data/pricing.json +++ b/libcloud/data/pricing.json @@ -467,28 +467,142 @@ "x-large": 0.12 }, "gogrid": { - "16GB": 3.04, "1GB": 0.19, - "24GB": 4.56, "2GB": 0.38, "4GB": 0.76, - "512MB": 0.095, - "8GB": 1.52 + "8GB": 1.52, + "16GB": 3.04, + "24GB": 4.56, + "512MB": 0.095 + }, + "google_asia": { + "f1-micro": 0.009, + "f1-micro-preemptible": 0.005, + "g1-small": 0.03, + "g1-small-preemptible": 0.01, + "n1-highcpu-2": 0.084, + "n1-highcpu-2-preemptible": 0.022, + "n1-highcpu-4": 0.168, + "n1-highcpu-4-preemptible": 0.044, + "n1-highcpu-8": 0.336, + "n1-highcpu-8-preemptible": 0.088, + "n1-highcpu-16": 0.672, + "n1-highcpu-16-preemptible": 0.176, + "n1-highcpu-32": 1.344, + "n1-highcpu-32-preemptible": 0.352, + "n1-highmem-2": 0.139, + "n1-highmem-2-preemptible": 0.0385, + "n1-highmem-4": 0.278, + "n1-highmem-4-preemptible": 0.077, + "n1-highmem-8": 0.556, + "n1-highmem-8-preemptible": 0.154, + "n1-highmem-16": 1.112, + "n1-highmem-16-preemptible": 0.308, + "n1-highmem-32": 2.224, + "n1-highmem-32-preemptible": 0.616, + "n1-standard-1": 0.055, + "n1-standard-1-preemptible": 0.0165, + "n1-standard-2": 0.11, + "n1-standard-2-preemptible": 0.033, + "n1-standard-4": 0.22, + "n1-standard-4-preemptible": 0.066, + "n1-standard-8": 0.44, + "n1-standard-8-preemptible": 0.132, + "n1-standard-16": 0.88, + "n1-standard-16-preemptible": 0.264, + "n1-standard-32": 1.76, + "n1-standard-32-preemptible": 0.528 + }, + "google_europe": { + "f1-micro": 0.009, + "f1-micro-preemptible": 0.005, + "g1-small": 0.03, + "g1-small-preemptible": 0.01, + "n1-highcpu-2": 0.084, + "n1-highcpu-2-preemptible": 0.022, + "n1-highcpu-4": 0.168, + "n1-highcpu-4-preemptible": 0.044, + "n1-highcpu-8": 0.336, + "n1-highcpu-8-preemptible": 0.088, + "n1-highcpu-16": 0.672, + "n1-highcpu-16-preemptible": 0.176, + "n1-highcpu-32": 1.344, + "n1-highcpu-32-preemptible": 0.352, + "n1-highmem-2": 0.139, + "n1-highmem-2-preemptible": 0.0385, + "n1-highmem-4": 0.278, + "n1-highmem-4-preemptible": 0.077, + "n1-highmem-8": 0.556, + "n1-highmem-8-preemptible": 0.154, + "n1-highmem-16": 1.112, + "n1-highmem-16-preemptible": 0.308, + "n1-highmem-32": 2.224, + "n1-highmem-32-preemptible": 0.616, + "n1-standard-1": 0.055, + "n1-standard-1-preemptible": 0.0165, + "n1-standard-2": 0.11, + "n1-standard-2-preemptible": 0.033, + "n1-standard-4": 0.22, + "n1-standard-4-preemptible": 0.066, + "n1-standard-8": 0.44, + "n1-standard-8-preemptible": 0.132, + "n1-standard-16": 0.88, + "n1-standard-16-preemptible": 0.264, + "n1-standard-32": 1.76, + "n1-standard-32-preemptible": 0.528 + }, + "google_us": { + "f1-micro": 0.008, + "f1-micro-preemptible": 0.005, + "g1-small": 0.027, + "g1-small-preemptible": 0.01, + "n1-highcpu-2": 0.076, + "n1-highcpu-2-preemptible": 0.02, + "n1-highcpu-4": 0.152, + "n1-highcpu-4-preemptible": 0.04, + "n1-highcpu-8": 0.304, + "n1-highcpu-8-preemptible": 0.08, + "n1-highcpu-16": 0.608, + "n1-highcpu-16-preemptible": 0.16, + "n1-highcpu-32": 1.216, + "n1-highcpu-32-preemptible": 0.32, + "n1-highmem-2": 0.126, + "n1-highmem-2-preemptible": 0.035, + "n1-highmem-4": 0.252, + "n1-highmem-4-preemptible": 0.07, + "n1-highmem-8": 0.504, + "n1-highmem-8-preemptible": 0.14, + "n1-highmem-16": 1.008, + "n1-highmem-16-preemptible": 0.28, + "n1-highmem-32": 2.016, + "n1-highmem-32-preemptible": 0.56, + "n1-standard-1": 0.05, + "n1-standard-1-preemptible": 0.015, + "n1-standard-2": 0.1, + "n1-standard-2-preemptible": 0.03, + "n1-standard-4": 0.2, + "n1-standard-4-preemptible": 0.06, + "n1-standard-8": 0.4, + "n1-standard-8-preemptible": 0.12, + "n1-standard-16": 0.8, + "n1-standard-16-preemptible": 0.24, + "n1-standard-32": 1.6, + "n1-standard-32-preemptible": 0.48 }, "nephoscale": { "1": 0.6, + "3": 0.063, + "5": 0.031, + "7": 0.125, + "9": 0.188, "11": 0.35, "27": 0.0, - "3": 0.063, "46": 0.1, "48": 0.15, - "5": 0.031, "50": 0.28, "52": 0.48, "54": 0.938, - "56": 0.75, - "7": 0.125, - "9": 0.188 + "56": 0.75 }, "nimbus": { "m1.large": 0.0, @@ -640,11 +754,11 @@ "performance1-2": 0.08, "performance1-4": 0.16, "performance1-8": 0.32, - "performance2-120": 5.44, "performance2-15": 0.68, "performance2-30": 1.36, "performance2-60": 2.72, - "performance2-90": 4.08 + "performance2-90": 4.08, + "performance2-120": 5.44 }, "rackspacenovalon": { "2": 0.032, @@ -658,11 +772,11 @@ "performance1-2": 0.08, "performance1-4": 0.16, "performance1-8": 0.32, - "performance2-120": 5.44, "performance2-15": 0.68, "performance2-30": 1.36, "performance2-60": 2.72, - "performance2-90": 4.08 + "performance2-90": 4.08, + "performance2-120": 5.44 }, "rackspacenovasyd": { "2": 0.026, @@ -676,11 +790,11 @@ "performance1-2": 0.08, "performance1-4": 0.16, "performance1-8": 0.32, - "performance2-120": 5.44, "performance2-15": 0.68, "performance2-30": 1.36, "performance2-60": 2.72, - "performance2-90": 4.08 + "performance2-90": 4.08, + "performance2-120": 5.44 }, "rackspacenovaus": { "2": 0.022, @@ -694,11 +808,11 @@ "performance1-2": 0.08, "performance1-4": 0.16, "performance1-8": 0.32, - "performance2-120": 5.44, "performance2-15": 0.68, "performance2-30": 1.36, "performance2-60": 2.72, - "performance2-90": 4.08 + "performance2-90": 4.08, + "performance2-120": 5.44 }, "serverlove": { "extra-large": 0.615, @@ -724,10 +838,10 @@ "4": 0.045, "5": 0.045, "6": 0.045, - "7": 0.090, - "8": 0.090, - "9": 0.090, - "10": 0.090, + "7": 0.09, + "8": 0.09, + "9": 0.09, + "10": 0.09, "11": 0.205, "12": 0.205 }, @@ -736,5 +850,5 @@ } }, "storage": {}, - "updated": 1427829652 + "updated": 1447082902 } \ No newline at end of file