def download_install(self, version, load_config=None, sync=False): if isstring(version): version = PanOSVersion(version) # Get list of software if needed if not self.versions: self.check() # Get versions as StrictVersion objects available_versions = map(PanOSVersion, self.versions.keys()) target_version = PanOSVersion(str(version)) current_version = PanOSVersion(self.pandevice.version) if str(target_version) not in available_versions: raise err.PanDeviceError("Error upgrading to unknown version: %s" % target_version) # Check if already on the target version if current_version == target_version: raise err.PanDeviceError( "Requested upgrade to version %s which is already running on device %s" % (target_version, self.pandevice.id)) # Download the software upgrade if not self.versions[str(target_version)]["downloaded"]: self.download(target_version, sync=True) # Install the software upgrade result = self.install(target_version, load_config=load_config, sync=sync) return result
def history(self, count=100, direction="backward", skip=None): """Returns a chunk of historical audit comment logs. Args: count (int): Number of audit comments to return, maximum 5000. direction (str): Specify whether logs are shown oldest first (``forward``) or newest first (``backward``). skip (int): Specify the number of logs to skip when doing log retrieval. This is useful when retrieving logs in batches where you can skip the previously retrieved logs. Returns: list of :class:`panos.policies.AuditCommentLog` """ dev = self.obj.nearest_pandevice() # Build up the query. query = "(subtype eq audit-comment)" # Add in the name. query += " and (path contains '\\'{0}\\'')".format(self.obj.uid) # Add in the rule type. query += " and (path contains '{0}')".format( self.obj.xpath_short().split("/")[-2], ) # Add in the rulebase type. p = self.obj.parent if p is None: raise err.PanDeviceError("rule has empty parent") if not any( isinstance(p, x) for x in (Rulebase, PreRulebase, PostRulebase)): raise err.PanDeviceError("{0} has non-rulebase parent".format( self.obj.uid)) query += " and (path contains {0})".format(p.xpath().split("/")[-1]) # Add in the vsys or device group. query += " and (path contains '\\'{0}\\'')".format(p.vsys) extra_qs = { "dir": direction, "uniq": "yes", } if skip is not None: extra_qs["skip"] = "{0}".format(int(skip)) resp = dev.xapi.log("config", count, filter=query, extra_qs=extra_qs) return [ AuditCommentLog(x) for x in resp.findall("./result/log/logs/entry") ]
def install(self, version="latest", sync_to_peer=True, skip_commit=False, sync=False): if not self.versions: self.check() available_versions = map(PanOSVersion, self.versions.keys()) latest_version = max(available_versions) if self.versions[str(latest_version)]["current"]: return self._logger.info("Device %s installing content version: %s" % (self.pandevice.id, version)) op = ( 'request content upgrade install commit "%s" sync-to-peer "%s" version "%s"' % ("no" if skip_commit else "yes", "yes" if sync_to_peer else "no", version)) response = self._op(op) if sync: result = self.pandevice.syncjob(response) if not result["success"]: raise err.PanDeviceError( "Device %s attempt to install content version %s failed: %s" % (self.pandevice.id, version, result["messages"])) return result else: return True
def download(self, sync_to_peer=None, sync=False): if not self.versions: self.check() available_versions = map(PanOSVersion, self.versions.keys()) latest_version = max(available_versions) if self.versions[str(latest_version)]["downloaded"]: return self._logger.info("Device %s downloading content version: %s" % (self.pandevice.id, latest_version)) if sync_to_peer is None: sync_to_peer_text = "" elif sync_to_peer: sync_to_peer_text = ' "" sync-to-peer "yes"' else: sync_to_peer_text = ' "" sync-to-peer "no"' command = "request content upgrade download latest{0}".format( sync_to_peer_text) response = self._op(command) if sync: result = self.pandevice.syncjob(response) if not result["success"]: raise err.PanDeviceError( "Device %s attempt to download content version %s failed: %s" % (self.pandevice.id, latest_version, result["messages"])) return result else: return True
def get_vm_auth_keys(self): """Returns the current VM auth keys. Raises: PanDeviceError Returns: list: list of dicts. Each dict has "authkey" and "expires" keys. """ cmd = "request bootstrap vm-auth-key show" # Raises PanDeviceError. resp = self.op(cmd) data = resp.find("./result") if data is None: raise err.PanDeviceError("No result in returned XML") ans = [] for x in data.findall("./bootstrap-vm-auth-keys/entry"): ans.append( { "authkey": x.find("./vm-auth-key").text, "expires": x.find("./expiry-time").text, } ) return ans
def generate_vm_auth_key(self, lifetime): """Generates a VM auth key to be placed in a VM's init-cfg.txt. Args: lifetime(int): The lifetime (in hours). Raises: PanDeviceError Returns: dict: has "authkey" and "expires" keys. """ cmd = 'request bootstrap vm-auth-key generate lifetime "{0}"' # Raises PanDeviceError. resp = self.op(cmd.format(lifetime)) data = resp.find("./result") if data is None: raise err.PanDeviceError("No result in returned XML") tokens = data.text.split() ans = { "authkey": tokens[3], "expires": " ".join(tokens[-2:]).rstrip(), } return ans
def show_system_resources(self): self.xapi.op(cmd="show system resources", cmd_xml=True) result = self.xapi.xml_root() if self._version_info >= (9, 0, 0): regex = re.compile( r"load average: ([\d\.]+).*? ([\d\.]+) id,.*KiB Mem :\s+(\d+) total,.*? (\d+) free", re.DOTALL, ) else: regex = re.compile( r"load average: ([\d.]+).* ([\d.]+)%id.*Mem:.*?([\d.]+)k total.*?([\d]+)k free", re.DOTALL, ) match = regex.search(result) if match: """ return cpu, mem_free, load """ return { "load": Decimal(match.group(1)), "cpu": 100 - Decimal(match.group(2)), "mem_total": int(match.group(3)), "mem_free": int(match.group(4)), } else: raise err.PanDeviceError("Problem parsing show system resources", pan_device=self)
def set_shared_policy_synced(self, sync_status): if sync_status == "In Sync": self.shared_policy_synced = True elif sync_status == "Out of Sync": self.shared_policy_synced = False elif not sync_status: self.shared_policy_synced = None else: raise err.PanDeviceError("Unknown shared policy status: %s" % str(sync_status))
def download(self, version, sync_to_peer=True, sync=False): self._logger.info("Device %s downloading version: %s" % (self.pandevice.id, version)) response = self._op( 'request system software download sync-to-peer "%s" version "%s"' % ("yes" if sync_to_peer else "no", version)) if sync: result = self.pandevice.syncjob(response) if not result["success"]: raise err.PanDeviceError( "Device %s attempt to download version %s failed: %s" % (self.pandevice.id, version, result["messages"])) return result else: return True
def install(self, version, load_config=None, sync=False): self._logger.info("Device %s installing version: %s" % (self.pandevice.id, version)) response = self._op('request system software install%s version "%s"' % ( " load-config " + load_config if load_config is not None else "", version, )) if sync: result = self.pandevice.syncjob(response) if not result["success"]: raise err.PanDeviceError( "Device %s attempt to install version %s failed: %s" % (self.pandevice.id, version, result["messages"])) return result else: return True
def download_install_reboot(self, version, load_config=None, sync=False): if isstring(version): version = PanOSVersion(version) self.download_install(version, load_config, sync=True) # Reboot the device self._logger.info( "Device %s is rebooting after upgrading to version %s. This will take a while." % (self.pandevice.id, version)) self.pandevice.restart() if sync: new_version = self.pandevice.syncreboot() if version != new_version: raise err.PanDeviceError( "Attempt to upgrade to version %s failed." "Device %s is on version %s after reboot." % (version, self.pandevice.id, new_version)) self.pandevice.version = new_version return new_version else: return None
def upgrade_to_version(self, target_version, dryrun=False): """Upgrade to the target version, completely all intermediate upgrades For example, if firewall is running version 6.0.5 and target version is 7.0.2, then this method will proceed through the following steps: - Upgrade to 6.1.0 and reboot - Upgrade to 7.0.0 and reboot - Upgrade to 7.0.1 and reboot This method does not support HA pairs. """ # Get list of software if needed if not self.versions: self.check() # For a dry run, need to record the starting version starting_version = self.pandevice.version # Get versions as StrictVersion objects available_versions = map(PanOSVersion, self.versions.keys()) current_version = PanOSVersion(self.pandevice.version) latest_version = max(available_versions) next_minor_version = self._next_minor_version(current_version) # Check that this is an upgrade, not a downgrade if current_version > target_version: raise err.PanDeviceError( "Device %s upgrade failed: Can't upgrade from %s to %s." % (self.pandevice.id, self.pandevice.version, target_version)) # Determine the next version to upgrade to if target_version == "latest": next_version = min(latest_version, next_minor_version) elif latest_version < target_version: next_version = next_minor_version elif not self._direct_upgrade_possible(current_version, target_version): next_version = next_minor_version else: next_version = PanOSVersion(str(target_version)) if next_version not in available_versions and not dryrun: self._logger.info( "Device %s upgrading to %s, currently on %s. Checking for newer versions." % (self.pandevice.id, target_version, self.pandevice.version)) self.check() available_versions = map(PanOSVersion, self.versions.keys()) latest_version = max(available_versions) # Check if done upgrading if current_version == target_version: self._logger.info("Device %s is running target version: %s" % (self.pandevice.id, target_version)) return True elif target_version == "latest" and current_version == latest_version: self._logger.info("Device %s is running latest version: %s" % (self.pandevice.id, latest_version)) if dryrun: self._logger.info( "NOTE: dryrun with 'latest' does not show all upgrades,") self._logger.info( "as new versions are learned through the upgrade process,") self._logger.info( "so results may be different than dryrun output when using 'latest'." ) return True # Ensure the content pack is upgraded to the latest self.pandevice.content.download_and_install_latest(sync=True) # Upgrade to the next version self._logger.info("Device %s will be upgraded to version: %s" % (self.pandevice.id, next_version)) if dryrun: self.pandevice.version = str(next_version) else: self.download_install_reboot(next_version, sync=True) self.check() result = self.upgrade_to_version(target_version, dryrun=dryrun) if result and dryrun: self.pandevice.version = starting_version return result
def get_registered_ip(self, ip=None, tags=None, prefix=None): """Return registered/tagged addresses When called without arguments, retrieves all registered addresses. Note: Passing a single ip and/or single tag to this method results in a response from the firewall that contains only the relevant entries. ie. the filtering is done on the firewall before it responds. Passing a list of multiple ip addresses or tags will result in retreival of the entire tag database from the firewall which is then filtered and returned with only the relevant entries. Therefor, using a single ip or tag is more efficient. **Support:** PAN-OS 6.0 and higher Args: ip (:obj:`list` or :obj:`str`): IP address(es) to get tags for tags (:obj:`list` or :obj:`str`): Tag(s) to get prefix (str): Override class tag prefix Returns: dict: ip addresses as keys with tags as values Raises: PanDeviceError if running PAN-OS < 8.0 and a logfile is returned instead of IP/tag mapings. """ if self.device is None: raise err.PanDeviceNotSet("No device set for this userid instance") version = self.device.retrieve_panos_version() if prefix is None: prefix = self.prefix # Build up the command. limit = 0 start_elm = None start_offset = 1 root = ET.Element("show") cmd = ET.SubElement(root, "object") if version >= (6, 1, 0): cmd = ET.SubElement(cmd, "registered-ip") if version >= (8, 0, 0): # PAN-OS 8.0+ supports paging. limit = 500 ET.SubElement(cmd, "limit").text = "{0}".format(limit) start_elm = ET.SubElement(cmd, "start-point") start_elm.text = "{0}".format(start_offset) else: cmd = ET.SubElement(cmd, "registered-address") # Add ip/tag filter arguments to command. ip = list(set(string_or_list_or_none(ip))) tags = list(set(string_or_list_or_none(tags))) tags = [prefix + t for t in tags] if len(tags) == 1: tag_element = ET.SubElement(cmd, "tag") ET.SubElement(tag_element, "entry", {"name": tags[0]}) if len(ip) == 1: ip_element = ET.SubElement(cmd, "ip") ip_element.text = ip[0] addresses = {} while True: resp = self.device.op( cmd=ET.tostring(root, encoding="utf-8"), vsys=self.device.vsys, cmd_xml=False, ) # PAN-OS 7.1 and lower can return "outfile" instead of actual results. outfile = resp.find("./result/msg/line/outfile") if outfile is not None: msg = [ 'PAN-OS returned "{0}" instead of IP/tag mappings'.format( outfile.text ), "please upgrade to PAN-OS 8.0+", ] raise err.PanDeviceError(", ".join(msg)) entries = resp.findall("./result/entry") for entry in entries: c_ip = entry.get("ip") if ip and c_ip not in ip: continue members = entry.findall("./tag/member") c_tags = [] for member in members: tag = member.text if not prefix or tag.startswith(prefix): if not tags or tag in tags: c_tags.append(tag) if c_tags: addresses[c_ip] = c_tags if start_elm is None or limit == 0 or len(entries) < limit: break start_offset += len(entries) start_elm.text = "{0}".format(start_offset) # Done. return addresses
def __init__(self, *args, **kwargs): if type(self) == NTPServer: raise err.PanDeviceError("Do not instantiate class. Please use a subclass.") super(NTPServer, self).__init__(*args, **kwargs)
def xpath_panorama(self): raise err.PanDeviceError( "Attempt to modify Panorama configuration on non-Panorama device")
def refresh(self, style, rules=None, all_rules=False): """Retrieves hit count information for the specified rules. PAN-OS 8.1+ Args: style (str): The rule style to use. The style can be "application-override", "authentication", "decryption", "dos", "nat", "pbf", "qos", "sdwan", "security", or "tunnel-inspect". rules (list): A list of rules. This can be a mix of `panos.policies` instances or basic strings. If no rules are given, then the hit count for all rules is retrieved. all_rules (bool): If this is False, only retrieve hit count information for the rules attached to the rulebase of the specified style. If this is True, then get all rules. Either way, any rule whose hit count is retrieved and is in the object hierarchy has the hit count data saved to its `opstate`. Returns: dict: A dict where the key is the rule name and the value is the hit count information. """ dev = self.obj.nearest_pandevice() if dev.retrieve_panos_version() < (8, 1, 0): raise err.PanDeviceError( "Rule hit count is supported in PAN-OS 8.1+") kids = [] for x in self.obj.children: if not hasattr(x, "HIT_COUNT_STYLE") or x.HIT_COUNT_STYLE != style: continue if rules is None or x.uid in rules: kids.append(x) cmd = ET.Element("show") sub = ET.SubElement(cmd, "rule-hit-count") sub = ET.SubElement(sub, "vsys") sub = ET.SubElement(sub, "vsys-name") sub = ET.SubElement(sub, "entry", {"name": dev.vsys or "vsys1"}) sub = ET.SubElement(sub, "rule-base") sub = ET.SubElement(sub, "entry", {"name": style}) sub = ET.SubElement(sub, "rules") if all_rules: ET.SubElement(sub, "all") else: # Loop over rules specified or the object hierarchy. rule_list = rules or kids or [] if not rule_list: return {} sub = ET.SubElement(sub, "list") for x in rule_list: if hasattr(x, "uid"): ET.SubElement(sub, "member").text = x.uid else: ET.SubElement(sub, "member").text = x res = dev.op(ET.tostring(cmd, encoding="utf-8"), cmd_xml=False) ans = {} for elm in res.findall( "./result/rule-hit-count/vsys/entry/rule-base/entry/rules/entry" ): name = elm.attrib["name"] for x in kids: if x.uid == name: x.opstate.hit_count.refresh(elm) ans[name] = x.opstate.hit_count break else: ans[name] = HitCount(name=name, elm=elm) return ans