initial version to setup compass-monit

Change-Id: I04da5b53d1c661ab7b4c1daff4977166daf07cbd
This commit is contained in:
xiaodongwang 2014-09-06 20:02:43 -07:00
parent 3a49409468
commit 722220f77c
24 changed files with 1306 additions and 531 deletions

42
.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
*.py[cod]
*~
*.swp
# C extensions
*.so
# log file
*.log
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
.testrepository
cover
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject

View File

View File

@ -0,0 +1,19 @@
# Copyright 2014 Huawei Technologies Co. 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 flask import Flask
app = Flask(__name__)
app.debug = True

View File

@ -1,480 +0,0 @@
#!/usr/bin/env python
from flask import Flask
from flask.ext import restful
from flask.ext.restful import reqparse
from flask.ext.jsonpify import jsonify
#from flask import request
from flask import redirect, request, current_app
import urllib
import json
import requests
import random
import time
app = Flask(__name__)
api = restful.Api(app)
#class MyEncoder1(json.JSONEncoder):
# def default(self, obj):
class HelloWorld(restful.Resource):
def get(self):
return {'warning': 'Please use /monit/api'}
api.add_resource(HelloWorld, '/', '/monit')
class HelloInfo(restful.Resource):
def get(self):
#return {'info': 'Select host, hostgroup, topology, service from here'}
r = requests.get(ROOT_URL+"/api/v1/tagnames")
return r.json()
api.add_resource(HelloInfo, '/monit/api')
class Proxy(restful.Resource):
def get(self, url):
# Get URL Parameters for host and metric info
urlParam = request.url.split('proxy/', 9);
my_url = urlParam[1]
#return my_url
r = requests.get(my_url, stream = True)
#return r.text
return json.loads(r.text)
#return Response(stream_with_context(r.iter_content()), content_type = r.headers['content-type'])
api.add_resource(Proxy, '/monit/api/proxy/<path:url>', defaults={'url': '/HelloWorld'})
class InvalidUrl(restful.Resource):
def get(self):
return {'warning': 'Please form the URL'}
api.add_resource(InvalidUrl, '/monit')
class Hosts(restful.Resource):
def get(self):
return {'hosts': '[\'host1\',\'host2\',\'host3\',\'host4\',\'host5\',\'host6\']'}
r = requests.get(ROOT_URL +"/api/v1/tagvalues")
# weed out just the hosts if possible
return r.json()
api.add_resource(Hosts, '/monit/api/hosts')
class Metrics(restful.Resource):
def get(self):
# This is a comprehensive list of metrics not per host due to limitations
r = requests.get(ROOT_URL +"/api/v1/metricnames")
return r.json()
api.add_resource(Metrics, '/monit/api/metrics')
class TsGenerateAlarmData:
def __init__(self):
self.params = " "
self.statusStr = "failure"
#self.resultStr = " "
buildStr = '"alarms":['
buildStr += '{"id":"critical","name":"critical","data":['
for j in range(1, 30):
duration = random.randint(0,172800) # number of seconds in 2 days
starttime = long(time.time() - (j * 86400)) # number of seconds in a day
endtime = long(starttime + duration/2)
buildStr += '{"start":' + str(long(starttime*1000)) + ',"end":' + str(long(endtime*1000)) + '},'
#remove a comma
buildStr = buildStr[:-1]
buildStr += ']},{"id":"minor","name":"minor","data":['
for j in range(1, 30):
duration = random.randint(0,172800) # number of seconds in 2 days
starttime = long(time.time() - (j * 86400)) # number of seconds in a day
endtime = long(starttime + duration/2)
buildStr += '{"start":' + str(long(starttime*1000)) + ',"end":' + str(long(endtime*1000)) + '},'
#remove a comma
buildStr = buildStr[:-1]
buildStr += ']},{"id":"positive","name":"positive","data":['
for j in range(1, 30):
duration = random.randint(0,172800) # number of seconds in 2 days
starttime = long(time.time() - (j * 86400)) # number of seconds in a day
endtime = long(starttime + duration/2)
buildStr += '{"start":' + str(long(starttime*1000)) + ',"end":' + str(long(endtime*1000)) + '},'
#remove a comma
buildStr = buildStr[:-1]
buildStr += ']},{"id":"info","name":"info","data":['
for j in range(1, 30):
duration = random.randint(0,172800) # number of seconds in 2 days
starttime = long(time.time() - (j * 86400)) # number of seconds in a day
endtime = long(starttime + duration/2)
buildStr += '{"start":' + str(long(starttime*1000)) + ',"end":' + str(long(endtime*1000)) + '},'
#remove a comma
buildStr = buildStr[:-1]
self.resultStr = buildStr + ']}]'
class TsQueryBuilder:
def __init__(self, host_s, metric):
self.params = " "
self.statusStr = "failure"
buildStr = '{ "metrics":['
repeatStr = '{"tags":{ "host":["HOSTNAME"]},"name":"METRIC","group_by":[{"name":"tag", "tags":["host"]}],"aggregators":[{"name":"sum", "align_sampling": false, "sampling":{ "value": "2", "unit": "minutes"}}]}'
finStr = '],"start_relative": { "value": "17", "unit": "minutes" }}'
repeatStr = repeatStr.replace("METRIC", metric)
i = len(host_s);
for hostname in host_s:
buildStr += repeatStr.replace("HOSTNAME", host_s[i-1])
i -= 1
if i != 0:
buildStr += ','
self.params = buildStr + finStr
#print "Query: " + self.params
r = requests.post(ROOT_URL +"/api/v1/datapoints/query", data=self.params)
# check the POST status and return success or fail here
if r.status_code == requests.codes.ok:
self.statusStr = "success"
self.resp_dict = r.json()
class HostMetric(restful.Resource):
def get(self, hostname, metricname):
# Get URL Parameters for host and metric info
urlParam = request.url.split('/', 9);
MyList = [urlParam[6]]
# Create and execute the query
qb = TsQueryBuilder(MyList, urlParam[8])
# Create JSON Prefix
valStr = '{"result":[{"metrics":[{"id":"' + urlParam[8] + '","data":['
# add all time series data
for key, value in qb.resp_dict["queries"][0]["results"][0]["values"]:
# round this value down
digits = str(key)
key = key - (int(digits[8]) * 10000)
key = key - (int(digits[9]) * 1000)
key = key - (int(digits[10]) * 100)
valStr += '{"time":' + str(key) + ',"value":' + str(value) + '},'
#remove a comma
valStr = valStr[:-1]
a = TsGenerateAlarmData()
#add final braces
valStr += ']}],'
valStr += a.resultStr
valStr += ',"id":"' + urlParam[6] + '"}],"status":"' + qb.statusStr + '"}'
#return valStr
return json.loads(valStr)
api.add_resource(
HostMetric,
'/monit/api/host/<hostname>/metric/<metricname>',
defaults={'hostname': '', 'metricname': ''}
)
class HostGroupMetric(restful.Resource):
def get(self, hostgroup, metricname):
# Get URL Parameters for host and metric info
urlParam = request.url.split('/', 9);
hostgroup = urlParam[6]
# need logic to query hostgroup and convert to list
MyList = ["host1","host2","host3","host4","host5","host6"]
# Create and execute the query
qb = TsQueryBuilder(MyList, urlParam[8])
# Create JSON Prefix
valStr = '{"result":['
#return qb.resp_dict
# add all time series data
i = 0
for host in MyList:
valStr += '{"metrics":[{"id":"' + urlParam[8] + '","data":['
for skey, svalue in qb.resp_dict["queries"][i]["results"][0]["values"]:
# round this value down
digits = str(skey)
skey = skey - (int(digits[8]) * 10000)
skey = skey - (int(digits[9]) * 1000)
skey = skey - (int(digits[10]) * 100)
valStr += '{"time":' + str(skey) + ',"value":' + str(svalue) + '},'
#remove a comma
valStr = valStr[:-1]
#add final recordbraces
#valStr += ']}],"id":"' + MyList[i] + '"},'
a = TsGenerateAlarmData()
#add final record braces
valStr += ']}],'
valStr += a.resultStr
valStr += ',"id":"' + MyList[i] + '"},'
i += 1;
#remove a comma
valStr = valStr[:-1]
#add final braces
valStr += '],"status":"' + qb.statusStr + '"}'
#return valStr
return json.loads(valStr)
api.add_resource(
HostGroupMetric,
'/monit/api/hostgroup/<hostgroup>/metric/<metricname>',
defaults={'hostgroup': '', 'metricname': ''}
)
class RsHostMetric(restful.Resource):
def get(self, hostname, metricname):
# Get URL Parameters for host and metric info
urlParam = request.url.split('/', 9);
MyList = [urlParam[6]]
# Create and execute the query
qb = TsQueryBuilder(MyList, urlParam[8])
# Create JSON Prefix
valStr = '{"series":[{"metrics":[{"id":"' + urlParam[8] + '","data":['
# add all time series data
for key, value in qb.resp_dict["queries"][0]["results"][0]["values"]:
# round this value down
digits = str(key)
key = key - (int(digits[8]) * 10000)
key = key - (int(digits[9]) * 1000)
key = key - (int(digits[10]) * 100)
valStr += '{"time":' + str(key) + ',"value":' + str(value) + '},'
#remove a comma
valStr = valStr[:-1]
#add final braces
valStr += ']}],"id":"' + urlParam[6] + '"}],"status":"' + qb.statusStr + '"}'
return json.loads(valStr)
api.add_resource(
RsHostMetric,
'/monit/api/rshost/<hostname>/metric/<metricname>',
defaults={'hostname': '', 'metricname': ''}
)
class RsHostGroupMetric(restful.Resource):
def get(self, hostgroup, metricname):
# Get URL Parameters for host and metric info
urlParam = request.url.split('/', 9) #;
urlCruft = urlParam[8].split('?', 2) #;
hostgroup = urlParam[6]
themetric = urlCruft[0]
print themetric
# need logic to query hostgroup and convert to list
MyList = ["host1","host2","host3","host4","host5","host6"]
# Create and execute the query
qb = TsQueryBuilder(MyList, themetric)
# Create JSON Prefix
valStr = '['
#return qb.resp_dict
# add all time series data
i = 0
for host in MyList:
valStr += '{"name":"' + MyList[i] + ": " + themetric + '","data":['
for skey, svalue in qb.resp_dict["queries"][i]["results"][0]["values"]:
# round this value down
digits = str(skey)
skey = skey - (int(digits[8]) * 10000)
skey = skey - (int(digits[9]) * 1000)
skey = skey - (int(digits[10]) * 100)
valStr += '{"x":' + str(skey) + ',"y":' + str(svalue) + '},'
#remove a comma
valStr = valStr[:-1]
#add final recordbraces
valStr += ']},'
i += 1;
#remove a comma
valStr = valStr[:-1]
#add final braces
valStr += ']'
callback = request.args.get('callback', False)
if callback:
content = str(callback) + '(' + valStr + ')'
return current_app.response_class(content, mimetype='application/json')
#return valStr
return json.loads(valStr)
api.add_resource(
RsHostGroupMetric,
'/monit/api/rshostgroup/<hostgroup>/metric/<metricname>',
defaults={'hostgroup': '', 'metricname': ''}
)
class Alarms(restful.Resource):
def get(self):
alarms = TsGenerateAlarmData()
r = alarms.resultStr
#return r
return json.loads("{"+r+"}")
api.add_resource(Alarms, '/monit/api/alarms')
class Services(restful.Resource):
def get(self):
r = requests.get(ROOT_URL +"/api/v1/services")
return r.json()
api.add_resource(Services, '/monit/api/services')
class Topology(restful.Resource):
def get(self):
#r = requests.get(ROOT_URL +"/api/v1/topology")
#return r.json()
valAStr = '{"status":"success", "result":[ {"id":"uc-datacenter","name":"uc-datacenter","children":[ {"id":"nova-compute","name":"nova-compute","state":"running","resource":"services","type":"service","children":[ {"id":"compute-server-1.huawei.com","name":"compute-server-1.huawei.com","state":"running","resource":"hosts","type":"server", "children":[ {"id":"nova-compute","name":"nova-compute","state":"running","resource":"services","type":"service","children":[]}, {"id":"nova-consoleauth","name":"nova-consoleauth","state":"running","resource":"services","type":"service","children":[]} ]}, {"id":"compute-server-2.huawei.com","name":"compute-server-2.huawei.com","state":"running","resource":"hosts","type":"server", "children":[ {"id":"nova-novncproxy","name":"nova-novncproxy","state":"running","resource":"services","type":"service","children":[]}, {"id":"ceilometer-agent-compute","name":"ceilometer-agent-compute","state":"running","resource":"services","type":"service","children":[]}, {"id":"neutron-openvswitch-agent","name":"nova-openvswitch-agent","state":"running","resource":"services","type":"service","children":[]} ]} ]} ]} ]}'
valStr = '{"status":"success", "result":[{"id":"Huawei-Lab-C","name":"Huawei-Lab-C","children":[ {"id":"10.145.81.219","name":"10.145.81.219","state":"warning","resource":"services","type":"service","children":[ {"name":"server-1.huawei.com","id":"host1.huawei.com","state":"running","resource":"hosts","type":"server", "children":[]}, {"name":"server-2.huawei.com","id":"host2.huawei.com","state":"running","resource":"hosts","type":"server", "children":[]}, {"name":"server-3.huawei.com","id":"host3.huawei.com","state":"running","resource":"hosts","type":"server", "children":[]}, {"name":"server-4.huawei.com","id":"host4.huawei.com","state":"running","resource":"hosts","type":"server", "children":[]}, {"name":"server-5.huawei.com","id":"host5.huawei.com","state":"warning","resource":"hosts","type":"server", "children":[]}, {"name":"server-6.huawei.com","id":"host6.huawei.com","state":"running","resource":"hosts","type":"server", "children":[]}, {"name":"server-7.huawei.com","id":"host7.huawei.com","state":"critical","resource":"hosts","type":"server", "children":[]}, {"name":"monit-server-1.huawei.com","id":"10_145_81_205","state":"running","resource":"hosts","type":"server", "children":[]}]}]}]}'
return json.loads(valStr)
#return valStr
api.add_resource(Topology, '/monit/api/topologies/1')
if __name__ == '__main__':
global ROOT_URL
ROOT_URL = "http://10.145.81.205:8080"
app.run(host='0.0.0.0', debug=True, threaded=True)
#app.run(debug=True)
"""
# Topo Structure
{"status":"success", "result":[
{"id":"uc-datacenter","name":"uc-datacenter","children":[
{"id":"nova-compute","name":"nova-compute","state":"running","resource":"services","type":"service","children":[
{"id":"compute-server-1.huawei.com","name":"compute-server-1.huawei.com","state":"running","resource":"hosts","type":"server", "children":[
{"id":"nova-compute","name":"nova-compute","state":"running","resource":"services","type":"service","children":[]},
{"id":"nova-consoleauth","name":"nova-consoleauth","state":"running","resource":"services","type":"service","children":[]},
]},
{"id":"compute-server-2.huawei.com","name":"compute-server-2.huawei.com","state":"running","resource":"hosts","type":"server", "children":[
{"id":"nova-novncproxy","name":"nova-novncproxy","state":"running","resource":"services","type":"service","children":[]},
{"id":"ceilometer-agent-compute","name":"ceilometer-agent-compute","state":"running","resource":"services","type":"service","children":[]},
{"id":"neutron-openvswitch-agent","name":"nova-openvswitch-agent","state":"running","resource":"services","type":"service","children":[]}
]}
]}
]}
]}
#Query structure
{
"metrics": [
{
"tags": {
"host": [ "nova-compute_local" ]
},
"name": "cpu.0.cpu.idle.value",
"aggregators": [
{
"name": "avg",
"align_sampling": true,
"sampling": { "value": "1", "unit": "minutes" }
}
]
},
{
"tags": {
"host": [ "controller_local" ]
},
"name": "cpu.0.cpu.idle.value",
"aggregators": [
{
"name": "avg",
"align_sampling": true,
"sampling": { "value": "1", "unit": "minutes" }
}
]
},
{
"tags": {
"host": [ "nova-compute_local" ]
},
"name": "cpu.0.cpu.idle.value",
"aggregators": [
{
"name": "avg",
"align_sampling": true,
"sampling": { "value": "1", "unit": "minutes" }
}
]
},
{
"tags": {
"host": [ "neutron_local" ]
},
"name": "cpu.0.cpu.idle.value",
"aggregators": [
{
"name": "avg",
"align_sampling": true,
"sampling": { "value": "1", "unit": "minutes" }
}
]
}
],
"cache_time": 0,
"start_relative": {
"value": "1",
"unit": "hours"
}
}
# Alarms
{?[?{?[?{.. more json here ...
{"id":"uc-server-1.huawei.com","name":"uc-server-1.huawei.com","resource":"hosts","state":"running","type":"server", "metrics":[], "alarms":[
{"id":"critical","name":"critical","data":[
{"start":1406831282409,"end":1406870037149},
{"start":1406745382748,"end":1406761927670}
]},
{"id":"minor","name":"minor","data":[
{"start":1406873957790,"end":1406886655198},
{"start":1406774590378,"end":1406850781190}
]},
{"id":"positive","name":"positive","data":[
{"start":1406873957790,"end":1406886655198},
{"start":1406774590378,"end":1406850781190}
]},
{"id":"info","name":"info","data":[
{"start":1406873957790,"end":1406886655198},
{"start":1406774590378,"end":1406850781190}
]}
]}
... more json here...?}?]?}?]?}
"""

View File

@ -1,48 +1,65 @@
#!/usr/bin/env python
# Copyright 2014 Huawei Technologies Co. 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 flask import Flask
from flask.ext import restful
from flask.ext.restful import reqparse
from flask.ext.jsonpify import jsonify
#from flask import request
from flask import redirect, request, current_app
"""Define all the RestfulAPI entry points."""
import logging
import requests
import random
import simplejson as json
import time
import urllib
import json
import requests
import random
import time
from flask.ext import restful
from flask import redirect, request, current_app
app = Flask(__name__)
from compass_metrics.api import app
from compass_metrics.utils import flags
from compass_metrics.utils import logsetting
from compass_metrics.utils import setting_wrapper as setting
api = restful.Api(app)
ROOT_URL = "http://metrics:8080"
#class MyEncoder1(json.JSONEncoder):
# def default(self, obj):
ROOT_URL = setting.ROOT_URL
class HelloInfo(restful.Resource):
def get(self):
#return {'info': 'Select host, hostgroup, topology, service from here'}
r = requests.get(ROOT_URL+"/api/v1/tagnames")
# return {'info': 'Select host, hostgroup, topology, service from here'}
url = ROOT_URL + '/api/v1/tagnames'
logging.debug('get url %s', url)
r = requests.get(url)
logging.debug('%s response: %s', request.path, r)
return r.json()
api.add_resource(HelloInfo, '/')
class Proxy(restful.Resource):
def get(self, url):
# Get URL Parameters for host and metric info
urlParam = request.url.split('proxy/', 9);
urlParam = request.url.split('proxy/', 9)
my_url = urlParam[1]
#return my_url
r = requests.get(my_url, stream = True)
#return r.text
# return my_url
r = requests.get(my_url, stream=True)
# return r.text
return json.loads(r.text)
#return Response(stream_with_context(r.iter_content()), content_type = r.headers['content-type'])
# return Response(stream_with_context(r.iter_content()), content_type = r.headers['content-type'])
api.add_resource(Proxy, '/proxy/<path:url>', defaults={'url': '/HelloWorld'})
@ -54,16 +71,20 @@ class Hosts(restful.Resource):
# weed out just the hosts if possible
return r.json()
api.add_resource(Hosts, '/hosts')
class Metrics(restful.Resource):
def get(self):
# This is a comprehensive list of metrics not per host due to limitations
r = requests.get(ROOT_URL +"/api/v1/metricnames")
return r.json()
api.add_resource(Metrics, '/metrics')
class TsGenerateAlarmData:
def __init__(self):
self.params = " "
@ -117,7 +138,28 @@ class TsQueryBuilder:
self.statusStr = "failure"
buildStr = '{ "metrics":['
repeatStr = '{"tags":{ "host":["HOSTNAME"]},"name":"METRIC","group_by":[{"name":"tag", "tags":["host"]}],"aggregators":[{"name":"sum", "align_sampling": false, "sampling":{ "value": "2", "unit": "minutes"}}]}'
repeatStr = json.dumps({
"tags": {
"host": ["HOSTNAME"]
},
"name": "METRIC",
"group_by": [
{
"name":"tag",
"tags": ["host"]
}
],
"aggregators": [
{
"name": "sum",
"align_sampling": False,
"sampling": {
"value": "2",
"unit": "minutes"
}
}
]
})
finStr = '],"start_relative": { "value": "17", "unit": "minutes" }}'
repeatStr = repeatStr.replace("METRIC", metric)
@ -136,6 +178,7 @@ class TsQueryBuilder:
self.statusStr = "success"
self.resp_dict = r.json()
class HostMetric(restful.Resource):
def get(self, hostname, metricname):
# Get URL Parameters for host and metric info
@ -169,12 +212,14 @@ class HostMetric(restful.Resource):
#return valStr
return json.loads(valStr)
api.add_resource(
HostMetric,
'/host/<hostname>/metric/<metricname>',
defaults={'hostname': '', 'metricname': ''}
)
class HostGroupMetric(restful.Resource):
def get(self, hostgroup, metricname):
# Get URL Parameters for host and metric info
@ -226,6 +271,7 @@ class HostGroupMetric(restful.Resource):
#return valStr
return json.loads(valStr)
api.add_resource(
HostGroupMetric,
'/hostgroup/<hostgroup>/metric/<metricname>',
@ -262,12 +308,14 @@ class RsHostMetric(restful.Resource):
return json.loads(valStr)
api.add_resource(
RsHostMetric,
'/rshost/<hostname>/metric/<metricname>',
defaults={'hostname': '', 'metricname': ''}
)
class RsHostGroupMetric(restful.Resource):
def get(self, hostgroup, metricname):
# Get URL Parameters for host and metric info
@ -336,15 +384,19 @@ class Alarms(restful.Resource):
#return r
return json.loads("{"+r+"}")
api.add_resource(Alarms, '/alarms')
class Services(restful.Resource):
def get(self):
r = requests.get(ROOT_URL +"/api/v1/services")
return r.json()
api.add_resource(Services, '/services')
class Topology(restful.Resource):
def get(self):
#r = requests.get(ROOT_URL +"/api/v1/topology")
@ -358,10 +410,16 @@ class Topology(restful.Resource):
api.add_resource(Topology, '/topologies/1')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, threaded=True)
#app.run(debug=True)
def init():
pass
if __name__ == '__main__':
flags.init()
logsetting.init()
init()
app.run(host='0.0.0.0', debug=True, threaded=True)
"""

View File

@ -1,6 +0,0 @@
from os import sys, path
sys.path.append(path.dirname(path.abspath(__file__)))
import anapi_v2
application = anapi_v2.app

View File

View File

@ -0,0 +1,92 @@
# Copyright 2014 Huawei Technologies Co. 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.
"""Module to load flags.
.. moduleauthor:: Xiaodong Wang <xiaodongwang@huawei.com>
"""
import sys
from optparse import OptionParser
class Flags(object):
"""Class to store flags."""
PARSER = OptionParser()
PARSED_OPTIONS = None
@classmethod
def parse_args(cls):
"""parse args."""
(options, argv) = Flags.PARSER.parse_args()
sys.argv = [sys.argv[0]] + argv
Flags.PARSED_OPTIONS = options
def __getattr__(self, name):
if Flags.PARSED_OPTIONS and hasattr(Flags.PARSED_OPTIONS, name):
return getattr(Flags.PARSED_OPTIONS, name)
for option in Flags.PARSER.option_list:
if option.dest == name:
return option.default
raise AttributeError('Option instance has no attribute %s' % name)
def __setattr__(self, name, value):
if Flags.PARSED_OPTIONS and hasattr(Flags.PARSED_OPTIONS, name):
setattr(Flags.PARSED_OPTIONS, name, value)
return
for option in Flags.PARSER.option_list:
if option.dest == name:
option.default = value
return
object.__setattr__(self, name, value)
OPTIONS = Flags()
def init():
"""Init flag parsing.
"""
OPTIONS.parse_args()
def add(flagname, **kwargs):
"""Add a flag name and its setting.
:param flagname: flag name declared in cmd as --<flagname>=...
:type flagname: str
"""
Flags.PARSER.add_option('--%s' % flagname,
dest=flagname, **kwargs)
def add_bool(flagname, default=True, **kwargs):
"""Add a bool flag name and its setting.
:param flagname: flag name declared in cmd as --[no]<flagname>.
:type flagname: str
:param default: default value
:type default: bool
"""
Flags.PARSER.add_option('--%s' % flagname,
dest=flagname, default=default,
action="store_true", **kwargs)
Flags.PARSER.add_option('--no%s' % flagname,
dest=flagname,
action="store_false", **kwargs)

View File

@ -0,0 +1,94 @@
# Copyright 2014 Huawei Technologies Co. 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.
"""Module to setup logging configuration.
.. moduleauthor:: Xiaodong Wang <xiaodongwang@huawei.com>
"""
import logging
import logging.handlers
import os
import os.path
import sys
from compass_metrics.utils import flags
from compass_metrics.utils import setting_wrapper as setting
flags.add('loglevel',
help='logging level', default=setting.DEFAULT_LOGLEVEL)
flags.add('logdir',
help='logging directory', default=setting.DEFAULT_LOGDIR)
flags.add('logfile',
help='logging filename', default=None)
flags.add('log_interval', type='int',
help='log interval', default=setting.DEFAULT_LOGINTERVAL)
flags.add('log_interval_unit',
help='log interval unit', default=setting.DEFAULT_LOGINTERVAL_UNIT)
flags.add('log_format',
help='log format', default=setting.DEFAULT_LOGFORMAT)
# mapping str setting in flag --loglevel to logging level.
LOGLEVEL_MAPPING = {
'finest': logging.DEBUG - 2, # more detailed log.
'fine': logging.DEBUG - 1, # detailed log.
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
# disable logging when logsetting.init not called
logging.getLogger().setLevel(logging.CRITICAL)
def init():
"""Init loggsetting. It should be called after flags.init."""
loglevel = flags.OPTIONS.loglevel.lower()
logdir = flags.OPTIONS.logdir
logfile = flags.OPTIONS.logfile
logger = logging.getLogger()
if logger.handlers:
for handler in logger.handlers:
logger.removeHandler(handler)
if logdir:
if not logfile:
logfile = os.path.basename(sys.argv[0])
handler = logging.handlers.TimedRotatingFileHandler(
os.path.join(logdir, logfile),
when=flags.OPTIONS.log_interval_unit,
interval=flags.OPTIONS.log_interval)
else:
if not logfile:
handler = logging.StreamHandler(sys.stderr)
else:
handler = logging.handlers.TimedRotatingFileHandler(
logfile,
when=flags.OPTIONS.log_interval_unit,
interval=flags.OPTIONS.log_interval)
if loglevel in LOGLEVEL_MAPPING:
logger.setLevel(LOGLEVEL_MAPPING[loglevel])
handler.setLevel(LOGLEVEL_MAPPING[loglevel])
formatter = logging.Formatter(
flags.OPTIONS.log_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

View File

@ -0,0 +1,52 @@
# Copyright 2014 Huawei Technologies Co. 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.
"""comapss setting wrapper.
.. moduleauthor:: Xiaodong Wang ,xiaodongwang@huawei.com>
"""
import datetime
import logging
import os
import os.path
# default setting
CONFIG_DIR = '/etc/compass_monit'
DEFAULT_LOGLEVEL = 'debug'
DEFAULT_LOGDIR = '/tmp'
DEFAULT_LOGINTERVAL = 1
DEFAULT_LOGINTERVAL_UNIT = 'h'
DEFAULT_LOGFORMAT = (
'%(asctime)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s')
WEB_LOGFILE = 'compass_monit.log'
ROOT_URL = 'http://localhost:8080'
if (
'COMPASS_METRICS_IGNORE_SETTING' in os.environ and
os.environ['COMPASS_METRICS_IGNORE_SETTING']
):
pass
else:
if 'COMPASS_METRICS_SETTING' in os.environ:
SETTING = os.environ['COMPASS_METRICS_SETTING']
else:
SETTING = '/etc/compass_monit/setting'
try:
logging.info('load setting from %s', SETTING)
execfile(SETTING, globals(), locals())
except Exception as error:
logging.exception(error)
raise error

18
conf/compass-monit.conf Normal file
View File

@ -0,0 +1,18 @@
# Apache config for monitor server
#
# Specify python path if you use virtualenv
WSGIDaemonProcess compass-monit threads=4 display-name=%{GROUP}
WSGIProcessGroup compass-monit
WSGIScriptAlias /monit/api/v1 /var/www/compass_monit/compass_monit.wsgi
WSGISocketPrefix /var/run/wsgi
<VirtualHost *:80>
DocumentRoot /var/www/compass_monit
<Directory "/var/www/compass_monit">
Options Indexes FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

25
conf/compass_monit.wsgi Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python
import site
import sys
import os
import os.path
activate_this='$PythonHome/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
site.addsitedir('$PythonHome/lib/python2.6/site-packages')
sys.path.append('$PythonHome')
os.environ['PYTHON_EGG_CACHE'] = '/tmp/.egg'
from compass_metrics.utils import flags
from compass_metrics.utils import logsetting
from compass_metrics.utils import setting_wrapper as setting
flags.init()
flags.OPTIONS.logfile = setting.WEB_LOGFILE
logsetting.init()
from compass_metrics.api import api as compass_metrics_api
compass_metrics_api.init()
application = compass_metrics_api.app

View File

@ -1,18 +0,0 @@
# Apache config for monitor server
#
# Specify python path if you use virtualenv
WSGIDaemonProcess api threads=4 display-name=%{GROUP}
WSGIProcessGroup api
WSGIScriptAlias /monit/api/v1 /var/www/restApi/monitor.wsgi
WSGISocketPrefix /var/run/wsgi
<VirtualHost *:80>
DocumentRoot /var/www/restApi
<Directory "/var/www/restApi">
Options Indexes FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

4
conf/setting Normal file
View File

@ -0,0 +1,4 @@
DEFAULT_LOGLEVEL = 'debug'
DEFAULT_LOGDIR = '/var/log/compass_monit'
WEB_LOGFILE = 'compass_monit.log'
ROOT_URL = 'http://$ipaddr:8080'

299
ez_setup.py Normal file
View File

@ -0,0 +1,299 @@
#!/usr/bin/python
#
# Copyright 2014 Huawei Technologies Co. 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.
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import optparse
import os
import shutil
import subprocess
import sys
import tarfile
import tempfile
from distutils import log
try:
from site import USER_SITE
except ImportError:
USER_SITE = None
DEFAULT_VERSION = "0.9.6"
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
def _python_cmd(*args):
"""run cmd in python."""
args = (sys.executable,) + args
return subprocess.call(args) == 0
def _install(tarball, install_args=()):
"""install tarball."""
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# installing
log.warn('Installing Setuptools')
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
# exitcode will be 2
return 2
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir):
"""build egg."""
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# building an egg
log.warn('Building a Setuptools egg in %s', to_dir)
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
raise IOError('Could not build the egg.')
def _do_download(version, download_base, to_dir, download_delay):
"""download package."""
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
% (version, sys.version_info[0], sys.version_info[1]))
if not os.path.exists(egg):
tarball = download_setuptools(version, download_base,
to_dir, download_delay)
_build_egg(egg, tarball, to_dir)
sys.path.insert(0, egg)
import setuptools
setuptools.bootstrap_install_from = egg
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, download_delay=15):
"""use setuptools to do the setup."""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
was_imported = 'pkg_resources' in sys.modules or \
'setuptools' in sys.modules
try:
import pkg_resources
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
pkg_resources.require("setuptools>=" + version)
return
except pkg_resources.DistributionNotFound:
return _do_download(version, download_base, to_dir,
download_delay)
except pkg_resources.VersionConflict:
error = sys.exc_info()[1]
if was_imported:
sys.stderr.writelines([
"The required version of setuptools (>=%s) is not available,",
"and can't be installed while this script is running. Please",
"install a more recent version first, using",
"'easy_install -U setuptools'.",
"",
"(Currently using %r)" % (version, error.args[0]),
"",
])
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return _do_download(version, download_base, to_dir,
download_delay)
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download
attempt.
"""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
tgz_name = "setuptools-%s.tar.gz" % version
url = download_base + tgz_name
saveto = os.path.join(to_dir, tgz_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
log.warn("Downloading %s", url)
src = urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = src.read()
dst = open(saveto, "wb")
dst.write(data)
finally:
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def _extractall(self, path=".", members=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
list returned by getmembers().
"""
import copy
import operator
from tarfile import ExtractError
directories = []
if members is None:
members = self
for tarinfo in members:
if tarinfo.isdir():
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
if sys.version_info < (2, 4):
def sorter(dir1, dir2):
"""sort dir"""
return cmp(dir1.name, dir2.name)
directories.sort(sorter)
directories.reverse()
else:
directories.sort(key=operator.attrgetter('name'), reverse=True)
# Set correct owner, mtime and filemode on directories.
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
self.chown(tarinfo, dirpath)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError:
error = sys.exc_info()[1]
if self.errorlevel > 1:
raise
else:
self._dbg(1, "tarfile: %s" % error)
def _build_install_args(options):
"""Build install args
Build the arguments to 'python setup.py install' on the setuptools package
"""
install_args = []
if options.user_install:
if sys.version_info < (2, 6):
log.warn("--user requires Python 2.6 or later")
raise SystemExit(1)
install_args.append('--user')
return install_args
def _parse_args():
"""Parse the command line for options."""
parser = optparse.OptionParser()
parser.add_option(
'--user', dest='user_install', action='store_true', default=False,
help='install in user site package (requires Python 2.6 or later)')
parser.add_option(
'--download-base', dest='download_base', metavar="URL",
default=DEFAULT_URL,
help='alternative URL from where to download the setuptools package')
options, _ = parser.parse_args()
# positional arguments are ignored
return options
def main(version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall."""
options = _parse_args()
tarball = download_setuptools(download_base=options.download_base)
return _install(tarball, _build_install_args(options))
if __name__ == '__main__':
sys.exit(main())

160
install/install.sh Executable file
View File

@ -0,0 +1,160 @@
#!/bin/bash
#
### Log the script all outputs locally
exec > >(sudo tee install.log)
exec 2>&1
### Creat a lock to avoid running multiple instances of script.
LOCKFILE="/tmp/`basename $0`"
LOCKFD=99
# PRIVATE
_lock() { flock -$1 $LOCKFD; }
_no_more_locking() { _lock u; _lock xn && rm -f $LOCKFILE; }
_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }
# ON START
_prepare_locking
# PUBLIC
exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail
exlock_now || exit 1
### BEGIN OF SCRIPT ###
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
### Trap any error code with related filename and line.
errtrap()
{
FILE=${BASH_SOURCE[1]:-$BASH_SOURCE[0]}
echo "[FILE: "$(basename $FILE)", LINE: $1] Error: Command or function exited with status $2"
}
if [[ "$-" == *x* ]]; then
trap 'errtrap $LINENO $?' ERR
fi
# Install figlet
sudo yum -y install figlet >& /dev/null
if [[ "$?" != "0" ]]; then
echo "failed to install figlet"
exit 1
else
echo "figlet is installed"
fi
figlet -ctf slant Compass Metrics Installer
while [ $1 ]; do
flags=$1
param=${flags/'--'/''}
var=$(echo $param | cut -d"=" -f1)
val=$(echo $param | cut -d"=" -f2)
export $var=$val
shift
done
# Load variables
loadvars()
{
varname=${1,,}
eval var=\$$(echo $1)
if [[ -z $var ]]; then
echo -e "\x1b[32mPlease enter the $varname (Example: $2):\x1b[37m"
while read input
do
if [ "$input" == "" ]; then
echo "Default $varname '$2' chosen"
export $(echo $1)="$2"
break
else
echo "You have entered $input"
export $(echo $1)="$input"
break
fi
done
fi
}
loadvars NIC "eth0"
sudo ifconfig $NIC
if [ $? -ne 0 ]; then
echo "There is no nic '$NIC' yet"
exit 1
fi
sudo ifconfig $NIC | grep 'inet addr:' >& /dev/null
if [ $? -ne 0 ]; then
echo "There is not any IP address assigned to the NIC '$NIC' yet, please assign an IP address first."
exit 1
fi
export ipaddr=$(ifconfig $NIC | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}')
export SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
export COMPASS_METTRICS_DIR=${SCRIPT_DIR}/..
echo 'Installing Required packages for Compass monit...'
sudo yum clean all
sudo yum update -y --skip-broken
sudo yum install -y python python-devel git wget syslinux mod_wsgi httpd yum-utils python-virtualenv
if [[ "$?" != "0" ]]; then
echo "failed to install yum dependency"
exit 1
fi
sudo easy_install --upgrade pip
if [[ "$?" != "0" ]]; then
echo "failed to install easy install"
exit 1
fi
sudo pip install virtualenvwrapper
sudo chkconfig httpd on
sudo mkdir -p /var/www/compass_monit
sudo mkdir -p /var/log/compass_monit
sudo mkdir -p /etc/compass_monit
sudo
sudo chmod -R 777 /var/log/compass_monit
cp -rf ${COMPASS_METTRICS_DIR}/conf/compass-monit.conf /etc/httpd/conf.d/compass-monit.conf
sudo cp -rf ${COMPASS_METTRICS_DIR}/conf/compass_monit.wsgi /var/www/compass_monit/compass_monit.wsgi
sudo cp -rf ${COMPASS_METTRICS_DIR}/conf/setting /etc/compass_monit/setting
cd ${COMPASS_METTRICS_DIR}
source `which virtualenvwrapper.sh`
if ! lsvirtualenv |grep compass-monit>/dev/null; then
mkvirtualenv compass-monit
fi
workon compass-monit
python setup.py install
if [[ "$?" != "0" ]]; then
echo "failed to install compass-monit package"
deactivate
exit 1
else
echo "compass-monit package is installed in virtualenv under current dir"
fi
sudo sed -e 's|$PythonHome|'$VIRTUAL_ENV'|' -i /var/www/compass_monit/compass_monit.wsgi
sudo sed -i "s/\$ipaddr/$ipaddr/g" /etc/compass_monit/setting
deactivate
sudo service httpd restart
sleep 10
sudo service httpd status
if [[ "$?" != "0" ]]; then
echo "httpd is not started"
exit 1
else
echo "httpd has already started"
fi
figlet -ctf slant Installation Complete!
echo -e "It takes\x1b[32m $SECONDS \x1b[0mseconds during the installation."

10
requirements.txt Normal file
View File

@ -0,0 +1,10 @@
argparse
flask
flask-script
flask-restful
flask-sqlalchemy
flask-login
simplejson
requests
flask-wtf
itsdangerous

4
setup.cfg Normal file
View File

@ -0,0 +1,4 @@
[egg_info]
tag_build=
tag_date=0

103
setup.py Normal file
View File

@ -0,0 +1,103 @@
#!/usr/bin/python
#
# Copyright 2014 Huawei Technologies Co. 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.
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
"""setup script."""
try:
from setuptools import find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools.command.test import test as TestCommand
from setuptools import setup
import os
import sys
class Tox(TestCommand):
"""Tox to do the setup."""
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import tox
errno = tox.cmdline(self.test_args)
sys.exit(errno)
INSTALL_REQUIRES_DIR = os.path.join(
os.path.dirname(__file__), 'requirements.txt')
with open(INSTALL_REQUIRES_DIR, 'r') as requires_file:
REQUIREMENTS = [line.strip() for line in requires_file if line != '\n']
setup(
name='compass_metrics',
version='0.1.0',
# general info
description='Monitoring for Open Deployment System',
long_description='Monitoring for Open Deployment System',
author='Compass Dev Group, Huawei Cloud',
author_email='shuo.yang@huawei.com',
url='https://github.com/stackforge/compass-monit',
download_url='',
# dependency
install_requires=REQUIREMENTS,
packages=find_packages(exclude=['compass_metrics.tests']),
include_package_data=True,
#TODO login UI will be replaced by compass_metrics's own templates later
package_data={'compass_metrics': ['templates/*.jinja', 'static/js/*.js',
'static/css/*.css', 'static/img/*.png']},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
],
# test,
tests_require=['tox'],
cmdclass={'test': Tox},
)

9
test-requirements.txt Normal file
View File

@ -0,0 +1,9 @@
discover
mock
unittest2
testtools>=0.9.32
testrepository>=0.0.17
mimeparse
coverage>=3.6
hacking>=0.8.0,<0.9
virtualenv

73
tools/install_venv.py Normal file
View File

@ -0,0 +1,73 @@
# 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.
#
# 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.
# Shamelessly copied from horizon project and renamed to Compass
import os
import sys
import install_venv_common as install_venv # noqa
def print_help(venv, root):
help = """
Compass development environment setup is complete.
Compass development uses virtualenv to track and manage Python
dependencies while in development and testing.
To activate the Compass virtualenv for the extent of your current shell
session you can run:
$ source %s/bin/activate
Or, if you prefer, you can run commands in the virtualenv on a case by case
basis by running:
$ %s/tools/with_venv.sh <your command>
Also, make test will automatically use the virtualenv.
"""
print(help % (venv, 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])
project = 'Compass'
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()
print_help(venv, root)
if __name__ == '__main__':
main(sys.argv)

View File

@ -0,0 +1,172 @@
# 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, '-r', self.test_requirements)
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)
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 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()

7
tools/with_venv.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)}
VENV_PATH=${VENV_PATH:-${TOOLS_PATH}}
VENV_DIR=${VENV_NAME:-/../.venv}
TOOLS=${TOOLS_PATH}
VENV=${VENV:-${VENV_PATH}/${VENV_DIR}}
source ${VENV}/bin/activate && "$@"

38
tox.ini Normal file
View File

@ -0,0 +1,38 @@
[tox]
minversion = 1.6
skipsdist = True
envlist = py26,py27,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_ALL=C
COMPASS_IGNORE_SETTING=1
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
install_command = pip install -U {opts} {packages}
usedevelop = True
commands = python setup.py testr --slowest --testr-args='{posargs}'
distribute = false
[testenv:pep8]
commands = flake8
distribute = false
[testenv:venv]
commands = {posargs}
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'
[tox:jenkins]
downloadcache = ~/cache/pip
[flake8]
ignore = H302,H233,H803,F401
show-source = true
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools,build