Port fping extension to work in v2.1/v3 framework
Ports v2 fping extension and adapts it to the v2.1/v3 API framework. API behaviour is identical. - unittest code modified to share testing with both v2/v2.1 - Adds expected error decorators for API methods Partially implements blueprint v2-on-v3-api Change-Id: I8dc4ede46826bf6062097a3b4ba6be9a09a1bd12
This commit is contained in:
7
doc/v3/api_samples/os-fping/fping-get-details-resp.json
Normal file
7
doc/v3/api_samples/os-fping/fping-get-details-resp.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"server": {
|
||||
"alive": false,
|
||||
"id": "f5e6fd6d-c0a3-4f9e-aabf-d69196b6d11a",
|
||||
"project_id": "openstack"
|
||||
}
|
||||
}
|
9
doc/v3/api_samples/os-fping/fping-get-resp.json
Normal file
9
doc/v3/api_samples/os-fping/fping-get-resp.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"alive": false,
|
||||
"id": "1d1aea35-472b-40cf-9337-8eb68480aaa1",
|
||||
"project_id": "openstack"
|
||||
}
|
||||
]
|
||||
}
|
16
doc/v3/api_samples/os-fping/server-post-req.json
Normal file
16
doc/v3/api_samples/os-fping/server-post-req.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"name": "new-server-test",
|
||||
"imageRef": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"flavorRef": "http://openstack.example.com/openstack/flavors/1",
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"personality": [
|
||||
{
|
||||
"path": "/etc/banner.txt",
|
||||
"contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
16
doc/v3/api_samples/os-fping/server-post-resp.json
Normal file
16
doc/v3/api_samples/os-fping/server-post-resp.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"adminPass": "xrDLoBeMD28B",
|
||||
"id": "3f69b6bd-00a8-4636-96ee-650093624304",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v3/servers/3f69b6bd-00a8-4636-96ee-650093624304",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/servers/3f69b6bd-00a8-4636-96ee-650093624304",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -154,6 +154,9 @@
|
||||
"compute_extension:floating_ips_bulk": "rule:admin_api",
|
||||
"compute_extension:fping": "",
|
||||
"compute_extension:fping:all_tenants": "rule:admin_api",
|
||||
"compute_extension:v3:os-fping": "",
|
||||
"compute_extension:v3:os-fping:discoverable": "",
|
||||
"compute_extension:v3:os-fping:all_tenants": "rule:admin_api",
|
||||
"compute_extension:hide_server_addresses": "is_admin:False",
|
||||
"compute_extension:v3:os-hide-server-addresses": "is_admin:False",
|
||||
"compute_extension:v3:os-hide-server-addresses:discoverable": "",
|
||||
|
155
nova/api/openstack/compute/plugins/v3/fping.py
Normal file
155
nova/api/openstack/compute/plugins/v3/fping.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# Copyright 2011 Grid Dynamics
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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
|
||||
|
||||
from oslo.config import cfg
|
||||
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.i18n import _
|
||||
from nova import utils
|
||||
|
||||
ALIAS = "os-fping"
|
||||
|
||||
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
|
||||
authorize_all_tenants = extensions.extension_authorizer(
|
||||
'compute', 'v3:%s:all_tenants' % ALIAS)
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('fping_path', 'nova.api.openstack.compute.contrib.fping')
|
||||
|
||||
|
||||
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(CONF.fping_path, os.X_OK):
|
||||
raise exc.HTTPServiceUnavailable(
|
||||
explanation=_("fping utility is not found."))
|
||||
|
||||
@staticmethod
|
||||
def fping(ips):
|
||||
fping_ret = utils.execute(CONF.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
|
||||
|
||||
@extensions.expected_errors(503)
|
||||
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}
|
||||
|
||||
@extensions.expected_errors((404, 503))
|
||||
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 as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
|
||||
class Fping(extensions.V3APIExtensionBase):
|
||||
"""Fping Management Extension."""
|
||||
|
||||
name = "Fping"
|
||||
alias = ALIAS
|
||||
version = 1
|
||||
|
||||
def get_resources(self):
|
||||
res = extensions.ResourceExtension(ALIAS, FpingController())
|
||||
return [res]
|
||||
|
||||
def get_controller_extensions(self):
|
||||
return []
|
@@ -15,7 +15,7 @@
|
||||
# under the License.
|
||||
|
||||
from nova.api.openstack.compute.contrib import fping
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack.compute.plugins.v3 import fping as fping_v21
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
@@ -29,10 +29,11 @@ def execute(*cmd, **args):
|
||||
return "".join(["%s is alive" % ip for ip in cmd[1:]])
|
||||
|
||||
|
||||
class FpingTest(test.TestCase):
|
||||
class FpingTestV21(test.TestCase):
|
||||
controller_cls = fping_v21.FpingController
|
||||
|
||||
def setUp(self):
|
||||
super(FpingTest, self).setUp()
|
||||
super(FpingTestV21, self).setUp()
|
||||
self.flags(verbose=True, use_ipv6=False)
|
||||
return_server = fakes.fake_instance_get()
|
||||
return_servers = fakes.fake_instance_get_all_by_filters()
|
||||
@@ -42,14 +43,15 @@ class FpingTest(test.TestCase):
|
||||
return_server)
|
||||
self.stubs.Set(nova.utils, "execute",
|
||||
execute)
|
||||
self.stubs.Set(fping.FpingController, "check_fping",
|
||||
self.stubs.Set(self.controller_cls, "check_fping",
|
||||
lambda self: None)
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.controller = fping.FpingController(self.ext_mgr)
|
||||
self.controller = self.controller_cls()
|
||||
|
||||
def _get_url(self):
|
||||
return "/v3"
|
||||
|
||||
def test_fping_index(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping")
|
||||
req = fakes.HTTPRequest.blank(self._get_url() + "/os-fping")
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertIn("servers", res_dict)
|
||||
for srv in res_dict["servers"]:
|
||||
@@ -57,36 +59,48 @@ class FpingTest(test.TestCase):
|
||||
self.assertIn(key, srv)
|
||||
|
||||
def test_fping_index_policy(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?all_tenants=1")
|
||||
req = fakes.HTTPRequest.blank(self._get_url() +
|
||||
"os-fping?all_tenants=1")
|
||||
self.assertRaises(exception.Forbidden, self.controller.index, req)
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping?all_tenants=1")
|
||||
req = fakes.HTTPRequest.blank(self._get_url() +
|
||||
"/os-fping?all_tenants=1")
|
||||
req.environ["nova.context"].is_admin = True
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertIn("servers", res_dict)
|
||||
|
||||
def test_fping_index_include(self):
|
||||
req = fakes.HTTPRequest.blank("/v2/1234/os-fping")
|
||||
req = fakes.HTTPRequest.blank(self._get_url() + "/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])
|
||||
req = fakes.HTTPRequest.blank(self._get_url() +
|
||||
"/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")
|
||||
req = fakes.HTTPRequest.blank(self._get_url() + "/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" %
|
||||
req = fakes.HTTPRequest.blank(self._get_url() +
|
||||
"/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)
|
||||
req = fakes.HTTPRequest.blank(self._get_url() +
|
||||
"os-fping/%s" % FAKE_UUID)
|
||||
res_dict = self.controller.show(req, FAKE_UUID)
|
||||
self.assertIn("server", res_dict)
|
||||
srv = res_dict["server"]
|
||||
for key in "project_id", "id", "alive":
|
||||
self.assertIn(key, srv)
|
||||
|
||||
|
||||
class FpingTestV2(FpingTestV21):
|
||||
controller_cls = fping.FpingController
|
||||
|
||||
def _get_url(self):
|
||||
return "/v2/1234"
|
||||
|
@@ -215,6 +215,8 @@ policy_data = """
|
||||
"compute_extension:floating_ips_bulk": "",
|
||||
"compute_extension:fping": "",
|
||||
"compute_extension:fping:all_tenants": "is_admin:True",
|
||||
"compute_extension:v3:os-fping": "",
|
||||
"compute_extension:v3:os-fping:all_tenants": "is_admin:True",
|
||||
"compute_extension:hide_server_addresses": "",
|
||||
"compute_extension:v3:os-hide-server-addresses": "",
|
||||
"compute_extension:hosts": "rule:admin_api",
|
||||
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"server": {
|
||||
"alive": false,
|
||||
"id": "%(uuid)s",
|
||||
"project_id": "openstack"
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"alive": false,
|
||||
"id": "%(uuid)s",
|
||||
"project_id": "openstack"
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"name": "new-server-test",
|
||||
"imageRef": "%(host)s/openstack/images/%(image_id)s",
|
||||
"flavorRef": "%(host)s/openstack/flavors/1",
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"personality": [
|
||||
{
|
||||
"path": "/etc/banner.txt",
|
||||
"contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"adminPass": "%(password)s",
|
||||
"id": "%(id)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(host)s/v3/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(host)s/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
45
nova/tests/integrated/v3/test_fping.py
Normal file
45
nova/tests/integrated/v3/test_fping.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# 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.
|
||||
|
||||
|
||||
from nova.api.openstack.compute.plugins.v3 import fping
|
||||
from nova.tests.api.openstack.compute.contrib import test_fping
|
||||
from nova.tests.integrated.v3 import test_servers
|
||||
from nova import utils
|
||||
|
||||
|
||||
class FpingSampleJsonTests(test_servers.ServersSampleBase):
|
||||
extension_name = "os-fping"
|
||||
|
||||
def setUp(self):
|
||||
super(FpingSampleJsonTests, self).setUp()
|
||||
|
||||
def fake_check_fping(self):
|
||||
pass
|
||||
self.stubs.Set(utils, "execute", test_fping.execute)
|
||||
self.stubs.Set(fping.FpingController, "check_fping",
|
||||
fake_check_fping)
|
||||
|
||||
def test_get_fping(self):
|
||||
self._post_server()
|
||||
response = self._do_get('os-fping')
|
||||
subs = self._get_regexes()
|
||||
self._verify_response('fping-get-resp', subs, response, 200)
|
||||
|
||||
def test_get_fping_details(self):
|
||||
uuid = self._post_server()
|
||||
response = self._do_get('os-fping/%s' % (uuid))
|
||||
subs = self._get_regexes()
|
||||
self._verify_response('fping-get-details-resp', subs, response, 200)
|
@@ -85,6 +85,7 @@ nova.api.v3.extensions =
|
||||
flavor_access = nova.api.openstack.compute.plugins.v3.flavor_access:FlavorAccess
|
||||
flavor_rxtx = nova.api.openstack.compute.plugins.v3.flavor_rxtx:FlavorRxtx
|
||||
flavor_manage = nova.api.openstack.compute.plugins.v3.flavor_manage:FlavorManage
|
||||
fping = nova.api.openstack.compute.plugins.v3.fping:Fping
|
||||
hide_server_addresses = nova.api.openstack.compute.plugins.v3.hide_server_addresses:HideServerAddresses
|
||||
hosts = nova.api.openstack.compute.plugins.v3.hosts:Hosts
|
||||
hypervisors = nova.api.openstack.compute.plugins.v3.hypervisors:Hypervisors
|
||||
|
Reference in New Issue
Block a user