forked from ungleich-public/cdist
28 changed files with 1769 additions and 36 deletions
@ -0,0 +1,390 @@
|
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# |
||||
# 2016 Darko Poljak (darko.poljak at gmail.com) |
||||
# |
||||
# This file is part of cdist. |
||||
# |
||||
# cdist is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# cdist is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with cdist. If not, see <http://www.gnu.org/licenses/>. |
||||
# |
||||
# |
||||
|
||||
import cdist |
||||
import logging |
||||
import os |
||||
import os.path |
||||
import itertools |
||||
import sys |
||||
from cdist.hostsource import hostfile_process_line |
||||
|
||||
DIST_INVENTORY_DB_NAME = "inventory" |
||||
|
||||
dist_inventory_db = os.path.abspath(os.path.join( |
||||
os.path.dirname(cdist.__file__), DIST_INVENTORY_DB_NAME)) |
||||
|
||||
|
||||
def determine_default_inventory_dir(args): |
||||
# The order of inventory dir setting by decreasing priority |
||||
# 1. inventory_dir argument |
||||
# 2. CDIST_INVENTORY_DIR env var if set |
||||
# 3. ~/.cdist/inventory if HOME env var is set |
||||
# 4. distribution inventory directory |
||||
if not args.inventory_dir: |
||||
if 'CDIST_INVENTORY_DIR' in os.environ: |
||||
args.inventory_dir = os.environ['CDIST_INVENTORY_DIR'] |
||||
else: |
||||
home = cdist.home_dir() |
||||
if home: |
||||
args.inventory_dir = os.path.join(home, DIST_INVENTORY_DB_NAME) |
||||
else: |
||||
args.inventory_dir = dist_inventory_db |
||||
|
||||
|
||||
def contains_all(big, little): |
||||
"""Return True if big contains all elements from little, |
||||
False otherwise. |
||||
""" |
||||
return set(little).issubset(set(big)) |
||||
|
||||
|
||||
def contains_any(big, little): |
||||
"""Return True if big contains any element from little, |
||||
False otherwise. |
||||
""" |
||||
for x in little: |
||||
if x in big: |
||||
return True |
||||
return False |
||||
|
||||
|
||||
def check_always_true(x, y): |
||||
return True |
||||
|
||||
|
||||
def rstrip_nl(s): |
||||
'''str.rstrip "\n" from s''' |
||||
return str.rstrip(s, "\n") |
||||
|
||||
|
||||
class Inventory(object): |
||||
"""Inventory main class""" |
||||
|
||||
def __init__(self, db_basedir=dist_inventory_db): |
||||
self.db_basedir = db_basedir |
||||
self.log = logging.getLogger("inventory") |
||||
self.init_db() |
||||
|
||||
def init_db(self): |
||||
self.log.debug("Init db: {}".format(self.db_basedir)) |
||||
if not os.path.exists(self.db_basedir): |
||||
os.makedirs(self.db_basedir, exist_ok=True) |
||||
elif not os.path.isdir(self.db_basedir): |
||||
raise cdist.Error(("Invalid inventory db basedir \'{}\'," |
||||
" must be a directory").format(self.db_basedir)) |
||||
|
||||
@staticmethod |
||||
def strlist_to_list(slist): |
||||
if slist: |
||||
result = [x for x in slist.split(',') if x] |
||||
else: |
||||
result = [] |
||||
return result |
||||
|
||||
def _input_values(self, source): |
||||
"""Yield input values from source. |
||||
Source can be a sequence or filename (stdin if '-'). |
||||
In case of filename each line represents one input value. |
||||
""" |
||||
if isinstance(source, str): |
||||
import fileinput |
||||
try: |
||||
with fileinput.FileInput(files=(source)) as f: |
||||
for x in f: |
||||
result = hostfile_process_line(x, strip_func=rstrip_nl) |
||||
if result: |
||||
yield result |
||||
except (IOError, OSError) as e: |
||||
raise cdist.Error("Error reading from \'{}\'".format( |
||||
source)) |
||||
else: |
||||
if source: |
||||
for x in source: |
||||
if x: |
||||
yield x |
||||
|
||||
def _host_path(self, host): |
||||
hostpath = os.path.join(self.db_basedir, host) |
||||
return hostpath |
||||
|
||||
def _all_hosts(self): |
||||
return os.listdir(self.db_basedir) |
||||
|
||||
def _check_host(self, hostpath): |
||||
if not os.path.exists(hostpath): |
||||
return False |
||||
else: |
||||
if not os.path.isfile(hostpath): |
||||
raise cdist.Error(("Host path \'{}\' exists, but is not" |
||||
" a valid file").format(hostpath)) |
||||
return True |
||||
|
||||
def _read_host_tags(self, hostpath): |
||||
result = set() |
||||
with open(hostpath, "rt") as f: |
||||
for tag in f: |
||||
tag = tag.rstrip("\n") |
||||
if tag: |
||||
result.add(tag) |
||||
return result |
||||
|
||||
def _get_host_tags(self, host): |
||||
hostpath = self._host_path(host) |
||||
if self._check_host(hostpath): |
||||
return self._read_host_tags(hostpath) |
||||
else: |
||||
return None |
||||
|
||||
def _write_host_tags(self, host, tags): |
||||
hostpath = self._host_path(host) |
||||
if self._check_host(hostpath): |
||||
with open(hostpath, "wt") as f: |
||||
for tag in tags: |
||||
f.write("{}\n".format(tag)) |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
@classmethod |
||||
def commandline(cls, args): |
||||
"""Manipulate inventory db""" |
||||
log = logging.getLogger("cdist") |
||||
if 'taglist' in args: |
||||
args.taglist = cls.strlist_to_list(args.taglist) |
||||
determine_default_inventory_dir(args) |
||||
|
||||
log.info("Using inventory: {}".format(args.inventory_dir)) |
||||
log.debug("Inventory args: {}".format(vars(args))) |
||||
log.debug("Inventory command: {}".format(args.subcommand)) |
||||
|
||||
if args.subcommand == "list": |
||||
c = InventoryList(hosts=args.host, istag=args.tag, |
||||
hostfile=args.hostfile, |
||||
db_basedir=args.inventory_dir, |
||||
list_only_host=args.list_only_host, |
||||
has_all_tags=args.has_all_tags) |
||||
elif args.subcommand == "add-host": |
||||
c = InventoryHost(hosts=args.host, hostfile=args.hostfile, |
||||
db_basedir=args.inventory_dir) |
||||
elif args.subcommand == "del-host": |
||||
c = InventoryHost(hosts=args.host, hostfile=args.hostfile, |
||||
all=args.all, db_basedir=args.inventory_dir, |
||||
action="del") |
||||
elif args.subcommand == "add-tag": |
||||
c = InventoryTag(hosts=args.host, tags=args.taglist, |
||||
hostfile=args.hostfile, tagfile=args.tagfile, |
||||
db_basedir=args.inventory_dir) |
||||
elif args.subcommand == "del-tag": |
||||
c = InventoryTag(hosts=args.host, tags=args.taglist, |
||||
hostfile=args.hostfile, tagfile=args.tagfile, |
||||
all=args.all, db_basedir=args.inventory_dir, |
||||
action="del") |
||||
else: |
||||
raise cdist.Error("Unknown inventory command \'{}\'".format( |
||||
args.subcommand)) |
||||
c.run() |
||||
|
||||
|
||||
class InventoryList(Inventory): |
||||
def __init__(self, hosts=None, istag=False, hostfile=None, |
||||
list_only_host=False, has_all_tags=False, |
||||
db_basedir=dist_inventory_db): |
||||
super().__init__(db_basedir) |
||||
self.hosts = hosts |
||||
self.istag = istag |
||||
self.hostfile = hostfile |
||||
self.list_only_host = list_only_host |
||||
self.has_all_tags = has_all_tags |
||||
|
||||
def _print(self, host, tags): |
||||
if self.list_only_host: |
||||
print("{}".format(host)) |
||||
else: |
||||
print("{} {}".format(host, ",".join(sorted(tags)))) |
||||
|
||||
def _do_list(self, it_tags, it_hosts, check_func): |
||||
if (it_tags is not None): |
||||
param_tags = set(it_tags) |
||||
self.log.debug("param_tags: {}".format(param_tags)) |
||||
else: |
||||
param_tags = set() |
||||
for host in it_hosts: |
||||
self.log.debug("host: {}".format(host)) |
||||
tags = self._get_host_tags(host) |
||||
if tags is None: |
||||
self.log.info("Host \'{}\' not found, skipped".format(host)) |
||||
continue |
||||
self.log.debug("tags: {}".format(tags)) |
||||
if check_func(tags, param_tags): |
||||
yield host, tags |
||||
|
||||
def entries(self): |
||||
if not self.hosts and not self.hostfile: |
||||
self.log.info("Listing all hosts") |
||||
it_hosts = self._all_hosts() |
||||
it_tags = None |
||||
check_func = check_always_true |
||||
else: |
||||
it = itertools.chain(self._input_values(self.hosts), |
||||
self._input_values(self.hostfile)) |
||||
if self.istag: |
||||
self.log.info("Listing by tag(s)") |
||||
it_hosts = self._all_hosts() |
||||
it_tags = it |
||||
if self.has_all_tags: |
||||
check_func = contains_all |
||||
else: |
||||
check_func = contains_any |
||||
else: |
||||
self.log.info("Listing by host(s)") |
||||
it_hosts = it |
||||
it_tags = None |
||||
check_func = check_always_true |
||||
for host, tags in self._do_list(it_tags, it_hosts, check_func): |
||||
yield host, tags |
||||
|
||||
def host_entries(self): |
||||
for host, tags in self.entries(): |
||||
yield host |
||||
|
||||
def run(self): |
||||
for host, tags in self.entries(): |
||||
self._print(host, tags) |
||||
|
||||
|
||||
class InventoryHost(Inventory): |
||||
def __init__(self, hosts=None, hostfile=None, |
||||
db_basedir=dist_inventory_db, all=False, action="add"): |
||||
super().__init__(db_basedir) |
||||
self.actions = ("add", "del") |
||||
if action not in self.actions: |
||||
raise cdist.Error("Invalid action \'{}\', valid actions are:" |
||||
" {}\n".format(action, self.actions.keys())) |
||||
self.action = action |
||||
self.hosts = hosts |
||||
self.hostfile = hostfile |
||||
self.all = all |
||||
|
||||
if not self.hosts and not self.hostfile: |
||||
self.hostfile = "-" |
||||
|
||||
def _new_hostpath(self, hostpath): |
||||
# create empty file |
||||
with open(hostpath, "w"): |
||||
pass |
||||
|
||||
def _action(self, host): |
||||
if self.action == "add": |
||||
self.log.info("Adding host \'{}\'".format(host)) |
||||
elif self.action == "del": |
||||
self.log.info("Deleting host \'{}\'".format(host)) |
||||
hostpath = self._host_path(host) |
||||
self.log.debug("hostpath: {}".format(hostpath)) |
||||
if self.action == "add" and not os.path.exists(hostpath): |
||||
self._new_hostpath(hostpath) |
||||
else: |
||||
if not os.path.isfile(hostpath): |
||||
raise cdist.Error(("Host path \'{}\' is" |
||||
" not a valid file").format(hostpath)) |
||||
if self.action == "del": |
||||
os.remove(hostpath) |
||||
|
||||
def run(self): |
||||
if self.action == "del" and self.all: |
||||
self.log.debug("Doing for all hosts") |
||||
it = self._all_hosts() |
||||
else: |
||||
self.log.debug("Doing for specified hosts") |
||||
it = itertools.chain(self._input_values(self.hosts), |
||||
self._input_values(self.hostfile)) |
||||
for host in it: |
||||
self._action(host) |
||||
|
||||
|
||||
class InventoryTag(Inventory): |
||||
def __init__(self, hosts=None, tags=None, hostfile=None, tagfile=None, |
||||
db_basedir=dist_inventory_db, all=False, action="add"): |
||||
super().__init__(db_basedir) |
||||
self.actions = ("add", "del") |
||||
if action not in self.actions: |
||||
raise cdist.Error("Invalid action \'{}\', valid actions are:" |
||||
" {}\n".format(action, self.actions.keys())) |
||||
self.action = action |
||||
self.hosts = hosts |
||||
self.tags = tags |
||||
self.hostfile = hostfile |
||||
self.tagfile = tagfile |
||||
self.all = all |
||||
|
||||
if not self.hosts and not self.hostfile: |
||||
self.allhosts = True |
||||
else: |
||||
self.allhosts = False |
||||
if not self.tags and not self.tagfile: |
||||
self.tagfile = "-" |
||||
|
||||
if self.hostfile == "-" and self.tagfile == "-": |
||||
raise cdist.Error("Cannot read both, hosts and tags, from stdin") |
||||
|
||||
def _read_input_tags(self): |
||||
self.input_tags = set() |
||||
for tag in itertools.chain(self._input_values(self.tags), |
||||
self._input_values(self.tagfile)): |
||||
self.input_tags.add(tag) |
||||
|
||||
def _action(self, host): |
||||
host_tags = self._get_host_tags(host) |
||||
if host_tags is None: |
||||
print("Host \'{}\' does not exist, skipping".format(host), |
||||
file=sys.stderr) |
||||
return |
||||
self.log.debug("existing host_tags: {}".format(host_tags)) |
||||
if self.action == "del" and self.all: |
||||
host_tags = set() |
||||
else: |
||||
for tag in self.input_tags: |
||||
if self.action == "add": |
||||
self.log.info("Adding tag \'{}\' for host \'{}\'".format( |
||||
tag, host)) |
||||
host_tags.add(tag) |
||||
elif self.action == "del": |
||||
self.log.info("Deleting tag \'{}\' for host \'{}\'".format( |
||||
tag, host)) |
||||
if tag in host_tags: |
||||
host_tags.remove(tag) |
||||
self.log.debug("new host tags: {}".format(host_tags)) |
||||
if not self._write_host_tags(host, host_tags): |
||||
self.log.info("{} does not exist, skipped".format(host)) |
||||
|
||||
def run(self): |
||||
if self.allhosts: |
||||
self.log.debug("Doing for all hosts") |
||||
it = self._all_hosts() |
||||
else: |
||||
self.log.debug("Doing for specified hosts") |
||||
it = itertools.chain(self._input_values(self.hosts), |
||||
self._input_values(self.hostfile)) |
||||
if not(self.action == "del" and self.all): |
||||
self._read_input_tags() |
||||
for host in it: |
||||
self._action(host) |
@ -0,0 +1,476 @@
|
||||
# -*- coding: utf-8 -*- |
||||
# |
||||
# 2016 Darko Poljak (darko.poljak at gmail.com) |
||||
# |
||||
# This file is part of cdist. |
||||
# |
||||
# cdist is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# cdist is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with cdist. If not, see <http://www.gnu.org/licenses/>. |
||||
# |
||||
# |
||||
|
||||
import os |
||||
import shutil |
||||
import cdist |
||||
import os.path as op |
||||
import unittest |
||||
import sys |
||||
from cdist import test |
||||
from cdist import inventory |
||||
from io import StringIO |
||||
|
||||
my_dir = op.abspath(op.dirname(__file__)) |
||||
fixtures = op.join(my_dir, 'fixtures') |
||||
inventory_dir = op.join(fixtures, "inventory") |
||||
|
||||
|
||||
class InventoryTestCase(test.CdistTestCase): |
||||
|
||||
def _create_host_with_tags(self, host, tags): |
||||
os.makedirs(inventory_dir, exist_ok=True) |
||||
hostfile = op.join(inventory_dir, host) |
||||
with open(hostfile, "w") as f: |
||||
for x in tags: |
||||
f.write("{}\n".format(x)) |
||||
|
||||
def setUp(self): |
||||
self.maxDiff = None |
||||
self.db = { |
||||
"loadbalancer1": ["loadbalancer", "all", "europe", ], |
||||
"loadbalancer2": ["loadbalancer", "all", "europe", ], |
||||
"loadbalancer3": ["loadbalancer", "all", "africa", ], |
||||
"loadbalancer4": ["loadbalancer", "all", "africa", ], |
||||
"web1": ["web", "all", "static", ], |
||||
"web2": ["web", "all", "dynamic", ], |
||||
"web3": ["web", "all", "dynamic", ], |
||||
"shell1": ["shell", "all", "free", ], |
||||
"shell2": ["shell", "all", "free", ], |
||||
"shell3": ["shell", "all", "charge", ], |
||||
"shell4": ["shell", "all", "charge", ], |
||||
"monty": ["web", "python", "shell", ], |
||||
"python": ["web", "python", "shell", ], |
||||
} |
||||
for x in self.db: |
||||
self.db[x] = sorted(self.db[x]) |
||||
for host in self.db: |
||||
self._create_host_with_tags(host, self.db[host]) |
||||
self.sys_stdout = sys.stdout |
||||
out = StringIO() |
||||
sys.stdout = out |
||||
|
||||
def _get_output(self): |
||||
sys.stdout.flush() |
||||
output = sys.stdout.getvalue().strip() |
||||
return output |
||||
|
||||
def tearDown(self): |
||||
sys.stdout = self.sys_stdout |
||||
shutil.rmtree(inventory_dir) |
||||
|
||||
def test_inventory_create_db(self): |
||||
dbdir = op.join(fixtures, "foo") |
||||
inv = inventory.Inventory(db_basedir=dbdir) |
||||
self.assertTrue(os.path.isdir(dbdir)) |
||||
self.assertEqual(inv.db_basedir, dbdir) |
||||
shutil.rmtree(inv.db_basedir) |
||||
|
||||
# InventoryList |
||||
def test_inventory_list_print(self): |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir) |
||||
invList.run() |
||||
output = self._get_output() |
||||
self.assertTrue(' ' in output) |
||||
|
||||
def test_inventory_list_print_host_only(self): |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
list_only_host=True) |
||||
invList.run() |
||||
output = self._get_output() |
||||
self.assertFalse(' ' in output) |
||||
|
||||
def test_inventory_list_all(self): |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
self.assertEqual(db, self.db) |
||||
|
||||
def test_inventory_list_by_host_hosts(self): |
||||
hosts = ("web1", "web2", "web3",) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
hosts=hosts) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
expected_db = {host: sorted(self.db[host]) for host in hosts} |
||||
self.assertEqual(db, expected_db) |
||||
|
||||
def test_inventory_list_by_host_hostfile(self): |
||||
hosts = ("web1", "web2", "web3",) |
||||
hostfile = op.join(fixtures, "hosts") |
||||
with open(hostfile, "w") as f: |
||||
for x in hosts: |
||||
f.write("{}\n".format(x)) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
hostfile=hostfile) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
expected_db = {host: sorted(self.db[host]) for host in hosts} |
||||
self.assertEqual(db, expected_db) |
||||
os.remove(hostfile) |
||||
|
||||
def test_inventory_list_by_host_hosts_hostfile(self): |
||||
hosts = ("shell1", "shell4",) |
||||
hostsf = ("web1", "web2", "web3",) |
||||
hostfile = op.join(fixtures, "hosts") |
||||
with open(hostfile, "w") as f: |
||||
for x in hostsf: |
||||
f.write("{}\n".format(x)) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
hosts=hosts, hostfile=hostfile) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
import itertools |
||||
expected_db = {host: sorted(self.db[host]) for host in |
||||
itertools.chain(hostsf, hosts)} |
||||
self.assertEqual(db, expected_db) |
||||
os.remove(hostfile) |
||||
|
||||
def _gen_expected_db_for_tags(self, tags): |
||||
db = {} |
||||
for host in self.db: |
||||
for tag in tags: |
||||
if tag in self.db[host]: |
||||
db[host] = self.db[host] |
||||
break |
||||
return db |
||||
|
||||
def _gen_expected_db_for_has_all_tags(self, tags): |
||||
db = {} |
||||
for host in self.db: |
||||
if set(tags).issubset(set(self.db[host])): |
||||
db[host] = self.db[host] |
||||
return db |
||||
|
||||
def test_inventory_list_by_tag_hosts(self): |
||||
tags = ("web", "shell",) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
istag=True, hosts=tags) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
expected_db = self._gen_expected_db_for_tags(tags) |
||||
self.assertEqual(db, expected_db) |
||||
|
||||
def test_inventory_list_by_tag_hostfile(self): |
||||
tags = ("web", "shell",) |
||||
tagfile = op.join(fixtures, "tags") |
||||
with open(tagfile, "w") as f: |
||||
for x in tags: |
||||
f.write("{}\n".format(x)) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
istag=True, hostfile=tagfile) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
expected_db = self._gen_expected_db_for_tags(tags) |
||||
self.assertEqual(db, expected_db) |
||||
os.remove(tagfile) |
||||
|
||||
def test_inventory_list_by_tag_hosts_hostfile(self): |
||||
tags = ("web", "shell",) |
||||
tagsf = ("dynamic", "europe",) |
||||
tagfile = op.join(fixtures, "tags") |
||||
with open(tagfile, "w") as f: |
||||
for x in tagsf: |
||||
f.write("{}\n".format(x)) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
istag=True, hosts=tags, |
||||
hostfile=tagfile) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
import itertools |
||||
expected_db = self._gen_expected_db_for_tags(tags + tagsf) |
||||
self.assertEqual(db, expected_db) |
||||
os.remove(tagfile) |
||||
|
||||
def test_inventory_list_by_tag_has_all_tags(self): |
||||
tags = ("web", "python", "shell",) |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir, |
||||
istag=True, hosts=tags, |
||||
has_all_tags=True) |
||||
entries = invList.entries() |
||||
db = {host: sorted(tags) for host, tags in entries} |
||||
expected_db = self._gen_expected_db_for_has_all_tags(tags) |
||||
self.assertEqual(db, expected_db) |
||||
|
||||
# InventoryHost |
||||
def test_inventory_host_add_hosts(self): |
||||
hosts = ("spam", "eggs", "foo",) |
||||
invHost = inventory.InventoryHost(db_basedir=inventory_dir, |
||||
action="add", hosts=hosts) |
||||
invHost.run() |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir) |
||||
expected_hosts = tuple(x for x in invList.host_entries() if x in hosts) |
||||
self.assertEqual(sorted(hosts), sorted(expected_hosts)) |
||||
|
||||
def test_inventory_host_add_hostfile(self): |
||||
hosts = ("spam-new", "eggs-new", "foo-new",) |
||||
hostfile = op.join(fixtures, "hosts") |
||||
with open(hostfile, "w") as f: |
||||
for x in hosts: |
||||
f.write("{}\n".format(x)) |
||||
invHost = inventory.InventoryHost(db_basedir=inventory_dir, |
||||
action="add", hostfile=hostfile) |
||||
invHost.run() |
||||
invList = inventory.InventoryList(db_basedir=inventory_dir) |
||||
expected_hosts = tuple(x for x in invList.host_entries() if x in hosts) |
||||
self.assertEqual(sorted(hosts), sorted(expected_hosts)) |
||||
os.remove(hostfile) |
||||
|
||||
|