Files
quark/bin/redis_sg_tool
Matt Dietz 10f101d7d0 Bug fix for --config-file in redis_sg_tool
The redis SG tool was incorrectly handling the --config-file option.
This patch amends it correctly pass the argument to oslo.config
2014-11-20 17:28:06 +00:00

267 lines
9.4 KiB
Python
Executable File

#!/usr/bin/python
# Copyright 2014 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.
"""Quark Redis Security Groups CLI tool.
Usage: redis_sg_tool [-h] [--config-file=PATH] [--retries=<retries>]
[--retry-delay=<delay>] <command> [--yarly]
Options:
-h --help Show this screen.
--version Show version.
--config-file=PATH Use a different config file path
--retries=<retries> Number of times to re-attempt some operations
--retry-delay=<delay> Amount of time to wait between retries
Available commands are:
redis_sg_tool test-connection
redis_sg_tool vifs-in-redis
redis_sg_tool num-groups
redis_sg_tool ports-with-groups
redis_sg_tool purge-orphans [--yarly]
redis_sg_tool write-groups [--yarly]
redis_sg_tool -h | --help
redis_sg_tool --version
"""
VERSION = 0.1
RETRIES = 5
RETRY_DELAY = 1
import sys
import time
import docopt
import netaddr
from neutron.common import config
import neutron.context
from oslo.config import cfg
from quark.db import api as db_api
from quark import exceptions as q_exc
from quark.security_groups import redis_client
class QuarkRedisTool(object):
def __init__(self, arguments):
self._args = arguments
self._retries = RETRIES
self._retry_delay = RETRY_DELAY
if self._args.get("--retries"):
self._retries = int(self._args["--retries"])
if self._args.get("--retry-delay"):
self._retry_delay = int(self._args["--retry-delay"])
config_args = []
if self._args.get("--config-file"):
config_args.append("--config-file=%s" %
self._args.pop("--config-file"))
self._dryrun = not self._args.get("--yarly")
config.init(config_args)
if not cfg.CONF.config_file:
sys.exit(_("ERROR: Unable to find configuration file via the "
"default search paths (~/.neutron/, ~/, /etc/neutron/, "
"/etc/) and the '--config-file' option!"))
def dispatch(self):
command = self._args.get("<command>")
if command == "test-connection":
self.test_connection()
elif command == "vifs-in-redis":
self.vif_count()
elif command == "num-groups":
self.num_groups()
elif command == "ports-with-groups":
self.ports_with_groups()
elif command == "purge-orphans":
self.purge_orphans(self._dryrun)
elif command == "write-groups":
self.write_groups(self._dryrun)
else:
print ("Redis security groups tool. Re-run with -h/--help for "
"options")
def _get_connection(self, use_master=False, giveup=True):
client = redis_client.Client(use_master=use_master)
try:
# You have to use the connection determine it's functional
result = client.echo("connected")
if result == "connected":
return client
except Exception, e:
print e
if giveup:
print "Giving up..."
sys.exit(1)
def test_connection(self):
client = self._get_connection()
if client:
print "Connected Successfully"
else:
print "Could not connect to Redis"
def vif_count(self):
client = self._get_connection()
print len(client.vif_keys())
def num_groups(self):
ctx = neutron.context.get_admin_context()
print db_api.security_group_count(ctx)
def ports_with_groups(self):
ctx = neutron.context.get_admin_context()
print db_api.ports_with_security_groups_count(ctx)
def purge_orphans(self, dryrun=False):
client = self._get_connection(use_master=not dryrun)
ctx = neutron.context.get_admin_context()
ports_with_groups = db_api.ports_with_security_groups_find(ctx).all()
if dryrun:
print
print ("Purging orphans in dry run mode. Existing rules in Redis "
"will be checked against those in the database. If any "
"are found in Redis but lack matching database rules, "
"they'll be deleted from the database.\n\nTo actually "
"apply the groups, re-run with the --yarly flag.")
print
print ("Found %s ports with security groups" %
len(ports_with_groups))
# Pre-spin the list of orphans
vifs = {}
for vif in client.vif_keys():
vifs[vif] = False
if dryrun:
print "Found %d VIFs in Redis" % len(vifs)
# Pop off the ones we find in the database
for port in ports_with_groups:
vif_key = client.rule_key(port["device_id"], port["mac_address"])
vifs.pop(vif_key, None)
if dryrun:
print "Found %d orphaned VIF rule sets" % len(vifs)
print '=' * 80
for orphan in vifs.keys():
if dryrun:
print "VIF %s is orphaned" % orphan
else:
for retry in xrange(self._retries):
try:
client.delete_vif_rules(orphan)
break
except q_exc.RedisConnectionFailure:
time.sleep(self._retry_delay)
client = self._get_connection(use_master=True,
giveup=False)
if dryrun:
print '=' * 80
print
print "Re-run with --yarly to apply changes"
print "Done!"
def write_groups(self, dryrun=False):
client = self._get_connection(use_master=not dryrun)
ctx = neutron.context.get_admin_context()
ports_with_groups = db_api.ports_with_security_groups_find(ctx).all()
if dryrun:
print
print ("Writing groups in dry run mode. Existing rules in Redis "
"will be checked against those in the database, with a "
"running report generated of all those that will be "
"overwritten.\n\nTo actually apply the groups, re-run "
"with the --yarly flag.")
print
print ("Found %s ports with security groups" %
len(ports_with_groups))
if dryrun:
vifs = len(client.vif_keys())
if vifs > 0:
print ("There are %d VIFs with rules in Redis, some of which "
"may be overwritten!" % vifs)
print
overwrite_count = 0
for port in ports_with_groups:
mac = netaddr.EUI(port["mac_address"])
# Rather than loading everything in one giant chunk, we'll make
# trips per port.
group_ids = [g["id"] for g in port.security_groups]
rules = db_api.security_group_rule_find(ctx, group_id=group_ids,
scope=db_api.ALL)
if dryrun:
existing_rules = client.get_rules_for_port(port["device_id"],
port["mac_address"])
if existing_rules:
overwrite_count += 1
db_len = len(rules)
existing_len = len(existing_rules["rules"])
print ("== Port ID:%s - MAC:%s - Device ID:%s - "
"Redis Rules:%d - DB Rules:%d" %
(port["id"], mac, port["device_id"], existing_len,
db_len))
if not dryrun:
for retry in xrange(self._retries):
try:
payload = client.serialize_rules(rules)
client.apply_rules(
port["device_id"], port["mac_address"], payload)
break
except q_exc.RedisConnectionFailure:
time.sleep(self._retry_delay)
client = self._get_connection(use_master=True,
giveup=False)
if dryrun:
print
print ("Total number of VIFs to overwrite/were overwritten: %s" %
overwrite_count)
diff = vifs - overwrite_count
if diff > 0:
print "Orphaned VIFs in Redis:", diff
print "Run purge-orphans to clean then up"
if dryrun:
print ("Total number of VIFs to write: %d" %
len(ports_with_groups))
if dryrun:
print '=' * 80
print "Re-run with --yarly to apply changes"
print "Done!"
if __name__ == "__main__":
arguments = docopt.docopt(__doc__,
version="Quark Redis CLI %.2f" % VERSION)
redis_tool = QuarkRedisTool(arguments)
redis_tool.dispatch()