Merge "API extension for fpinging instances"
This commit is contained in:
commit
48967d3822
@ -47,6 +47,8 @@
|
||||
"compute_extension:floating_ip_dns": "",
|
||||
"compute_extension:floating_ip_pools": "",
|
||||
"compute_extension:floating_ips": "",
|
||||
"compute_extension:fping": "",
|
||||
"compute_extension:fping:all_tenants": "rule:admin_api",
|
||||
"compute_extension:hosts": "rule:admin_api",
|
||||
"compute_extension:hypervisors": "rule:admin_api",
|
||||
"compute_extension:instance_usage_audit_log": "rule:admin_api",
|
||||
|
162
nova/api/openstack/compute/contrib/fping.py
Normal file
162
nova/api/openstack/compute/contrib/fping.py
Normal file
@ -0,0 +1,162 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Grid Dynamics
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 itertools
|
||||
import os
|
||||
import time
|
||||
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import extensions
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
authorize = extensions.extension_authorizer('compute', 'fping')
|
||||
authorize_all_tenants = extensions.extension_authorizer(
|
||||
'compute', 'fping:all_tenants')
|
||||
fping_opts = [
|
||||
cfg.StrOpt("fping_path",
|
||||
default="/usr/sbin/fping",
|
||||
help="Full path to fping."),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(fping_opts)
|
||||
|
||||
|
||||
class FpingController(object):
|
||||
|
||||
def __init__(self, network_api=None):
|
||||
self.compute_api = compute.API()
|
||||
self.last_call = {}
|
||||
|
||||
def check_fping(self):
|
||||
if not os.access(FLAGS.fping_path, os.X_OK):
|
||||
raise exc.HTTPServiceUnavailable(
|
||||
explanation=_("fping utility is not found."))
|
||||
|
||||
@staticmethod
|
||||
def fping(ips):
|
||||
fping_ret = utils.execute(FLAGS.fping_path, *ips,
|
||||
check_exit_code=False)
|
||||
if not fping_ret:
|
||||
return set()
|
||||
alive_ips = set()
|
||||
for line in fping_ret[0].split("\n"):
|
||||
ip = line.split(" ", 1)[0]
|
||||
if "alive" in line:
|
||||
alive_ips.add(ip)
|
||||
return alive_ips
|
||||
|
||||
@staticmethod
|
||||
def _get_instance_ips(context, instance):
|
||||
ret = []
|
||||
for network in common.get_networks_for_instance(
|
||||
context, instance).values():
|
||||
all_ips = itertools.chain(network["ips"], network["floating_ips"])
|
||||
ret += [ip["address"] for ip in all_ips]
|
||||
return ret
|
||||
|
||||
def index(self, req):
|
||||
context = req.environ["nova.context"]
|
||||
search_opts = dict(deleted=False)
|
||||
if "all_tenants" in req.GET:
|
||||
authorize_all_tenants(context)
|
||||
else:
|
||||
authorize(context)
|
||||
if context.project_id:
|
||||
search_opts["project_id"] = context.project_id
|
||||
else:
|
||||
search_opts["user_id"] = context.user_id
|
||||
self.check_fping()
|
||||
include = req.GET.get("include", None)
|
||||
if include:
|
||||
include = set(include.split(","))
|
||||
exclude = set()
|
||||
else:
|
||||
include = None
|
||||
exclude = req.GET.get("exclude", None)
|
||||
if exclude:
|
||||
exclude = set(exclude.split(","))
|
||||
else:
|
||||
exclude = set()
|
||||
|
||||
instance_list = self.compute_api.get_all(
|
||||
context, search_opts=search_opts)
|
||||
ip_list = []
|
||||
instance_ips = {}
|
||||
instance_projects = {}
|
||||
|
||||
for instance in instance_list:
|
||||
uuid = instance["uuid"]
|
||||
if uuid in exclude or (include is not None and
|
||||
uuid not in include):
|
||||
continue
|
||||
ips = [str(ip) for ip in self._get_instance_ips(context, instance)]
|
||||
instance_ips[uuid] = ips
|
||||
instance_projects[uuid] = instance["project_id"]
|
||||
ip_list += ips
|
||||
alive_ips = self.fping(ip_list)
|
||||
res = []
|
||||
for instance_uuid, ips in instance_ips.iteritems():
|
||||
res.append({
|
||||
"id": instance_uuid,
|
||||
"project_id": instance_projects[instance_uuid],
|
||||
"alive": bool(set(ips) & alive_ips),
|
||||
})
|
||||
return {"servers": res}
|
||||
|
||||
def show(self, req, id):
|
||||
try:
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context)
|
||||
self.check_fping()
|
||||
instance = self.compute_api.get(context, id)
|
||||
ips = [str(ip) for ip in self._get_instance_ips(context, instance)]
|
||||
alive_ips = self.fping(ips)
|
||||
return {
|
||||
"server": {
|
||||
"id": instance["uuid"],
|
||||
"project_id": instance["project_id"],
|
||||
"alive": bool(set(ips) & alive_ips),
|
||||
}
|
||||
}
|
||||
except exception.NotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
|
||||
|
||||
class Fping(extensions.ExtensionDescriptor):
|
||||
"""Fping Management Extension."""
|
||||
|
||||
name = "Fping"
|
||||
alias = "os-fping"
|
||||
namespace = "http://docs.openstack.org/compute/ext/fping/api/v1.1"
|
||||
updated = "2012-07-06T00:00:00+00:00"
|
||||
|
||||
def get_resources(self):
|
||||
res = extensions.ResourceExtension(
|
||||
"os-fping",
|
||||
FpingController())
|
||||
return [res]
|
@ -212,6 +212,7 @@ DEFAULT_LIMITS = [
|
||||
Limit("PUT", "*", ".*", 10, PER_MINUTE),
|
||||
Limit("GET", "*changes-since*", ".*changes-since.*", 3, PER_MINUTE),
|
||||
Limit("DELETE", "*", ".*", 100, PER_MINUTE),
|
||||
Limit("GET", "*/os-fping", "^/os-fping", 12, PER_HOUR),
|
||||
]
|
||||
|
||||
|
||||
|
94
nova/tests/api/openstack/compute/contrib/test_fping.py
Normal file
94
nova/tests/api/openstack/compute/contrib/test_fping.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2011 Grid Dynamics
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 nova.api.openstack.compute.contrib import fping
|
||||
from nova.api.openstack import extensions
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
import nova.utils
|
||||
|
||||
|
||||
FAKE_UUID = fakes.FAKE_UUID
|
||||
|
||||
|
||||
def execute(*cmd, **args):
|
||||
return "".join(["%s is alive" % ip for ip in cmd[1:]])
|
||||
|
||||
|
||||
class FpingTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FpingTest, self).setUp()
|
||||
self.flags(verbose=True, use_ipv6=False)
|
||||
return_server = fakes.fake_instance_get()
|
||||
return_servers = fakes.fake_instance_get_all_by_filters()
|
||||
self.stubs.Set(nova.db, "instance_get_all_by_filters",
|
||||
return_servers)
|
||||
self.stubs.Set(nova.db, "instance_get_by_uuid",
|
||||
return_server)
|
||||
self.stubs.Set(nova.db, "instance_get_all_by_project",
|
||||
return_servers)
|
||||
self.stubs.Set(nova.utils, "execute",
|
||||
execute)
|
||||
self.stubs.Set(fping.FpingController, "check_fping",
|
||||
lambda self: None)
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.controller = fping.FpingController(self.ext_mgr)
|
||||
|
||||
def test_fping_index(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping")
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertTrue("servers" in res_dict)
|
||||
for srv in res_dict["servers"]:
|
||||
for key in "project_id", "id", "alive":
|
||||
self.assertTrue(key in srv)
|
||||
|
||||
def test_fping_index_policy(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?all_tenants=1")
|
||||
self.assertRaises(exception.NotAuthorized, self.controller.index, req)
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?all_tenants=1")
|
||||
req.environ["nova.context"].is_admin = True
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertTrue("servers" in res_dict)
|
||||
|
||||
def test_fping_index_include(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping")
|
||||
res_dict = self.controller.index(req)
|
||||
ids = [srv["id"] for srv in res_dict["servers"]]
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?include=%s" % ids[0])
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertEqual(len(res_dict["servers"]), 1)
|
||||
self.assertEqual(res_dict["servers"][0]["id"], ids[0])
|
||||
|
||||
def test_fping_index_exclude(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping")
|
||||
res_dict = self.controller.index(req)
|
||||
ids = [srv["id"] for srv in res_dict["servers"]]
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?exclude=%s" %
|
||||
",".join(ids[1:]))
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertEqual(len(res_dict["servers"]), 1)
|
||||
self.assertEqual(res_dict["servers"][0]["id"], ids[0])
|
||||
|
||||
def test_fping_show(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping/%s" % FAKE_UUID)
|
||||
res_dict = self.controller.show(req, FAKE_UUID)
|
||||
self.assertTrue("server" in res_dict)
|
||||
srv = res_dict["server"]
|
||||
for key in "project_id", "id", "alive":
|
||||
self.assertTrue(key in srv)
|
@ -224,6 +224,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/services/api/v2",
|
||||
"updated": "%(timestamp)s"
|
||||
},
|
||||
{
|
||||
"alias": "os-fping",
|
||||
"description": "%(text)s",
|
||||
"links": [],
|
||||
"name": "Fping",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/fping/api/v1.1",
|
||||
"updated": "%(timestamp)s"
|
||||
},
|
||||
{
|
||||
"alias": "os-hypervisors",
|
||||
"description": "%(text)s",
|
||||
|
@ -84,6 +84,9 @@
|
||||
<extension alias="os-services" name="Services" namespace="http://docs.openstack.org/compute/ext/services/api/v2" updated="%(timestamp)s">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-fping" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/fping/api/v1.1" name="Fping">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-hypervisors" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/hypervisors/api/v1.1" name="Hypervisors">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
|
@ -66,6 +66,19 @@
|
||||
],
|
||||
"regex": ".*changes-since.*",
|
||||
"uri": "*changes-since*"
|
||||
},
|
||||
{
|
||||
"limit": [
|
||||
{
|
||||
"next-available": "%(timestamp)s",
|
||||
"remaining": 12,
|
||||
"unit": "HOUR",
|
||||
"value": 12,
|
||||
"verb": "GET"
|
||||
}
|
||||
],
|
||||
"regex": "^/os-fping",
|
||||
"uri": "*/os-fping"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -12,6 +12,9 @@
|
||||
<rate regex=".*changes-since.*" uri="*changes-since*">
|
||||
<limit next-available="%(timestamp)s" unit="MINUTE" verb="GET" remaining="3" value="3"/>
|
||||
</rate>
|
||||
<rate regex="^/os-fping" uri="*/os-fping">
|
||||
<limit next-available="%(timestamp)s" unit="HOUR" verb="GET" remaining="12" value="12"/>
|
||||
</rate>
|
||||
</rates>
|
||||
<absolute>
|
||||
<limit name="maxServerMeta" value="128"/>
|
||||
|
@ -71,6 +71,19 @@
|
||||
],
|
||||
"regex": ".*changes-since.*",
|
||||
"uri": "*changes-since*"
|
||||
},
|
||||
{
|
||||
"limit": [
|
||||
{
|
||||
"next-available": "%(timestamp)s",
|
||||
"remaining": 12,
|
||||
"unit": "HOUR",
|
||||
"value": 12,
|
||||
"verb": "GET"
|
||||
}
|
||||
],
|
||||
"regex": "^/os-fping",
|
||||
"uri": "*/os-fping"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -12,6 +12,9 @@
|
||||
<rate regex=".*changes-since.*" uri="*changes-since*">
|
||||
<limit next-available="%(timestamp)s" unit="MINUTE" verb="GET" remaining="3" value="3"/>
|
||||
</rate>
|
||||
<rate regex="^/os-fping" uri="*/os-fping">
|
||||
<limit next-available="%(timestamp)s" unit="HOUR" verb="GET" remaining="12" value="12"/>
|
||||
</rate>
|
||||
</rates>
|
||||
<absolute>
|
||||
<limit name="maxServerMeta" value="128"/>
|
||||
|
@ -105,6 +105,8 @@
|
||||
"compute_extension:floating_ip_dns": "",
|
||||
"compute_extension:floating_ip_pools": "",
|
||||
"compute_extension:floating_ips": "",
|
||||
"compute_extension:fping": "",
|
||||
"compute_extension:fping:all_tenants": "is_admin:True",
|
||||
"compute_extension:hosts": "",
|
||||
"compute_extension:hypervisors": "",
|
||||
"compute_extension:instance_usage_audit_log": "",
|
||||
|
Loading…
x
Reference in New Issue
Block a user