def get_connection_status(): """Return the eth & wlan connection status of the specified probe, The only argument is mac, which should be the probe's MAC address Returned statuses will be either: invalid-mac unknown-mac {"eth0": 0 or 1, "wlan0": 1 or 0} For backwards compatibility: connected """ mac = request.args.get('mac', '') if mac == '': return 'invalid-mac' mac = util.convert_mac(mac, 'storage') probe = database.get_probe(mac) if probe is None: return 'unknown-mac' status = util.is_probe_connected(probe.port) if status: con_stat = util.get_interface_connection_status(probe.port) if con_stat is not None: return con_stat else: return 'connected' return '{"eth0": 0, "wlan0": 0}'
def get_port(): """Returns the port number associated with the supplied MAC. The port will be used to construct a SSH tunnel The function can give the following responses (with explanation): invalid-mac : The supplied MAC was invalid (in format) unknown-mac : The MAC was valid, but is not in the database no-registered-key : No SSH key has been associated with this MAC, and therefore no port will be sent <port> : Returns the queried port (a valid MAC was received) """ mac = request.args.get('mac', '') if not util.is_mac_valid(mac): return 'invalid-mac' mac = util.convert_mac(mac, mode='storage') probe = database.get_probe(mac) if probe is None: return 'unknown-mac' if probe.pub_key == '' or probe.host_key == '': return 'no-registered-key' return str(probe.port)
def update_probe(self, current_probe_id, name=None, new_custom_id=None, location=None): """Update 'current_probe_id' with new attributes""" probe = self.get_probe(current_probe_id) if probe is None: return False conv_curr = util.convert_mac(current_probe_id, mode='storage') conv_new = util.convert_mac(new_custom_id, mode='storage') if not conv_curr == conv_new: if self.is_valid_id(new_custom_id): probe.custom_id = util.convert_mac(new_custom_id, mode='storage') else: return False if self.is_valid_string(name): probe.name = name if self.is_valid_string(location): probe.location = location return True
def is_valid_id(self, probe_id): """Return true if 'probe_id' is a valid MAC address for use as a probe id.""" if not self.is_valid_string(probe_id): return False if not util.is_mac_valid(probe_id): return False probe_id = util.convert_mac(probe_id, mode='storage') is_unused = len(self.session.query(Probe.custom_id).filter(Probe.custom_id == probe_id).all()) == 0 return is_unused
def probe_setup(): """Render page for adding/editing probe data. Also parse data on POST. More specifically, the data that can be changed are: - Basic probe info (name, MAC and location) - Script configurations (interval and enabled/disabled for each script) - Network configration (SSID, anon id, username, password, cert) """ probe_id = request.args.get('id', '') if probe_id == '': flash('No probe ID specified') abort(404) probe_id = util.convert_mac(probe_id, mode='storage') probe = database.get_probe(probe_id) if probe is None or probe.user.username != current_user.username: flash('Unknown probe ID') abort(404) if request.method == 'POST': successful_script_update = form_parsers.update_scripts() successful_network_update = form_parsers.update_network_configs() successful_certificate_upload = form_parsers.upload_certificate( probe_id, current_user.username) successful_probe_update = form_parsers.update_probe(probe_id) if (successful_script_update and successful_probe_update and successful_network_update and successful_certificate_upload): database.save_changes() action = request.form.get('action', '') if action == 'save_as_default': ansible.export_group_config( current_user.username, {'group_script_configs': database.get_script_data(probe)}, 'script_configs') ansible.export_group_config( current_user.username, {'networks': database.get_network_config_data(probe)}, 'network_configs') ansible.make_certificate_default(probe_id, current_user.username) return redirect(url_for('probes')) else: database.revert_changes() return generate_probe_setup_template(probe_id, current_user.username)
def valid_network_configs(self, probe, with_warning=False): """Returns true if 'probe's network config(s) has been filled out""" success = True for net_conf in probe.network_configs: # For now we just check for any (not two_g & and five_g), because # we don't use the two_g and five_g at the moment if net_conf.name == 'any' and not net_conf.is_filled(): if with_warning: message = messages.ERROR_MESSAGE['fill_out_network_credentials'].format( str(probe.name) + ' / ' + util.convert_mac(probe.custom_id, mode='display')) flash(message, 'error') success = False return success
def remove_probe(self, username, probe_custom_id): """Remove the probe with id 'probe_custom_id', as long as it belongs to 'username'""" probe = self.get_probe(probe_custom_id) if probe is None or self.get_user(username).id != probe.user_id: flash('Invalid probe ID', 'error') return False probe_id = util.convert_mac(probe_custom_id, mode='storage') ansible.remove_host_cert(probe_id) if probe is not None: self.session.delete(probe) return True
def get_probe_data(self, probe_id): """Return a dictionary containing all saved data about 'probe_id'""" probe = self.get_probe(probe_id) data = { 'name': probe.name, 'id': util.convert_mac(probe.custom_id, mode='display'), 'storage_id': probe.custom_id, 'location': probe.location, 'scripts': self.get_script_data(probe), 'network_configs': self.get_network_config_data(probe), 'associated': probe.associated, 'association_period_expired': probe.association_period_expired() } return data
def register_key(): """Takes an SSH public key, host key and a MAC, and registers it to the corresponding probe if a probe with that MAC has been added through the website. The function can give the following responses (with explanation): invalid-mac : The supplied MAC was invalid (in form) unknown-mac : The MAC was valid, but is not in the database invalid-pub-key : The pub key was invalid (in form) invalid-host-key : The host key was invalid (in form) already-registered : There already exists keys associated with this MAC assocation-period-expired : The assocation period has expired and needs to be renewed through the web site success : The keys were successfully registered/associated with the corresponding MAC address """ mac = request.form.get('mac', '') if not util.is_mac_valid(mac): return 'invalid-mac' mac = util.convert_mac(mac, mode='storage') probe = database.get_probe(mac) if probe is None: return 'unknown-mac' pub_key = request.form.get('pub_key', '') host_key = request.form.get('host_key', '') if pub_key == '' or not util.is_pub_ssh_key_valid(pub_key): return 'invalid-pub-key' if host_key == '' or not util.is_ssh_host_key_valid(host_key): return 'invalid-host-key' if probe.pub_key != '' or probe.host_key != '': return 'already-registered' if probe.association_period_expired(): return 'association-period-expired' probe.set_pub_key(pub_key) probe.set_host_key(host_key) probe.associated = True database.save_changes() ansible.export_known_hosts(database) return 'success'
def get_ansible_status(): """Return the status on the current ansible update The only argument is mac, which should be the probe's MAC address Returned statuses will be either: invalid-mac unknown-mac updating failed not-updated updated-{time of last update} """ mac = request.args.get('mac', '') if mac == '': return 'invalid-mac' mac = util.convert_mac(mac, 'storage') probe = database.get_probe(mac) if probe is None: return 'unknown-mac' status = ansible.get_playbook_status(current_user.username, probe) if status in ['updating', 'failed']: return status if status == 'completed': probe.has_been_updated = True if (probe.last_updated is None or (datetime.today() - probe.last_updated).total_seconds() >= 60): probe.last_updated = datetime.today() database.save_changes() if status == 'completed' or probe.has_been_updated: if probe.last_updated is None: probe.last_updated = datetime.today() database.save_changes() time = util.get_textual_timedelta(datetime.today() - probe.last_updated) return 'updated-{}'.format(time) return 'not-updated'
def add_probe(self, username, probe_name, custom_id, location=None, scripts=None, network_configs=None): """Create a probe instance, add it to the database session, and return it to the caller""" if not self.is_valid_id(custom_id): return None port = self.generate_probe_port() if port == -1: print('Error generating port number. The port space may be ' 'exhausted (though that is unlikely)') return None probe = Probe(probe_name, util.convert_mac(custom_id, mode='storage'), location, port) user = self.get_user(username) user.probes.append(probe) if scripts is None: self.load_default_scripts(probe, username) if network_configs is None: self.load_default_network_configs(probe, username) return probe
def get_probe(self, probe_id): """Return the Probe class instance with the custom_id/MAC 'probe_id'""" probe_id = util.convert_mac(probe_id, mode='storage') return self.session.query(Probe).filter(Probe.custom_id == probe_id).first()
def remove_host_config(probe_id): """Remove all Ansible configs associated with 'probe_id'""" probe_id = util.convert_mac(probe_id, mode='storage') dir_path = os.path.join(settings.ANSIBLE_PATH, 'host_vars', probe_id) if os.path.isdir(dir_path): shutil.rmtree(dir_path)
def export_host_config(probe_id, data, filename): """Export 'data' as a host/probe specific YAML config file under the name 'filename' for the probe with 'probe_id' as custom id (a MAC)""" probe_id = util.convert_mac(probe_id, mode='storage') dir_path = os.path.join(settings.ANSIBLE_PATH, 'host_vars', probe_id) _write_config(dir_path, data, filename)