Implement /prices by extracting price from odoo
Change-Id: Ie1a39253fe1f722ccce79cf2657fa4e5a0bc8eaa
This commit is contained in:
parent
0d20c80a6e
commit
28fb58703e
@ -32,4 +32,14 @@ def prices_get():
|
||||
|
||||
@rest.get('/costs')
|
||||
def costs_get():
|
||||
return api.render(costs=costs.get_costs())
|
||||
return api.render(costs=costs.get_costs())
|
||||
|
||||
|
||||
@rest.get('/usages')
|
||||
def usages_get():
|
||||
return api.render(usages={})
|
||||
|
||||
|
||||
@rest.get('/health')
|
||||
def health_get():
|
||||
return api.render(health={})
|
||||
|
@ -23,7 +23,7 @@ from sqlalchemy import create_engine
|
||||
from sqlalchemy import pool
|
||||
|
||||
from distil.db.sqlalchemy import model_base
|
||||
from distil.openstack.common import importutils
|
||||
from oslo_utils import importutils
|
||||
|
||||
|
||||
importutils.import_module('distil.db.sqlalchemy.models')
|
||||
|
@ -100,16 +100,9 @@ def upgrade():
|
||||
# mysql_engine=MYSQL_ENGINE,
|
||||
# mysql_charset=MYSQL_CHARSET)
|
||||
|
||||
op.create_table('last_run',
|
||||
sa.Column('id', sa.Integer, primary_key=True,
|
||||
sa.Sequence("last_run_id_seq")),
|
||||
sa.Column('last_run', sa.DateTime(), nullable=True),
|
||||
mysql_engine=MYSQL_ENGINE,
|
||||
mysql_charset=MYSQL_CHARSET)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('project')
|
||||
op.drop_table('usage')
|
||||
op.drop_table('resource')
|
||||
op.drop_table('last_run')
|
@ -13,15 +13,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from oslo.utils import timeutils
|
||||
from oslo.db.sqlalchemy import models as oslo_models
|
||||
from oslo_utils import timeutils
|
||||
from oslo_db.sqlalchemy import models as oslo_models
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy.ext import declarative
|
||||
from sqlalchemy import Text
|
||||
from sqlalchemy.types import TypeDecorator
|
||||
|
||||
from distil.openstack.common import jsonutils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
|
||||
class JSONEncodedDict(TypeDecorator):
|
||||
|
@ -14,12 +14,24 @@
|
||||
# limitations under the License.
|
||||
|
||||
from distil import rater
|
||||
from distil.rater import rate_file
|
||||
from distil.utils import odoo
|
||||
|
||||
class OdooRater(rater.BaseRater):
|
||||
|
||||
def __init__(self, conf):
|
||||
self.prices = odoo.Odoo().get_prices()
|
||||
|
||||
def rate(self, name, region=None):
|
||||
erp = odoo.Odoo()
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
pass
|
||||
if not self.prices:
|
||||
return rate_file.FileRater().rate(name, region)
|
||||
|
||||
for region in self.prices:
|
||||
for category in self.prices[region]:
|
||||
for product in self.prices[region][category]:
|
||||
if product == name:
|
||||
return {'rate': self.prices[name]['price'],
|
||||
'unit': self.prices[name]['unit']
|
||||
}
|
||||
|
||||
return rate_file.FileRater().rate(name, region)
|
||||
|
0
distil/service/api/v2/__init__.py
Normal file
0
distil/service/api/v2/__init__.py
Normal file
23
distil/service/api/v2/costs.py
Normal file
23
distil/service/api/v2/costs.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
def get_costs():
|
||||
return {'id': 1}
|
@ -21,9 +21,5 @@ LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
def get_prices(format=None):
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
erp = odoo.Odoo()
|
||||
erp.get_prices()
|
||||
pdb.set_trace()
|
||||
return {'id': 1}
|
||||
prices = odoo.Odoo().get_prices()
|
||||
return prices
|
84
distil/utils/odoo.py
Normal file
84
distil/utils/odoo.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||
#
|
||||
# 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 odoorpc
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
PRODUCT_CATEGORY = ('Compute', 'Network', 'Block Storage', 'Object Storage')
|
||||
REGION_MAPPING = {'nz-por-1': 'NZ-POR-1', 'nz_wlg_2': 'NZ-WLG-2'}
|
||||
|
||||
class Odoo(object):
|
||||
|
||||
def __init__(self):
|
||||
self.odoo = odoorpc.ODOO(CONF.odoo.hostname,
|
||||
protocol=CONF.odoo.protocol,
|
||||
port=CONF.odoo.port,
|
||||
version=CONF.odoo.version)
|
||||
|
||||
self.odoo.login(CONF.odoo.database, CONF.odoo.user, CONF.odoo.password)
|
||||
|
||||
self.order = self.odoo.env['sale.order']
|
||||
self.orderline = self.odoo.env['sale.order.line']
|
||||
self.tenant = self.odoo.env['cloud.tenant']
|
||||
self.partner = self.odoo.env['res.partner']
|
||||
self.pricelist = self.odoo.env['product.pricelist']
|
||||
self.product = self.odoo.env['product.product']
|
||||
self.category = self.odoo.env['product.category']
|
||||
|
||||
def get_prices(self, region=None):
|
||||
# TODO(flwang): Need to cache the prices, now generally this method
|
||||
# will take 30+ seconds to get the two regions prices.
|
||||
if region:
|
||||
regions = [REGION_MAPPING[region.lower()]]
|
||||
else:
|
||||
regions = REGION_MAPPING.values()
|
||||
|
||||
prices = {}
|
||||
for r in regions:
|
||||
prices[r] = {}
|
||||
for category in PRODUCT_CATEGORY:
|
||||
prices[r][category.lower()] = {}
|
||||
c = self.category.search([
|
||||
('name', '=', category),
|
||||
('display_name', 'ilike', r)
|
||||
])
|
||||
product_ids = self.product.search([
|
||||
('categ_id', '=', c[0]),
|
||||
('sale_ok', '=', True),
|
||||
('active', '=', True)
|
||||
])
|
||||
products = self.odoo.execute('product.product',
|
||||
'read',
|
||||
product_ids)
|
||||
for p in products:
|
||||
name = p['name_template'][len(r)+1:]
|
||||
price = round(p['lst_price'], 5)
|
||||
# NOTE(flwang): default_code is Internal Reference on Odoo
|
||||
# GUI
|
||||
unit = p['default_code']
|
||||
desc = p['description']
|
||||
prices[r][category.lower()][name] = {'price': price,
|
||||
'unit': unit,
|
||||
'description': desc
|
||||
}
|
||||
|
||||
return prices
|
||||
|
||||
def get_customers(self):
|
||||
pass
|
@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env python2
|
||||
import sys
|
||||
import os
|
||||
import pprint
|
||||
import argparse
|
||||
import math
|
||||
import ConfigParser
|
||||
from decimal import Decimal
|
||||
|
||||
import odoorpc
|
||||
|
||||
# requires distilclient>=0.5.1
|
||||
from distilclient.client import Client as DistilClient
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--start', required=True, help='Start date')
|
||||
parser.add_argument('--end', required=True, help='End date')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
conf = ConfigParser.ConfigParser()
|
||||
conf.read(['glue.ini'])
|
||||
|
||||
region = conf.get('openstack', 'region')
|
||||
|
||||
oerp = odoorpc.ODOO(
|
||||
conf.get('odoo', 'hostname'),
|
||||
protocol=conf.get('odoo', 'protocol'),
|
||||
port=conf.getint('odoo', 'port'),
|
||||
version=conf.get('odoo', 'version')
|
||||
)
|
||||
oerp.login(
|
||||
conf.get('odoo', 'database'),
|
||||
conf.get('odoo', 'user'),
|
||||
conf.get('odoo', 'password')
|
||||
)
|
||||
|
||||
# debug helper
|
||||
def dump_all(model, fields, conds=None):
|
||||
print '%s:' % model
|
||||
ids = oerp.search(model, conds or [])
|
||||
objs = oerp.read(model, ids)
|
||||
for obj in objs:
|
||||
print ' %s %s' % (obj['id'], {f:obj[f] for f in fields})
|
||||
|
||||
pricelist_model = oerp.env['product.pricelist']
|
||||
pricelist = oerp.search('product.pricelist',
|
||||
[('name', '=', conf.get('odoo', 'export_pricelist'))])
|
||||
|
||||
product_category = oerp.search('product.category',
|
||||
[('name', '=', conf.get('odoo', 'product_category'))])
|
||||
|
||||
product_ids = oerp.search('product.product',
|
||||
[('categ_id', '=', product_category[0]),
|
||||
('sale_ok', '=', True),
|
||||
('active', '=', True)])
|
||||
|
||||
products = oerp.read('product.product', product_ids)
|
||||
|
||||
prices = {}
|
||||
|
||||
for p in products:
|
||||
if not p['name_template'].startswith(region + '.'):
|
||||
continue
|
||||
base_name = p['name_template'][len(region)+1:]
|
||||
# exported prices are for one unit -- do not take into account
|
||||
# any bulk pricing rules.
|
||||
unit_price = pricelist_model.price_get([pricelist[0]], p['id'], 1)[str(pricelist[0])]
|
||||
prices[base_name] = unit_price
|
||||
print '%s %s' % (base_name, unit_price)
|
||||
|
||||
# create the snapshot in distil
|
||||
dc = DistilClient(
|
||||
os_username=os.getenv('OS_USERNAME'),
|
||||
os_password=os.getenv('OS_PASSWORD'),
|
||||
os_tenant_id=os.getenv('OS_TENANT_ID'),
|
||||
os_auth_url=os.getenv('OS_AUTH_URL'),
|
||||
os_region_name=os.getenv('OS_REGION_NAME'))
|
||||
|
||||
dc.set_prices(args.start, args.end, prices)
|
25
tools/config/check_uptodate.sh
Normal file
25
tools/config/check_uptodate.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PROJECT_NAME=${PROJECT_NAME:-wellington}
|
||||
CFGFILE_NAME=${PROJECT_NAME}.conf.sample
|
||||
|
||||
if [ -e etc/${PROJECT_NAME}/${CFGFILE_NAME} ]; then
|
||||
CFGFILE=etc/${PROJECT_NAME}/${CFGFILE_NAME}
|
||||
elif [ -e etc/${CFGFILE_NAME} ]; then
|
||||
CFGFILE=etc/${CFGFILE_NAME}
|
||||
else
|
||||
echo "${0##*/}: can not find config file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMPDIR=`mktemp -d /tmp/${PROJECT_NAME}.XXXXXX`
|
||||
trap "rm -rf $TEMPDIR" EXIT
|
||||
|
||||
tools/config/generate_sample.sh -b ./ -p ${PROJECT_NAME} -o ${TEMPDIR}
|
||||
|
||||
if ! diff -u ${TEMPDIR}/${CFGFILE_NAME} ${CFGFILE}
|
||||
then
|
||||
echo "${0##*/}: ${PROJECT_NAME}.conf.sample is not up to date."
|
||||
echo "${0##*/}: Please run ${0%%${0##*/}}generate_sample.sh."
|
||||
exit 1
|
||||
fi
|
119
tools/config/generate_sample.sh
Normal file
119
tools/config/generate_sample.sh
Normal file
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
print_hint() {
|
||||
echo "Try \`${0##*/} --help' for more information." >&2
|
||||
}
|
||||
|
||||
PARSED_OPTIONS=$(getopt -n "${0##*/}" -o hb:p:m:l:o: \
|
||||
--long help,base-dir:,package-name:,output-dir:,module:,library: -- "$@")
|
||||
|
||||
if [ $? != 0 ] ; then print_hint ; exit 1 ; fi
|
||||
|
||||
eval set -- "$PARSED_OPTIONS"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "${0##*/} [options]"
|
||||
echo ""
|
||||
echo "options:"
|
||||
echo "-h, --help show brief help"
|
||||
echo "-b, --base-dir=DIR project base directory"
|
||||
echo "-p, --package-name=NAME project package name"
|
||||
echo "-o, --output-dir=DIR file output directory"
|
||||
echo "-m, --module=MOD extra python module to interrogate for options"
|
||||
echo "-l, --library=LIB extra library that registers options for discovery"
|
||||
exit 0
|
||||
;;
|
||||
-b|--base-dir)
|
||||
shift
|
||||
BASEDIR=`echo $1 | sed -e 's/\/*$//g'`
|
||||
shift
|
||||
;;
|
||||
-p|--package-name)
|
||||
shift
|
||||
PACKAGENAME=`echo $1`
|
||||
shift
|
||||
;;
|
||||
-o|--output-dir)
|
||||
shift
|
||||
OUTPUTDIR=`echo $1 | sed -e 's/\/*$//g'`
|
||||
shift
|
||||
;;
|
||||
-m|--module)
|
||||
shift
|
||||
MODULES="$MODULES -m $1"
|
||||
shift
|
||||
;;
|
||||
-l|--library)
|
||||
shift
|
||||
LIBRARIES="$LIBRARIES -l $1"
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BASEDIR=${BASEDIR:-`pwd`}
|
||||
if ! [ -d $BASEDIR ]
|
||||
then
|
||||
echo "${0##*/}: missing project base directory" >&2 ; print_hint ; exit 1
|
||||
elif [[ $BASEDIR != /* ]]
|
||||
then
|
||||
BASEDIR=$(cd "$BASEDIR" && pwd)
|
||||
fi
|
||||
|
||||
PACKAGENAME=${PACKAGENAME:-${BASEDIR##*/}}
|
||||
TARGETDIR=$BASEDIR/$PACKAGENAME
|
||||
if ! [ -d $TARGETDIR ]
|
||||
then
|
||||
echo "${0##*/}: invalid project package name" >&2 ; print_hint ; exit 1
|
||||
fi
|
||||
|
||||
OUTPUTDIR=${OUTPUTDIR:-$BASEDIR/etc}
|
||||
# NOTE(bnemec): Some projects put their sample config in etc/,
|
||||
# some in etc/$PACKAGENAME/
|
||||
if [ -d $OUTPUTDIR/$PACKAGENAME ]
|
||||
then
|
||||
OUTPUTDIR=$OUTPUTDIR/$PACKAGENAME
|
||||
elif ! [ -d $OUTPUTDIR ]
|
||||
then
|
||||
echo "${0##*/}: cannot access \`$OUTPUTDIR': No such file or directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASEDIRESC=`echo $BASEDIR | sed -e 's/\//\\\\\//g'`
|
||||
find $TARGETDIR -type f -name "*.pyc" -delete
|
||||
FILES=$(find $TARGETDIR -type f -name "*.py" ! -path "*/tests/*" \
|
||||
-exec grep -l "Opt(" {} + | sed -e "s/^$BASEDIRESC\///g" | sort -u)
|
||||
|
||||
RC_FILE="`dirname $0`/oslo.config.generator.rc"
|
||||
if test -r "$RC_FILE"
|
||||
then
|
||||
source "$RC_FILE"
|
||||
fi
|
||||
|
||||
for mod in ${WELLINGTON_CONFIG_GENERATOR_EXTRA_MODULES}; do
|
||||
MODULES="$MODULES -m $mod"
|
||||
done
|
||||
|
||||
for lib in ${WELLINGTON_CONFIG_GENERATOR_EXTRA_LIBRARIES}; do
|
||||
LIBRARIES="$LIBRARIES -l $lib"
|
||||
done
|
||||
|
||||
export EVENTLET_NO_GREENDNS=yes
|
||||
|
||||
OS_VARS=$(set | sed -n '/^OS_/s/=[^=]*$//gp' | xargs)
|
||||
[ "$OS_VARS" ] && eval "unset \$OS_VARS"
|
||||
DEFAULT_MODULEPATH=wellington.openstack.common.config.generator
|
||||
MODULEPATH=${MODULEPATH:-$DEFAULT_MODULEPATH}
|
||||
OUTPUTFILE=$OUTPUTDIR/$PACKAGENAME.conf.sample
|
||||
python -m $MODULEPATH $MODULES $LIBRARIES $FILES > $OUTPUTFILE
|
||||
|
||||
# Hook to allow projects to append custom config file snippets
|
||||
CONCAT_FILES=$(ls $BASEDIR/tools/config/*.conf.sample 2>/dev/null)
|
||||
for CONCAT_FILE in $CONCAT_FILES; do
|
||||
cat $CONCAT_FILE >> $OUTPUTFILE
|
||||
done
|
1
tools/config/oslo.config.generator.rc
Normal file
1
tools/config/oslo.config.generator.rc
Normal file
@ -0,0 +1 @@
|
||||
export WELLINGTON_CONFIG_GENERATOR_EXTRA_MODULES="keystoneclient.middleware.auth_token"
|
3
tools/install_venv
Normal file
3
tools/install_venv
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
tox -vvv -evenv -- python --version
|
75
tools/install_venv.py
Normal file
75
tools/install_venv.py
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# Copyright 2013 IBM Corp.
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 ConfigParser
|
||||
import os
|
||||
import sys
|
||||
|
||||
import install_venv_common as install_venv # flake8: noqa
|
||||
|
||||
|
||||
def print_help(project, venv, root):
|
||||
help = """
|
||||
%(project)s development environment setup is complete.
|
||||
|
||||
%(project)s development uses virtualenv to track and manage Python
|
||||
dependencies while in development and testing.
|
||||
|
||||
To activate the %(project)s virtualenv for the extent of your current
|
||||
shell session you can run:
|
||||
|
||||
$ source %(venv)s/bin/activate
|
||||
|
||||
Or, if you prefer, you can run commands in the virtualenv on a case by
|
||||
case basis by running:
|
||||
|
||||
$ %(root)s/tools/with_venv.sh <your command>
|
||||
"""
|
||||
print help % dict(project=project, venv=venv, root=root)
|
||||
|
||||
|
||||
def main(argv):
|
||||
root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
if os.environ.get('tools_path'):
|
||||
root = os.environ['tools_path']
|
||||
venv = os.path.join(root, '.venv')
|
||||
if os.environ.get('venv'):
|
||||
venv = os.environ['venv']
|
||||
|
||||
pip_requires = os.path.join(root, 'requirements.txt')
|
||||
test_requires = os.path.join(root, 'test-requirements.txt')
|
||||
py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
|
||||
setup_cfg = ConfigParser.ConfigParser()
|
||||
setup_cfg.read('setup.cfg')
|
||||
project = setup_cfg.get('metadata', 'name')
|
||||
|
||||
install = install_venv.InstallVenv(
|
||||
root, venv, pip_requires, test_requires, py_version, project)
|
||||
options = install.parse_args(argv)
|
||||
install.check_python_version()
|
||||
install.check_dependencies()
|
||||
install.create_virtualenv(no_site_packages=options.no_site_packages)
|
||||
install.install_dependencies()
|
||||
install.post_process()
|
||||
print_help(project, venv, root)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
212
tools/install_venv_common.py
Normal file
212
tools/install_venv_common.py
Normal file
@ -0,0 +1,212 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Provides methods needed by installation script for OpenStack development
|
||||
virtual environments.
|
||||
|
||||
Since this script is used to bootstrap a virtualenv from the system's Python
|
||||
environment, it should be kept strictly compatible with Python 2.6.
|
||||
|
||||
Synced in from openstack-common
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
class InstallVenv(object):
|
||||
|
||||
def __init__(self, root, venv, requirements,
|
||||
test_requirements, py_version,
|
||||
project):
|
||||
self.root = root
|
||||
self.venv = venv
|
||||
self.requirements = requirements
|
||||
self.test_requirements = test_requirements
|
||||
self.py_version = py_version
|
||||
self.project = project
|
||||
|
||||
def die(self, message, *args):
|
||||
print(message % args, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def check_python_version(self):
|
||||
if sys.version_info < (2, 6):
|
||||
self.die("Need Python Version >= 2.6")
|
||||
|
||||
def run_command_with_code(self, cmd, redirect_output=True,
|
||||
check_exit_code=True):
|
||||
"""Runs a command in an out-of-process shell.
|
||||
|
||||
Returns the output of that command. Working directory is self.root.
|
||||
"""
|
||||
if redirect_output:
|
||||
stdout = subprocess.PIPE
|
||||
else:
|
||||
stdout = None
|
||||
|
||||
proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
|
||||
output = proc.communicate()[0]
|
||||
if check_exit_code and proc.returncode != 0:
|
||||
self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
|
||||
return (output, proc.returncode)
|
||||
|
||||
def run_command(self, cmd, redirect_output=True, check_exit_code=True):
|
||||
return self.run_command_with_code(cmd, redirect_output,
|
||||
check_exit_code)[0]
|
||||
|
||||
def get_distro(self):
|
||||
if (os.path.exists('/etc/fedora-release') or
|
||||
os.path.exists('/etc/redhat-release')):
|
||||
return Fedora(
|
||||
self.root, self.venv, self.requirements,
|
||||
self.test_requirements, self.py_version, self.project)
|
||||
else:
|
||||
return Distro(
|
||||
self.root, self.venv, self.requirements,
|
||||
self.test_requirements, self.py_version, self.project)
|
||||
|
||||
def check_dependencies(self):
|
||||
self.get_distro().install_virtualenv()
|
||||
|
||||
def create_virtualenv(self, no_site_packages=True):
|
||||
"""Creates the virtual environment and installs PIP.
|
||||
|
||||
Creates the virtual environment and installs PIP only into the
|
||||
virtual environment.
|
||||
"""
|
||||
if not os.path.isdir(self.venv):
|
||||
print('Creating venv...', end=' ')
|
||||
if no_site_packages:
|
||||
self.run_command(['virtualenv', '-q', '--no-site-packages',
|
||||
self.venv])
|
||||
else:
|
||||
self.run_command(['virtualenv', '-q', self.venv])
|
||||
print('done.')
|
||||
else:
|
||||
print("venv already exists...")
|
||||
pass
|
||||
|
||||
def pip_install(self, *args):
|
||||
self.run_command(['tools/with_venv.sh',
|
||||
'pip', 'install', '--upgrade'] + list(args),
|
||||
redirect_output=False)
|
||||
|
||||
def install_dependencies(self):
|
||||
print('Installing dependencies with pip (this can take a while)...')
|
||||
|
||||
# First things first, make sure our venv has the latest pip and
|
||||
# setuptools and pbr
|
||||
self.pip_install('pip>=1.4')
|
||||
self.pip_install('setuptools')
|
||||
self.pip_install('pbr')
|
||||
|
||||
self.pip_install('-r', self.requirements)
|
||||
self.pip_install('-r', self.test_requirements)
|
||||
|
||||
def post_process(self):
|
||||
self.get_distro().post_process()
|
||||
|
||||
def parse_args(self, argv):
|
||||
"""Parses command-line arguments."""
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-n', '--no-site-packages',
|
||||
action='store_true',
|
||||
help="Do not inherit packages from global Python "
|
||||
"install")
|
||||
return parser.parse_args(argv[1:])[0]
|
||||
|
||||
|
||||
class Distro(InstallVenv):
|
||||
|
||||
def check_cmd(self, cmd):
|
||||
return bool(self.run_command(['which', cmd],
|
||||
check_exit_code=False).strip())
|
||||
|
||||
def install_virtualenv(self):
|
||||
if self.check_cmd('virtualenv'):
|
||||
return
|
||||
|
||||
if self.check_cmd('easy_install'):
|
||||
print('Installing virtualenv via easy_install...', end=' ')
|
||||
if self.run_command(['easy_install', 'virtualenv']):
|
||||
print('Succeeded')
|
||||
return
|
||||
else:
|
||||
print('Failed')
|
||||
|
||||
self.die('ERROR: virtualenv not found.\n\n%s development'
|
||||
' requires virtualenv, please install it using your'
|
||||
' favorite package management tool' % self.project)
|
||||
|
||||
def post_process(self):
|
||||
"""Any distribution-specific post-processing gets done here.
|
||||
|
||||
In particular, this is useful for applying patches to code inside
|
||||
the venv.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Fedora(Distro):
|
||||
"""This covers all Fedora-based distributions.
|
||||
|
||||
Includes: Fedora, RHEL, CentOS, Scientific Linux
|
||||
"""
|
||||
|
||||
def check_pkg(self, pkg):
|
||||
return self.run_command_with_code(['rpm', '-q', pkg],
|
||||
check_exit_code=False)[1] == 0
|
||||
|
||||
def apply_patch(self, originalfile, patchfile):
|
||||
self.run_command(['patch', '-N', originalfile, patchfile],
|
||||
check_exit_code=False)
|
||||
|
||||
def install_virtualenv(self):
|
||||
if self.check_cmd('virtualenv'):
|
||||
return
|
||||
|
||||
if not self.check_pkg('python-virtualenv'):
|
||||
self.die("Please install 'python-virtualenv'.")
|
||||
|
||||
super(Fedora, self).install_virtualenv()
|
||||
|
||||
def post_process(self):
|
||||
"""Workaround for a bug in eventlet.
|
||||
|
||||
This currently affects RHEL6.1, but the fix can safely be
|
||||
applied to all RHEL and Fedora distributions.
|
||||
|
||||
This can be removed when the fix is applied upstream.
|
||||
|
||||
Nova: https://bugs.launchpad.net/nova/+bug/884915
|
||||
Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
|
||||
RHEL: https://bugzilla.redhat.com/958868
|
||||
"""
|
||||
|
||||
if os.path.exists('contrib/redhat-eventlet.patch'):
|
||||
# Install "patch" program if it's not there
|
||||
if not self.check_pkg('patch'):
|
||||
self.die("Please install 'patch'.")
|
||||
|
||||
# Apply the eventlet patch
|
||||
self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
|
||||
'site-packages',
|
||||
'eventlet/green/subprocess.py'),
|
||||
'contrib/redhat-eventlet.patch')
|
4
tools/with_venv.sh
Executable file
4
tools/with_venv.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
TOOLS=`dirname $0`
|
||||
VENV=$TOOLS/../.venv
|
||||
source $VENV/bin/activate && $@
|
Loading…
Reference in New Issue
Block a user