Skip to content

Commit

Permalink
Moved commands to commands dir; Added shared dir and moved nodes and …
Browse files Browse the repository at this point in the history
…common code there; Huge expansion of collect
  • Loading branch information
0xdabbad00 committed Jun 4, 2018
1 parent 437dd11 commit 95ee3a8
Show file tree
Hide file tree
Showing 12 changed files with 778 additions and 293 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ venv/
web/data.json
.coverage
htmlcov/
account-data/
183 changes: 24 additions & 159 deletions cloudmapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,184 +23,49 @@
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
This script manages CloudMapper, a tool for creating network diagrams of AWS environments.
This script manages CloudMapper, a tool for analyzing AWS environments.
"""
from __future__ import (absolute_import, division, print_function)
import json
import argparse
import sys
from cloudmapper.webserver import run_webserver
import pkgutil
import commands

__version__ = "1.0.0"
__version__ = "2.0.0"

def get_account(account_name, config, config_filename):
for account in config["accounts"]:
if account["name"] == account_name:
return account
if account_name is None and account.get("default", False):
return account

# Else could not find account
if account_name is None:
exit("ERROR: Must specify an account, or set one in {} as a default".format(config_filename))
exit("ERROR: Account named \"{}\" not found in {}".format(account_name, config_filename))


def run_gathering(arguments):
from cloudmapper.gatherer import gather
parser = argparse.ArgumentParser()
parser.add_argument("--config", help="Config file name",
default="config.json", type=str)
parser.add_argument("--account-name", help="Account to collect from",
required=False, type=str)
parser.add_argument("--profile-name", help="AWS profile name",
required=False, type=str)
parser.add_argument('--clean', help='Remove any existing data for the account before gathering', action='store_true')

args = parser.parse_args(arguments)

if not args.account_name:
try:
config = json.load(open(args.config))
except IOError:
exit("ERROR: Unable to load config file \"{}\"".format(args.config))
except ValueError as e:
exit("ERROR: Config file \"{}\" could not be loaded ({}), see config.json.demo for an example".format(args.config, e))
args.account_name = get_account(args.account_name, config, args.config)['name']

gather(args)


def run_configure(arguments):
from cloudmapper.configure import configure
if len(arguments) == 0:
exit("ERROR: Missing action for configure. Should be in {add-cidr|add-account|remove-cidr|remove-account}")
return
action = arguments[0]
arguments = arguments[1:]
parser = argparse.ArgumentParser()
parser.add_argument("--config-file", help="Path to the config file",
default="config.json", type=str)
if action == 'add-account' or action == 'remove-account':
required = True if action.startswith('add') else False
parser.add_argument("--name", help="Account name",
required=required, type=str)
parser.add_argument("--id", help="Account ID",
required=required, type=str)
parser.add_argument("--default", help="Default account",
required=False, default="False", type=str)
elif action == 'add-cidr' or action == 'remove-cidr':
required = True if action.startswith('add') else False
parser.add_argument("--cidr", help="CIDR IP",
required=required, type=str)
parser.add_argument("--name", help="CIDR Name",
required=required, type=str)
args = parser.parse_args(arguments)
configure(action, args)


def run_prepare(arguments):
from cloudmapper.prepare import prepare

# Parse arguments
parser = argparse.ArgumentParser()
parser.add_argument("--config", help="Config file name",
default="config.json", type=str)
parser.add_argument("--account-name", help="Account to collect from",
required=False, type=str)
parser.add_argument("--regions", help="Regions to restrict to (ex. us-east-1,us-west-2)",
default=None, type=str)
parser.add_argument("--vpc-ids", help="VPC ids to restrict to (ex. vpc-1234,vpc-abcd)",
default=None, type=str)
parser.add_argument("--vpc-names", help="VPC names to restrict to (ex. prod,dev)",
default=None, type=str)
parser.add_argument("--internal-edges", help="Show all connections (default)",
dest='internal_edges', action='store_true')
parser.add_argument("--no-internal-edges", help="Only show connections to external CIDRs",
dest='internal_edges', action='store_false')
parser.add_argument("--inter-rds-edges", help="Show connections between RDS instances",
dest='inter_rds_edges', action='store_true')
parser.add_argument("--no-inter-rds-edges", help="Do not show connections between RDS instances (default)",
dest='inter_rds_edges', action='store_false')
parser.add_argument("--read-replicas", help="Show RDS read replicas (default)",
dest='read_replicas', action='store_true')
parser.add_argument("--no-read-replicas", help="Do not show RDS read replicas",
dest='read_replicas', action='store_false')
parser.add_argument("--azs", help="Show availability zones (default)",
dest='azs', action='store_true')
parser.add_argument("--no-azs", help="Do not show availability zones",
dest='azs', action='store_false')
parser.add_argument("--collapse-by-tag", help="Collapse nodes with the same tag to a single node",
dest='collapse_by_tag', default=None, type=str)

parser.set_defaults(internal_edges=True)
parser.set_defaults(inter_rds_edges=False)
parser.set_defaults(read_replicas=True)
parser.set_defaults(azs=True)

args = parser.parse_args(arguments)

outputfilter = {}
if args.regions:
# Regions are given as 'us-east-1,us-west-2'. Split this by the comma,
# wrap each with quotes, and add the comma back. This is needed for how we do filtering.
outputfilter["regions"] = ','.join(['"' + r + '"' for r in args.regions.split(',')])
if args.vpc_ids:
outputfilter["vpc-ids"] = ','.join(['"' + r + '"' for r in args.vpc_ids.split(',')])
if args.vpc_names:
outputfilter["vpc-names"] = ','.join(['"' + r + '"' for r in args.vpc_names.split(',')])

outputfilter["internal_edges"] = args.internal_edges
outputfilter["read_replicas"] = args.read_replicas
outputfilter["inter_rds_edges"] = args.inter_rds_edges
outputfilter["azs"] = args.azs
outputfilter["collapse_by_tag"] = args.collapse_by_tag

# Read accounts file
try:
config = json.load(open(args.config))
except IOError:
exit("ERROR: Unable to load config file \"{}\"".format(args.config))
except ValueError as e:
exit("ERROR: Config file \"{}\" could not be loaded ({}), see config.json.demo for an example".format(args.config, e))
account = get_account(args.account_name, config, args.config)

prepare(account, config, outputfilter)


def show_help():
def show_help(commands):
print("CloudMapper {}".format(__version__))
print("usage: {} [gather|prepare|serve] [...]".format(sys.argv[0]))
print(" configure: Configure and create your config file")
print(" gather: Queries AWS for account data and caches it locally")
print(" prepare: Prepares the data for viewing")
print(" serve: Runs a local webserver for viewing the data")
print("usage: {} [{}] [...]".format(sys.argv[0], "|".join(sorted(commands.keys()))))
for command, module in sorted(commands.items()):
print(" {}: {}".format(command, module.__description__))
exit(-1)


def main():
"""Entry point for the CLI."""


# Load commands
# TODO: This adds half a second to the start time. Is there a smarter way to do this?
commands = {}
commands_path = 'commands'
for importer, command_name, _ in pkgutil.iter_modules([commands_path]):
full_package_name = '%s.%s' % (commands_path, command_name)
module = importer.find_module(command_name
).load_module(full_package_name)
commands[command_name] = module

# Parse command
if len(sys.argv) <= 1:
show_help()

show_help(commands)
command = sys.argv[1]
arguments = sys.argv[2:]

if command == "prepare":
run_prepare(arguments)
elif command == "serve":
run_webserver(arguments)
elif command == "gather":
run_gathering(arguments)
elif command == "configure":
run_configure(arguments)
if command in commands:
commands[command].run(arguments)
else:
show_help()

print("Complete")

show_help(commands)

if __name__ == "__main__":
main()
118 changes: 0 additions & 118 deletions cloudmapper/gatherer.py

This file was deleted.

File renamed without changes.
Loading

0 comments on commit 95ee3a8

Please sign in to comment.