Commit 297a524fd43fbd3e916a49042777b42bb8862162
Exists in
master
Persistência dos ativos de rede num banco local
Showing
11 changed files
with
148 additions
and
25 deletions
Show diff stats
cocar/__init__.py
| @@ -7,7 +7,6 @@ import ConfigParser | @@ -7,7 +7,6 @@ import ConfigParser | ||
| 7 | import logging | 7 | import logging |
| 8 | import logging.config | 8 | import logging.config |
| 9 | from sqlalchemy.engine import create_engine | 9 | from sqlalchemy.engine import create_engine |
| 10 | -from sqlalchemy.ext.declarative import declarative_base | ||
| 11 | from sqlalchemy.orm import scoped_session, sessionmaker | 10 | from sqlalchemy.orm import scoped_session, sessionmaker |
| 12 | 11 | ||
| 13 | 12 | ||
| @@ -46,11 +45,8 @@ class Cocar(object): | @@ -46,11 +45,8 @@ class Cocar(object): | ||
| 46 | # SQLAlchemy | 45 | # SQLAlchemy |
| 47 | sqlalchemy_url = self.config.get('sqlalchemy', 'url') | 46 | sqlalchemy_url = self.config.get('sqlalchemy', 'url') |
| 48 | self.engine = create_engine(sqlalchemy_url, echo=True) | 47 | self.engine = create_engine(sqlalchemy_url, echo=True) |
| 49 | - self.Base = declarative_base() | ||
| 50 | - self.Base.metadata.bind = self.engine | ||
| 51 | - self.session = scoped_session( | 48 | + self.Session = scoped_session( |
| 52 | sessionmaker(bind=self.engine, | 49 | sessionmaker(bind=self.engine, |
| 53 | - autocommit=True, | ||
| 54 | - #expire_on_commit=False | ||
| 55 | - ) | ||
| 56 | - ) | 50 | + autocommit=True |
| 51 | + ) | ||
| 52 | + ) | ||
| 57 | \ No newline at end of file | 53 | \ No newline at end of file |
cocar/model/__init__.py
| 1 | #!/bin/env python | 1 | #!/bin/env python |
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | -__author__ = 'eduardo' | ||
| 4 | \ No newline at end of file | 3 | \ No newline at end of file |
| 4 | +__author__ = 'eduardo' | ||
| 5 | + | ||
| 6 | +from sqlalchemy.ext.declarative import declarative_base | ||
| 7 | + | ||
| 8 | +Base = declarative_base() | ||
| 5 | \ No newline at end of file | 9 | \ No newline at end of file |
cocar/model/computer.py
| 1 | #!/bin/env python | 1 | #!/bin/env python |
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | __author__ = 'eduardo' | 3 | __author__ = 'eduardo' |
| 4 | - | 4 | +from sqlalchemy.schema import Column |
| 5 | +from sqlalchemy.types import * | ||
| 6 | +from sqlalchemy import ForeignKey | ||
| 5 | from .host import Host | 7 | from .host import Host |
| 6 | 8 | ||
| 7 | 9 | ||
| @@ -9,6 +11,12 @@ class Computer(Host): | @@ -9,6 +11,12 @@ class Computer(Host): | ||
| 9 | """ | 11 | """ |
| 10 | Ativo de rede identificado como estação de trabalho | 12 | Ativo de rede identificado como estação de trabalho |
| 11 | """ | 13 | """ |
| 14 | + __tablename__ = 'computador' | ||
| 15 | + network_ip = Column(String(16), ForeignKey("host.network_ip"), nullable=False, primary_key=True) | ||
| 16 | + so_name = Column(String) | ||
| 17 | + so_version = Column(String) | ||
| 18 | + accuracy = Column(Integer) | ||
| 19 | + | ||
| 12 | def __init__(self, | 20 | def __init__(self, |
| 13 | so, | 21 | so, |
| 14 | *args, | 22 | *args, |
| @@ -19,4 +27,10 @@ class Computer(Host): | @@ -19,4 +27,10 @@ class Computer(Host): | ||
| 19 | :param so: Sistema Operacional encontrado | 27 | :param so: Sistema Operacional encontrado |
| 20 | """ | 28 | """ |
| 21 | Host.__init__(self, *args, **kwargs) | 29 | Host.__init__(self, *args, **kwargs) |
| 22 | - self.so = so | ||
| 23 | \ No newline at end of file | 30 | \ No newline at end of file |
| 31 | + self.so = so | ||
| 32 | + | ||
| 33 | + #SQLAlchemy parameters | ||
| 34 | + os_elm = self.so.items()[0] | ||
| 35 | + self.so_name = os_elm[1]['osfamily'] | ||
| 36 | + self.so_version = os_elm[1]['version'] | ||
| 37 | + self.accuracy = os_elm[1]['accuracy'] | ||
| 24 | \ No newline at end of file | 38 | \ No newline at end of file |
cocar/model/host.py
| @@ -2,12 +2,23 @@ | @@ -2,12 +2,23 @@ | ||
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | __author__ = 'eduardo' | 3 | __author__ = 'eduardo' |
| 4 | from netaddr import IPAddress | 4 | from netaddr import IPAddress |
| 5 | +from sqlalchemy.schema import Column | ||
| 6 | +from sqlalchemy.types import String, Integer | ||
| 7 | +from . import Base | ||
| 5 | 8 | ||
| 6 | 9 | ||
| 7 | -class Host(object): | 10 | +class Host(Base): |
| 8 | """ | 11 | """ |
| 9 | Classe que define um ativo de rede | 12 | Classe que define um ativo de rede |
| 10 | """ | 13 | """ |
| 14 | + __tablename__ = 'host' | ||
| 15 | + network_ip = Column(String(16), primary_key=True, nullable=False) | ||
| 16 | + mac_address = Column(String(18)) | ||
| 17 | + name = Column(String) | ||
| 18 | + inclusion_date = Column(String(20)) | ||
| 19 | + scantime = Column(Integer) | ||
| 20 | + ports = Column(String) | ||
| 21 | + | ||
| 11 | def __init__(self, | 22 | def __init__(self, |
| 12 | ip_address, | 23 | ip_address, |
| 13 | mac_address=None, | 24 | mac_address=None, |
| @@ -32,4 +43,25 @@ class Host(object): | @@ -32,4 +43,25 @@ class Host(object): | ||
| 32 | self.hostname = hostname | 43 | self.hostname = hostname |
| 33 | self.inclusion_date = inclusion_date | 44 | self.inclusion_date = inclusion_date |
| 34 | self.scantime = scantime | 45 | self.scantime = scantime |
| 35 | - self.open_ports = open_ports | ||
| 36 | \ No newline at end of file | 46 | \ No newline at end of file |
| 47 | + self.open_ports = open_ports | ||
| 48 | + | ||
| 49 | + # Parâmetros do SQLAlchemy | ||
| 50 | + self.network_ip = str(self.ip_address) | ||
| 51 | + self.ports = ','.join(map(str, self.open_ports.keys())) | ||
| 52 | + if len(self.hostname.values()) > 0: | ||
| 53 | + self.name = self.hostname.values()[0] | ||
| 54 | + else: | ||
| 55 | + self.name = None | ||
| 56 | + | ||
| 57 | + def __repr__(self): | ||
| 58 | + """ | ||
| 59 | + Metodo que passa a lista de parametros da classe | ||
| 60 | + """ | ||
| 61 | + return "<Host('%s, %s, %s, %s, %s, %s')>" % ( | ||
| 62 | + self.network_ip, | ||
| 63 | + self.mac_address, | ||
| 64 | + self.name, | ||
| 65 | + self.inclusion_date, | ||
| 66 | + self.scantime, | ||
| 67 | + self.ports | ||
| 68 | + ) | ||
| 37 | \ No newline at end of file | 69 | \ No newline at end of file |
cocar/model/network.py
| @@ -4,14 +4,25 @@ __author__ = 'eduardo' | @@ -4,14 +4,25 @@ __author__ = 'eduardo' | ||
| 4 | import os.path | 4 | import os.path |
| 5 | from .. import Cocar | 5 | from .. import Cocar |
| 6 | from netaddr import IPNetwork, IPSet | 6 | from netaddr import IPNetwork, IPSet |
| 7 | +from ..model import Base | ||
| 8 | +from sqlalchemy.schema import Column | ||
| 9 | +from sqlalchemy.types import String, Integer | ||
| 7 | 10 | ||
| 8 | 11 | ||
| 9 | -class Network(Cocar): | 12 | +class Network(Base): |
| 10 | """ | 13 | """ |
| 11 | Rede onde a busca será realizada | 14 | Rede onde a busca será realizada |
| 12 | """ | 15 | """ |
| 16 | + __tablename__ = 'network' | ||
| 17 | + ip_network = Column(String(16), nullable=False, primary_key=True) | ||
| 18 | + network_file = Column(String) | ||
| 19 | + netmask = Column(String(16)) | ||
| 20 | + prefixlen = Column(Integer) | ||
| 21 | + name = Column(String) | ||
| 22 | + | ||
| 13 | def __init__(self, | 23 | def __init__(self, |
| 14 | network_ip, | 24 | network_ip, |
| 25 | + network_file=None, | ||
| 15 | netmask=None, | 26 | netmask=None, |
| 16 | prefixlen=None, | 27 | prefixlen=None, |
| 17 | name=None | 28 | name=None |
| @@ -22,17 +33,19 @@ class Network(Cocar): | @@ -22,17 +33,19 @@ class Network(Cocar): | ||
| 22 | :param cidr: CIDR para calcular a máscara da rede | 33 | :param cidr: CIDR para calcular a máscara da rede |
| 23 | :param name: Nome da rede | 34 | :param name: Nome da rede |
| 24 | """ | 35 | """ |
| 25 | - Cocar.__init__(self) | ||
| 26 | self.network_ip = IPNetwork(network_ip) | 36 | self.network_ip = IPNetwork(network_ip) |
| 27 | self.netmask = netmask | 37 | self.netmask = netmask |
| 28 | self.prefixlen = prefixlen | 38 | self.prefixlen = prefixlen |
| 29 | self.name = name | 39 | self.name = name |
| 30 | - self.network_file = self.cocar_data_dir + "/" + str(self.network_ip.ip) + ".xml" | 40 | + self.network_file = network_file |
| 31 | if self.netmask is None: | 41 | if self.netmask is None: |
| 32 | self.netmask = self.network_ip.netmask | 42 | self.netmask = self.network_ip.netmask |
| 33 | if self.prefixlen is None: | 43 | if self.prefixlen is None: |
| 34 | self.prefixlen = self.network_ip.prefixlen | 44 | self.prefixlen = self.network_ip.prefixlen |
| 35 | 45 | ||
| 46 | + # SQLAlchemy attribute | ||
| 47 | + self.ip_network = str(self.network_ip.ip) | ||
| 48 | + | ||
| 36 | def ip_list(self): | 49 | def ip_list(self): |
| 37 | """ | 50 | """ |
| 38 | Método que encontra a lista de IP's da subrede | 51 | Método que encontra a lista de IP's da subrede |
cocar/model/printer.py
| @@ -3,16 +3,26 @@ | @@ -3,16 +3,26 @@ | ||
| 3 | __author__ = 'eduardo' | 3 | __author__ = 'eduardo' |
| 4 | 4 | ||
| 5 | from .host import Host | 5 | from .host import Host |
| 6 | +from sqlalchemy import ForeignKey | ||
| 7 | +from sqlalchemy.schema import Column | ||
| 8 | +from sqlalchemy.types import String, Integer | ||
| 6 | 9 | ||
| 7 | 10 | ||
| 8 | class Printer(Host): | 11 | class Printer(Host): |
| 9 | """ | 12 | """ |
| 10 | Classe que identifica uma impressora | 13 | Classe que identifica uma impressora |
| 11 | """ | 14 | """ |
| 15 | + __tablename__ = 'printer' | ||
| 16 | + network_ip = Column(String(16), ForeignKey("host.network_ip"), nullable=False, primary_key=True) | ||
| 17 | + counter = Column(Integer) | ||
| 18 | + serial = Column(String(50)) | ||
| 19 | + description = Column(String) | ||
| 20 | + | ||
| 12 | def __init__(self, | 21 | def __init__(self, |
| 13 | counter=None, | 22 | counter=None, |
| 14 | model=None, | 23 | model=None, |
| 15 | serial=None, | 24 | serial=None, |
| 25 | + description=None, | ||
| 16 | *args, | 26 | *args, |
| 17 | **kwargs | 27 | **kwargs |
| 18 | ): | 28 | ): |
| @@ -24,4 +34,5 @@ class Printer(Host): | @@ -24,4 +34,5 @@ class Printer(Host): | ||
| 24 | Host.__init__(self, *args, **kwargs) | 34 | Host.__init__(self, *args, **kwargs) |
| 25 | self.counter = counter | 35 | self.counter = counter |
| 26 | self.model = model | 36 | self.model = model |
| 27 | - self.serial = serial | ||
| 28 | \ No newline at end of file | 37 | \ No newline at end of file |
| 38 | + self.serial = serial | ||
| 39 | + self.description = description | ||
| 29 | \ No newline at end of file | 40 | \ No newline at end of file |
cocar/tests/__init__.py
| @@ -4,6 +4,7 @@ __author__ = 'eduardo' | @@ -4,6 +4,7 @@ __author__ = 'eduardo' | ||
| 4 | from .. import Cocar | 4 | from .. import Cocar |
| 5 | import os | 5 | import os |
| 6 | import os.path | 6 | import os.path |
| 7 | +from ..model import Base | ||
| 7 | 8 | ||
| 8 | cocar = Cocar(environment='test') | 9 | cocar = Cocar(environment='test') |
| 9 | test_dir = os.path.dirname(os.path.realpath(__file__)) | 10 | test_dir = os.path.dirname(os.path.realpath(__file__)) |
| @@ -13,7 +14,7 @@ def setup_package(): | @@ -13,7 +14,7 @@ def setup_package(): | ||
| 13 | """ | 14 | """ |
| 14 | Setup test data for the package | 15 | Setup test data for the package |
| 15 | """ | 16 | """ |
| 16 | - cocar.Base.metadata.create_all(cocar.engine) | 17 | + Base.metadata.create_all(cocar.engine) |
| 17 | pass | 18 | pass |
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -21,5 +22,5 @@ def teardown_package(): | @@ -21,5 +22,5 @@ def teardown_package(): | ||
| 21 | """ | 22 | """ |
| 22 | Remove test data | 23 | Remove test data |
| 23 | """ | 24 | """ |
| 24 | - cocar.Base.metadata.drop_all(cocar.engine) | 25 | + Base.metadata.drop_all(cocar.engine) |
| 25 | pass | 26 | pass |
| 26 | \ No newline at end of file | 27 | \ No newline at end of file |
cocar/tests/test_discover.py
| @@ -74,7 +74,7 @@ class TestDiscover(unittest.TestCase): | @@ -74,7 +74,7 @@ class TestDiscover(unittest.TestCase): | ||
| 74 | self.assertTrue(os.path.isfile(session.outfile)) | 74 | self.assertTrue(os.path.isfile(session.outfile)) |
| 75 | 75 | ||
| 76 | # Apaga arquivo | 76 | # Apaga arquivo |
| 77 | - os.unlink(session.outfile) | 77 | + #os.unlink(session.outfile) |
| 78 | 78 | ||
| 79 | def test_scan_rede(self): | 79 | def test_scan_rede(self): |
| 80 | """ | 80 | """ |
cocar/tests/test_identify.py
| @@ -60,8 +60,9 @@ class TestIdentify(unittest.TestCase): | @@ -60,8 +60,9 @@ class TestIdentify(unittest.TestCase): | ||
| 60 | self.assertIsInstance(computer, Computer) | 60 | self.assertIsInstance(computer, Computer) |
| 61 | 61 | ||
| 62 | # Se é um computer, tenho que identificar o SO | 62 | # Se é um computer, tenho que identificar o SO |
| 63 | - os_elm = computer.so.items()[0] | ||
| 64 | - self.assertEqual(os_elm[1]['osfamily'], 'Linux') | 63 | + self.assertEqual(computer.so_name, 'Linux') |
| 64 | + self.assertEqual(computer.so_version, 'Linux 3.7 - 3.9') | ||
| 65 | + self.assertEqual(computer.accuracy, '98') | ||
| 65 | 66 | ||
| 66 | def test_identify_printer(self): | 67 | def test_identify_printer(self): |
| 67 | """ | 68 | """ |
cocar/tests/test_persistence.py
| @@ -6,6 +6,8 @@ import unittest | @@ -6,6 +6,8 @@ import unittest | ||
| 6 | import cocar.tests | 6 | import cocar.tests |
| 7 | from ..xml_utils import NmapXML | 7 | from ..xml_utils import NmapXML |
| 8 | from ..model.computer import Computer | 8 | from ..model.computer import Computer |
| 9 | +from ..model.printer import Printer | ||
| 10 | +from ..model.network import Network | ||
| 9 | 11 | ||
| 10 | 12 | ||
| 11 | class TestPersistence(unittest.TestCase): | 13 | class TestPersistence(unittest.TestCase): |
| @@ -20,12 +22,13 @@ class TestPersistence(unittest.TestCase): | @@ -20,12 +22,13 @@ class TestPersistence(unittest.TestCase): | ||
| 20 | self.network_file = cocar.tests.test_dir + "/fixtures/192.168.0.0-24.xml" | 22 | self.network_file = cocar.tests.test_dir + "/fixtures/192.168.0.0-24.xml" |
| 21 | self.localhost_file = cocar.tests.test_dir + "/fixtures/127.0.0.1.xml" | 23 | self.localhost_file = cocar.tests.test_dir + "/fixtures/127.0.0.1.xml" |
| 22 | self.printer_file = cocar.tests.test_dir + "/fixtures/printer.xml" | 24 | self.printer_file = cocar.tests.test_dir + "/fixtures/printer.xml" |
| 25 | + self.session = cocar.tests.cocar.Session | ||
| 23 | 26 | ||
| 24 | def test_connect(self): | 27 | def test_connect(self): |
| 25 | """ | 28 | """ |
| 26 | Testa conexão do SQLAlchemy | 29 | Testa conexão do SQLAlchemy |
| 27 | """ | 30 | """ |
| 28 | - db_session = cocar.tests.cocar.session | 31 | + db_session = self.session |
| 29 | self.assertIsNotNone(db_session) | 32 | self.assertIsNotNone(db_session) |
| 30 | 33 | ||
| 31 | def test_persist_computer(self): | 34 | def test_persist_computer(self): |
| @@ -40,8 +43,54 @@ class TestPersistence(unittest.TestCase): | @@ -40,8 +43,54 @@ class TestPersistence(unittest.TestCase): | ||
| 40 | computer = nmap_xml.identify_host(hostname) | 43 | computer = nmap_xml.identify_host(hostname) |
| 41 | self.assertIsInstance(computer, Computer) | 44 | self.assertIsInstance(computer, Computer) |
| 42 | 45 | ||
| 46 | + # Agora testa a persistência | ||
| 47 | + self.session.add(computer) | ||
| 48 | + self.session.flush() | ||
| 49 | + | ||
| 50 | + # Tenta ver se gravou | ||
| 51 | + results = self.session.query(Computer).first() | ||
| 52 | + self.assertIsNotNone(results) | ||
| 53 | + | ||
| 54 | + def test_persist_printer(self): | ||
| 55 | + """ | ||
| 56 | + Grava impressora no banco de dados | ||
| 57 | + """ | ||
| 58 | + hostname = '10.72.168.3' | ||
| 59 | + nmap_xml = NmapXML(self.printer_file) | ||
| 60 | + host = nmap_xml.parse_xml() | ||
| 61 | + assert host | ||
| 62 | + | ||
| 63 | + printer = nmap_xml.identify_host(hostname) | ||
| 64 | + self.assertIsInstance(printer, Printer) | ||
| 65 | + | ||
| 66 | + # Agora testa a persistência | ||
| 67 | + self.session.add(printer) | ||
| 68 | + self.session.flush() | ||
| 69 | + | ||
| 70 | + # Tenta ver se gravou | ||
| 71 | + results = self.session.query(Printer).first() | ||
| 72 | + self.assertIsNotNone(results) | ||
| 73 | + | ||
| 74 | + def test_persist_network(self): | ||
| 75 | + """ | ||
| 76 | + Testa gravação dos dados de rede | ||
| 77 | + """ | ||
| 78 | + rede = Network( | ||
| 79 | + network_ip='192.168.0.0', | ||
| 80 | + netmask='255.255.255.0', | ||
| 81 | + network_file='/tmp/network.xml', | ||
| 82 | + name='Rede de Teste' | ||
| 83 | + ) | ||
| 84 | + self.session.add(rede) | ||
| 85 | + self.session.flush() | ||
| 86 | + | ||
| 87 | + # Tenta ver se gravou | ||
| 88 | + results = self.session.query(Network).first() | ||
| 89 | + self.assertIsNotNone(results) | ||
| 90 | + | ||
| 43 | def tearDown(self): | 91 | def tearDown(self): |
| 44 | """ | 92 | """ |
| 45 | Remove dados | 93 | Remove dados |
| 46 | """ | 94 | """ |
| 95 | + self.session.close() | ||
| 47 | pass | 96 | pass |
| 48 | \ No newline at end of file | 97 | \ No newline at end of file |
cocar/xml_utils.py
| @@ -69,7 +69,8 @@ class NmapXML(object): | @@ -69,7 +69,8 @@ class NmapXML(object): | ||
| 69 | 'vendor': osclass.get('vendor'), | 69 | 'vendor': osclass.get('vendor'), |
| 70 | 'osfamily': osclass.get('osfamily'), | 70 | 'osfamily': osclass.get('osfamily'), |
| 71 | 'accuracy': osclass.get('accuracy'), | 71 | 'accuracy': osclass.get('accuracy'), |
| 72 | - 'cpe': osclass.findtext('cpe') | 72 | + 'cpe': osclass.findtext('cpe'), |
| 73 | + 'version': osmatch.get('name') | ||
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | # General attributes | 76 | # General attributes |
| @@ -86,11 +87,12 @@ class NmapXML(object): | @@ -86,11 +87,12 @@ class NmapXML(object): | ||
| 86 | 87 | ||
| 87 | # Ordena os sistemas operacionais por accuracy | 88 | # Ordena os sistemas operacionais por accuracy |
| 88 | host = self.hosts[hostname] | 89 | host = self.hosts[hostname] |
| 89 | - accuracy = 0 | 90 | + accuracy = int(0) |
| 90 | if host.get('os'): | 91 | if host.get('os'): |
| 91 | # Nesse caso já sei que é computador. Precisa identificar o OS | 92 | # Nesse caso já sei que é computador. Precisa identificar o OS |
| 92 | for os in host['os'].keys(): | 93 | for os in host['os'].keys(): |
| 93 | if int(host['os'][os]['accuracy']) > accuracy: | 94 | if int(host['os'][os]['accuracy']) > accuracy: |
| 95 | + accuracy = int(host['os'][os]['accuracy']) | ||
| 94 | os_final = os | 96 | os_final = os |
| 95 | 97 | ||
| 96 | scantime = int(host.get('endtime')) - int(host.get('starttime')) | 98 | scantime = int(host.get('endtime')) - int(host.get('starttime')) |