forked from ungleich-public/cdist
		
	[scanner] add to beta commands
This commit is contained in:
		
					parent
					
						
							
								91d99bf08a
							
						
					
				
			
			
				commit
				
					
						09dfcfe81e
					
				
			
		
					 4 changed files with 124 additions and 6 deletions
				
			
		|  | @ -8,10 +8,11 @@ import cdist.configuration | |||
| import cdist.log | ||||
| import cdist.preos | ||||
| import cdist.info | ||||
| import cdist.scan.commandline | ||||
| 
 | ||||
| 
 | ||||
| # set of beta sub-commands | ||||
| BETA_COMMANDS = set(('install', 'inventory', )) | ||||
| BETA_COMMANDS = set(('install', 'inventory', 'scan', )) | ||||
| # set of beta arguments for sub-commands | ||||
| BETA_ARGS = { | ||||
|     'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )), | ||||
|  | @ -470,6 +471,35 @@ def get_parsers(): | |||
|             'pattern', nargs='?', help='Glob pattern.') | ||||
|     parser['info'].set_defaults(func=cdist.info.Info.commandline) | ||||
| 
 | ||||
|     # Scan = config + further | ||||
|     parser['scan'] = parser['sub'].add_parser('scan', add_help=False, | ||||
|                                                  parents=[parser['config']]) | ||||
| 
 | ||||
|     parser['scan'] = parser['sub'].add_parser( | ||||
|             'scan', parents=[parser['loglevel'], | ||||
|                              parser['beta'], | ||||
|                              parser['colored_output'], | ||||
|                              parser['common'], | ||||
|                              parser['config_main']]) | ||||
| 
 | ||||
|     parser['scan'].add_argument( | ||||
|         '-m', '--mode', help='Which modes should run', | ||||
|         action='append', default=[], | ||||
|         choices=['scan', 'trigger']) | ||||
|     parser['scan'].add_argument( | ||||
|         '--config', | ||||
|         action='store_true', | ||||
|         help='Try to configure detected hosts') | ||||
|     parser['scan'].add_argument( | ||||
|         '-I', '--interfaces', | ||||
|         action='append',  default=[], | ||||
|         help='On which interfaces to scan/trigger') | ||||
|     parser['scan'].add_argument( | ||||
|         '-d', '--delay', | ||||
|         action='store',  default=3600, | ||||
|         help='How long to wait before reconfiguring after last try') | ||||
|     parser['scan'].set_defaults(func=cdist.scan.commandline.commandline) | ||||
| 
 | ||||
|     for p in parser: | ||||
|         parser[p].epilog = EPILOG | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										55
									
								
								cdist/scan/commandline.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								cdist/scan/commandline.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # 2020 Nico Schottelius (nico-cdist at schottelius.org) | ||||
| # | ||||
| # 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 logging | ||||
| 
 | ||||
| log = logging.getLogger("scan") | ||||
| 
 | ||||
| 
 | ||||
| # define this outside of the class to not handle scapy import errors by default | ||||
| def commandline(args): | ||||
|     log.debug(args) | ||||
| 
 | ||||
|     try: | ||||
|         import cdist.scan.scan as scan | ||||
|     except ModuleNotFoundError: | ||||
|         print('cdist scan requires scapy to be installed') | ||||
| 
 | ||||
|     processes = [] | ||||
| 
 | ||||
|     if not args.mode: | ||||
|         # By default scan and trigger, but do not call any action | ||||
|         args.mode = ['scan', 'trigger' ] | ||||
| 
 | ||||
|     if 'trigger' in args.mode: | ||||
|         t = scan.Trigger(interfaces=args.interfaces) | ||||
|         t.start() | ||||
|         processes.append(t) | ||||
|         log.debug("Trigger started") | ||||
| 
 | ||||
|     if 'scan' in args.mode: | ||||
|         s = scan.Scanner(interfaces=args.interfaces, args=args) | ||||
|         s.start() | ||||
|         processes.append(s) | ||||
|         log.debug("Scanner started") | ||||
| 
 | ||||
|     for process in processes: | ||||
|         process.join() | ||||
|  | @ -50,13 +50,14 @@ | |||
| 
 | ||||
| from multiprocessing import Process | ||||
| import os | ||||
| 
 | ||||
| # FIXME: fail gracefully if non existent - i.e. "scapy required for scanner - please install python3-scapy" | ||||
| import logging | ||||
| from scapy.all import * | ||||
| 
 | ||||
| # Datetime overwrites scapy.all.datetime - needs to be imported AFTER | ||||
| import datetime | ||||
| 
 | ||||
| log = logging.getLogger("scan") | ||||
| 
 | ||||
| class Trigger(object): | ||||
|     """ | ||||
|     Trigger an ICMPv6EchoReply from all hosts that are alive | ||||
|  | @ -87,6 +88,7 @@ class Trigger(object): | |||
| 
 | ||||
|     def trigger(self, interface): | ||||
|         packet = IPv6(dst=f"ff02::1%{interface}") / ICMPv6EchoRequest() | ||||
|         log.debug(f"Sending request on {interface}") | ||||
|         send(packet, verbose=self.verbose) | ||||
| 
 | ||||
| class Scanner(object): | ||||
|  | @ -94,18 +96,18 @@ class Scanner(object): | |||
|     Scan for replies of hosts, maintain the up-to-date database | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, interfaces=None, outdir=None): | ||||
|     def __init__(self, interfaces=None, args=None, outdir=None): | ||||
|         self.interfaces = interfaces | ||||
| 
 | ||||
|         if outdir: | ||||
|             self.outdir = outdir | ||||
|         else: | ||||
|             self.outdir = "." | ||||
|             self.outdir = os.path.join(os.environ['HOME'], '.cdist', 'scan') | ||||
| 
 | ||||
|     def handle_pkg(self, pkg): | ||||
|         if ICMPv6EchoReply in pkg: | ||||
|             host = pkg['IPv6'].src | ||||
|             print(f"Host {host} is alive") | ||||
|             log.verbose(f"Host {host} is alive") | ||||
| 
 | ||||
|             dir = os.path.join(self.outdir, host) | ||||
|             fname = os.path.join(dir, "last_seen") | ||||
|  | @ -118,13 +120,21 @@ class Scanner(object): | |||
|             with open(fname, "w") as fd: | ||||
|                 fd.write(f"{now}\n") | ||||
| 
 | ||||
|     def start(self): | ||||
|         self.process = Process(target=self.scan) | ||||
|         self.process.start() | ||||
| 
 | ||||
|     def join(self): | ||||
|         self.process.join() | ||||
| 
 | ||||
|     def scan(self): | ||||
|         log.debug("Scanning - zzzzz") | ||||
|         sniff(iface=self.interfaces, | ||||
|               filter="icmp6", | ||||
|               prn=self.handle_pkg) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     t = Trigger(interfaces=["wlan0"]) | ||||
|     t.start() | ||||
|  | @ -32,3 +32,26 @@ | |||
|      - Record when configured (successfully) | ||||
|      - Record when seen | ||||
|    - Enables configurations in stateless environments | ||||
| ** Sample output v2020-10-29 | ||||
| 23:14] bridge:~% sudo  cdist scan -b -I wlan0 -vv | ||||
| VERBOSE: cdist: version 6.8.0-36-g91d99bf0 | ||||
| VERBOSE: scan: Host fe80::21d:72ff:fe86:46b is alive | ||||
| VERBOSE: scan: Host fe80::ce2d:e0ff:fed4:2611 is alive | ||||
| VERBOSE: scan: Host fe80::21b:fcff:feee:f4c1 is alive | ||||
| VERBOSE: scan: Host fe80::e2ff:f7ff:fe00:20e6 is alive | ||||
| VERBOSE: scan: Host fe80::20d:b9ff:fe49:ac11 is alive | ||||
| VERBOSE: scan: Host fe80::9e93:4eff:fe6c:c1f4 is alive | ||||
| VERBOSE: scan: Host fe80::ce32:e5ff:fe79:7ea7 is alive | ||||
| VERBOSE: scan: Host fe80::219:d2ff:feb2:2e12 is alive | ||||
| VERBOSE: scan: Host fe80::d66d:6dff:fe33:e00 is alive | ||||
| VERBOSE: scan: Host fe80::21b:fcff:feee:f446 is alive | ||||
| VERBOSE: scan: Host fe80::21b:fcff:feee:f4b1 is alive | ||||
| VERBOSE: scan: Host fe80::20d:b9ff:fe4c:547d is alive | ||||
| VERBOSE: scan: Host fe80::bad8:12ff:fe65:313d is alive | ||||
| VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f6f0 is alive | ||||
| VERBOSE: scan: Host fe80::ba69:f4ff:fec5:6041 is alive | ||||
| VERBOSE: scan: Host fe80::f29f:c2ff:fe7c:275e is alive | ||||
| VERBOSE: scan: Host fe80::ba69:f4ff:fec5:8db7 is alive | ||||
| VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f863 is alive | ||||
| VERBOSE: scan: Host fe80::21b:fcff:feee:f4bc is alive | ||||
| ... | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue