<commit_before>import collections
import csv
import datetime
import os
import stat
import pwd
import StringIO

from pysh.shell.pycmd import register_pycmd
from pysh.shell.pycmd import pycmd
from pysh.shell.pycmd import IOType
from pysh.shell.table import Table

def file_to_array(f):
  return map(lambda line: line.rstrip('\r\n'), f.readlines())

class Permission(int):
  def __init__(self, val):
    int.__init__(self, val)

  def __str__(self):
    return ''.join((self.__to_rwx(self >> 6),
                    self.__to_rwx(self >> 3),
                    self.__to_rwx(self >> 0)))

  def __to_rwx(self,  rwx):
    result = ['-'] * 3
    if rwx & (1 << 2):
      result[0] = 'r'
    if rwx & (1 << 1):
      result[1] = 'w'
    if rwx & (1 << 0):
      result[2] = 'x'
    return ''.join(result)

@pycmd(name='echo', inType=IOType.No)
def pycmd_echo(args, input):
  line = []
  for arg in args[1:]:
    if not isinstance(arg, basestring) and (
      isinstance(arg, collections.Iterable)):
      if line:
        yield ' '.join(line)
        line = []
      for e in arg:
        yield e
    else:
      line.append(str(arg))
  if line:
    yield ' '.join(line)

@pycmd(name='map')
def pycmd_map(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  f = args[1]
  assert callable(f)
  return (f(x) for x in input)

@pycmd(name='filter')
def pycmd_filter(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  cond = args[1]
  assert callable(cond)
  for x in input:
    if cond(x):
      yield x

@pycmd(name='reduce')
def pycmd_reduce(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  f = args[1]
  assert callable(f)
  return [reduce(f, input)]

@pycmd(name='readcsv')
def pycmd_readcsv(args, input):
  return csv.reader(input)

@pycmd(name='pyls')
def pycmd_pls(args, input):
  table = Table(['type', 'mode', 'user', 'group', 'mtime', 'atime', 'path'])
  for arg in args[1:]:
    file_stat = os.lstat(arg)
    file_type = '?'
    if stat.S_ISDIR(file_stat.st_mode):
      file_type = 'd'
    if stat.S_ISLNK(file_stat.st_mode):
      file_type = 'l'
    if stat.S_ISSOCK(file_stat.st_mode):
      file_type = 's'
    if stat.S_ISFIFO(file_stat.st_mode):
      file_type = 'p'
    if stat.S_ISCHR(file_stat.st_mode):
      file_type = 'c'
    if stat.S_ISREG(file_stat.st_mode):
      file_type = '-'
    user = pwd.getpwuid(file_stat.st_uid).pw_name
    group = pwd.getpwuid(file_stat.st_gid).pw_name
    permission = file_stat.st_mode & 0777
    mtime = datetime.datetime.fromtimestamp(int(file_stat.st_mtime))
    atime = datetime.datetime.fromtimestamp(int(file_stat.st_atime))
    table.add_row([file_type, Permission(permission),
                   user, group, mtime, atime, arg])
  return table

@pycmd(name='where')
def pycmd_pls(args, input):
  assert len(args) == 2
  row = list(input)[0]
  table = row.table()
  return table.where(args[1])

@pycmd(name='orderby')
def pycmd_pls(args, input):
  assert len(args) == 2 or len(args) == 3
  row = list(input)[0]
  table = row.table()
  asc = True
  if len(args) == 3:
    args2 = args[2].lower()
    if args2 == 'desc':
      asc = False
    elif args2 != 'asc':
      raise Exception('args[2] must be desc or asc.')
  return table.orderby(args[1], asc)

@pycmd(name='cd', inType=IOType.No, outType=IOType.No)
def pycmd_cd(args, input):
  assert len(args) == 2 or len(args) == 1
  if len(args) == 2:
    dir = args[1]
  else:
    dir = os.environ.get('HOME', '')
  if dir:
    os.chdir(dir)
  return ()
<commit_msg>Fixed the way how pyls gets group name.<commit_after>import collections
import csv
import datetime
import grp
import os
import stat
import pwd
import StringIO

from pysh.shell.pycmd import register_pycmd
from pysh.shell.pycmd import pycmd
from pysh.shell.pycmd import IOType
from pysh.shell.table import Table

def file_to_array(f):
  return map(lambda line: line.rstrip('\r\n'), f.readlines())

class Permission(int):
  def __init__(self, val):
    int.__init__(self, val)

  def __str__(self):
    return ''.join((self.__to_rwx(self >> 6),
                    self.__to_rwx(self >> 3),
                    self.__to_rwx(self >> 0)))

  def __to_rwx(self,  rwx):
    result = ['-'] * 3
    if rwx & (1 << 2):
      result[0] = 'r'
    if rwx & (1 << 1):
      result[1] = 'w'
    if rwx & (1 << 0):
      result[2] = 'x'
    return ''.join(result)

@pycmd(name='echo', inType=IOType.No)
def pycmd_echo(args, input):
  line = []
  for arg in args[1:]:
    if not isinstance(arg, basestring) and (
      isinstance(arg, collections.Iterable)):
      if line:
        yield ' '.join(line)
        line = []
      for e in arg:
        yield e
    else:
      line.append(str(arg))
  if line:
    yield ' '.join(line)

@pycmd(name='map')
def pycmd_map(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  f = args[1]
  assert callable(f)
  return (f(x) for x in input)

@pycmd(name='filter')
def pycmd_filter(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  cond = args[1]
  assert callable(cond)
  for x in input:
    if cond(x):
      yield x

@pycmd(name='reduce')
def pycmd_reduce(args, input):
  assert len(args) == 2
  if isinstance(input, file):
    input = file_to_array(input)
  f = args[1]
  assert callable(f)
  return [reduce(f, input)]

@pycmd(name='readcsv')
def pycmd_readcsv(args, input):
  return csv.reader(input)

@pycmd(name='pyls')
def pycmd_pls(args, input):
  table = Table(['type', 'mode', 'user', 'group', 'mtime', 'atime', 'path'])
  for arg in args[1:]:
    file_stat = os.lstat(arg)
    file_type = '?'
    if stat.S_ISDIR(file_stat.st_mode):
      file_type = 'd'
    if stat.S_ISLNK(file_stat.st_mode):
      file_type = 'l'
    if stat.S_ISSOCK(file_stat.st_mode):
      file_type = 's'
    if stat.S_ISFIFO(file_stat.st_mode):
      file_type = 'p'
    if stat.S_ISCHR(file_stat.st_mode):
      file_type = 'c'
    if stat.S_ISREG(file_stat.st_mode):
      file_type = '-'
    user = pwd.getpwuid(file_stat.st_uid).pw_name
    group = grp.getgrgid(file_stat.st_gid).gr_name
    permission = file_stat.st_mode & 0777
    mtime = datetime.datetime.fromtimestamp(int(file_stat.st_mtime))
    atime = datetime.datetime.fromtimestamp(int(file_stat.st_atime))
    table.add_row([file_type, Permission(permission),
                   user, group, mtime, atime, arg])
  return table

@pycmd(name='where')
def pycmd_pls(args, input):
  assert len(args) == 2
  row = list(input)[0]
  table = row.table()
  return table.where(args[1])

@pycmd(name='orderby')
def pycmd_pls(args, input):
  assert len(args) == 2 or len(args) == 3
  row = list(input)[0]
  table = row.table()
  asc = True
  if len(args) == 3:
    args2 = args[2].lower()
    if args2 == 'desc':
      asc = False
    elif args2 != 'asc':
      raise Exception('args[2] must be desc or asc.')
  return table.orderby(args[1], asc)

@pycmd(name='cd', inType=IOType.No, outType=IOType.No)
def pycmd_cd(args, input):
  assert len(args) == 2 or len(args) == 1
  if len(args) == 2:
    dir = args[1]
  else:
    dir = os.environ.get('HOME', '')
  if dir:
    os.chdir(dir)
  return ()
<|endoftext|>


<commit_before>#!/usr/bin/python
# -- Content-Encoding: UTF-8 --
"""
Herald Core service
"""

# Herald
from herald.exceptions import InvalidPeerAccess, NoTransport, HeraldTimeout
import herald
import herald.beans as beans

# Pelix
from pelix.ipopo.decorators import ComponentFactory, Requires, Provides, \
    Validate, Invalidate, Instantiate, RequiresMap, BindField, UpdateField, \
    UnbindField
import pelix.threadpool
import pelix.utilities

# Standard library
import fnmatch
import logging
import re
import threading

# ------------------------------------------------------------------------------

_logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------------

@ComponentFactory("herald-core-factory")
@Provides(herald.SERVICE_HERALD)
@Requires('_directory', herald.SERVICE_DIRECTORY)
@Requires('_listeners', herald.SERVICE_LISTENER, True, True)
@RequiresMap('_transports', herald.SERVICE_TRANSPORT, herald.PROP_ACCESS_ID,
             False, False, True)
@Instantiate("herald-core")
class Herald(object):
    """
    Herald core service
    """
    def __init__(self):
        """
        Sets up members
        """
        # Herald core directory
        self._directory = None

        # Message listeners (dependency)
        self._listeners = []

        # Filter -> Listener (computed)
        self.__msg_listeners = {}

        # Thread safety for listeners
        self.__lock = threading.Lock()

        # Herald transports: access ID -> implementation
        self._transports = {}

        # Notification threads
        self.__pool = pelix.threadpool.ThreadPool(5, logname="HeraldNotify")

        # Events used for blocking "send()": UID -> EventData
        self.__waiting_events = {}

    @Validate
    def _validate(self, context):
        """
        Component validated
        """
        # Start the thread pool
        self.__pool.start()

    @Invalidate
    def _invalidate(self, context):
        """
        Component invalidated
        """
        # Stop the thread pool
        self.__pool.stop()

        # Clear waiting events (set them with no data)
        for event in tuple(self.__waiting_events.values()):
            event.set(None)
        self.__waiting_events.clear()

        # Clear the thread pool
        self.__pool.clear()

    def __compile_pattern(self, pattern):
        """
        Converts a file name pattern to a regular expression

        :param pattern: A file name pattern
        :return: A compiled regular expression
        """
        return re.compile(fnmatch.translate(pattern), re.IGNORECASE)

    @BindField('_listeners')
    def _bind_listener(self, _, listener, svc_ref):
        """
        A message listener has been bound
        """
        with self.__lock:
            re_filters = set(self.__compile_pattern(fn_filter)
                             for fn_filter
                             in svc_ref.get_property(herald.PROP_FILTERS)
                             or [])
            for re_filter in re_filters:
                self.__msg_listeners.setdefault(re_filter, set()) \
                    .add(listener)

    @UpdateField('_listeners')
    def _update_listener(self, _, listener, svc_ref, old_props):
        """
        The properties of a message listener have been updated
        """
        with self.__lock:
            # Get old and new filters as sets
            old_filters = set(self.__compile_pattern(fn_filter)
                              for fn_filter
                              in old_props.get(herald.PROP_FILTERS) or [])
            new_filters = set(self.__compile_pattern(fn_filter)
                              for fn_filter
                              in svc_ref.get_property(herald.PROP_FILTERS)
                              or [])

            # Compute differences
            added_filters = new_filters.difference(old_filters)
            removed_filters = old_filters.difference(new_filters)

            # Add new filters
            for re_filter in added_filters:
                self.__msg_listeners.setdefault(re_filter, set()) \
                    .add(listener)

            # Remove old ones
            for re_filter in removed_filters:
                try:
                    listeners = self.__msg_listeners[re_filter]
                    listeners.remove(listener)
                except KeyError:
                    # Filter or listener not found
                    pass
                else:
                    # Clean up dictionary if necessary
                    if not listeners:
                        del self.__msg_listeners[re_filter]

    @UnbindField('_listeners')
    def _unbind_listener(self, _, listener, svc_ref):
        """
        A message listener has gone away
        """
        with self.__lock:
            re_filters = set(self.__compile_pattern(fn_filter)
                             for fn_filter
                             in svc_ref.get_property(herald.PROP_FILTERS)
                             or [])
            for re_filter in re_filters:
                try:
                    listeners = self.__msg_listeners[re_filter]
                    listeners.remove(listener)
                except KeyError:
                    # Filter or listener not found
                    pass
                else:
                    # Clean up dictionary if necessary
                    if not listeners:
                        del self.__msg_listeners[re_filter]

    def handle_message(self, message):
        """
        Handles a message received from a transport implementation.

        Unlocks/calls back the senders of the message this one responds to.

        :param message: A MessageReceived bean forged by the transport
        """
        _logger.warning("Got message %s from %s",
                        message.subject, message.sender)

        # User a tuple, because list can't be compared to tuples
        parts = tuple(part for part in message.subject.split('/') if part)
        if parts[:2] == ('herald', 'directory'):
            try:
                # Directory update message
                self._handle_directory_message(message, parts[2])
            except IndexError:
                # Not enough arguments for a directory update: ignore
                pass

        # Notify others of the message
        self.__notify(message)

    def _handle_directory_message(self, message, kind):
        """
        Handles a directory update message

        :param message: Message received from another peer
        :param kind: Kind of directory message
        """
        if kind == 'newcomer':
            # A new peer appears: register it
            self._directory.register(message.content)

            # Reply to it
            self.reply(message, self._directory.get_local_peer().dump(),
                       'herald/directory/welcome')

        elif kind == 'welcome':
            # A peer replied to our 'newcomer' event
            self._directory.register(message.content)

        elif kind == 'bye':
            # A peer is going away
            self._directory.unregister(message.content)

    def __notify(self, message):
        """
        Calls back message senders about responses or notifies the reception of
        a message

        :param message: The received message
        """
        if message.reply_to:
            try:
                # This is an answer to a message: unlock the sender
                self.__waiting_events.pop(message.reply_to).set(message)
            except KeyError:
                # Nobody was waiting for the event
                pass

        # Compute the list of listeners to notify
        msg_listeners = set()
        subject = message.subject

        with self.__lock:
            for re_filter, re_listeners in self.__msg_listeners.items():
                if re_filter.match(subject) is not None:
                    msg_listeners.update(re_listeners)

        # Call listeners in the thread pool
        for listener in msg_listeners:
            try:
                self.__pool.enqueue(listener.herald_message, herald, message)
            except (AttributeError, ValueError):
                # Invalid listener
                pass

    def _fire_reply(self, message, reply_to):
        """
        Tries to fire a reply to the given message

        :param message: Message to send as a reply
        :param reply_to: Message the first argument replies to
        :return: The UID of the sent message, None
        """
        # Use the message source peer
        try:
            transport = self._transports[reply_to.access]
        except KeyError:
            # Reception transport is not available anymore...
            raise NoTransport("No reply transport for access {0}"
                              .format(reply_to.access))
        else:
            # Try to get the Peer bean. If unknown, consider that the
            # "extra" data will help the transport to reply
            try:
                peer = self._directory.get_peer(reply_to.sender)
            except KeyError:
                peer = None

            try:
                # Send the reply
                transport.fire(peer, message, reply_to.extra)
            except InvalidPeerAccess:
                raise NoTransport("Can't reply to {0} using {1} transport"
                                  .format(peer, reply_to.access))
            else:
                # Reply sent. Stop here
                return message.uid

    def fire(self, target, message):
        """
        Fires (and forget) the given message to the target

        :param target: The UID of a Peer, or a Peer object
                       (ignored if reply_to is given)
        :param message: A Message bean
        :return: The UID of the message sent
        :raise KeyError: Unknown peer UID
        :raise NoTransport: No transport found to send the message
        """
        # Standard behavior
        # Get the Peer object
        if not isinstance(target, beans.Peer):
            peer = self._directory.get_peer(target)
        else:
            peer = target

        # Check if some transports are bound
        if not self._transports:
            raise NoTransport("No transport bound yet.")

        # Get accesses
        accesses = peer.get_accesses()
        for access in accesses:
            try:
                transport = self._transports[access]
            except KeyError:
                # No transport for this kind of access
                pass
            else:
                try:
                    # Call it
                    transport.fire(peer, message)
                except InvalidPeerAccess as ex:
                    # Transport can't read peer access data
                    _logger.debug("Error using transport %s: %s", access, ex)
                else:
                    # Success
                    break
        else:
            # No transport for those accesses
            raise NoTransport("No transport found for peer {0}".format(peer))

        return message.uid

    def send(self, target, message, timeout=None):
        """
        Sends a message, and waits for its reply

        :param target: The UID of a Peer, or a Peer object
                       (ignored if reply_to is given)
        :param message: A Message bean
        :param timeout: Maximum time to wait for an answer
        :return: The reply message bean
        :raise KeyError: Unknown peer UID
        :raise NoTransport: No transport found to send the message
        :raise HeraldTimeout: Timeout raised before getting an answer
        """
        # Prepare an event, which will be set when the answer will be received
        event = pelix.utilities.EventData()
        self.__waiting_events[message.uid] = event

        try:
            # Fire the message
            self.fire(target, message)

            # Message sent, wait for an answer
            if event.wait(timeout):
                if event.data is not None:
                    return event.data
                else:
                    # Message cancelled due to invalidation
                    raise HeraldTimeout("Herald stops listening to messages",
                                        message)
            else:
                raise HeraldTimeout("Timeout reached before receiving a reply",
                                    message)
        finally:
            try:
                # Clean up
                del self.__waiting_events[message.uid]
            except KeyError:
                # Ignore errors at this point
                pass

    def reply(self, message, content, subject=None):
        """
        Replies to a message

        :param message: Original message
        :param content: Content of the response
        :param subject: Reply message subject (same as request if None)
        :raise NoTransport: No transport/access found to send the reply
        """
        # Normalize subject
        if not subject:
            subject = message.subject

        try:
            # Try to reuse the same transport
            self._fire_reply(beans.Message(subject, content), message)
        except NoTransport:
            # Continue...
            pass
        else:
            # No error
            return

        # If not possible: fire a standard reply
        try:
            # Fire the reply
            self.fire(message.sender, beans.Message(subject, content))
        except KeyError:
            # Convert KeyError to NoTransport
            raise NoTransport("No access to reply to {0}"
                              .format(message.sender))
<commit_msg>Removed the trace of receiving a message<commit_after>#!/usr/bin/python
# -- Content-Encoding: UTF-8 --
"""
Herald Core service
"""

# Herald
from herald.exceptions import InvalidPeerAccess, NoTransport, HeraldTimeout
import herald
import herald.beans as beans

# Pelix
from pelix.ipopo.decorators import ComponentFactory, Requires, Provides, \
    Validate, Invalidate, Instantiate, RequiresMap, BindField, UpdateField, \
    UnbindField
import pelix.threadpool
import pelix.utilities

# Standard library
import fnmatch
import logging
import re
import threading

# ------------------------------------------------------------------------------

_logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------------

@ComponentFactory("herald-core-factory")
@Provides(herald.SERVICE_HERALD)
@Requires('_directory', herald.SERVICE_DIRECTORY)
@Requires('_listeners', herald.SERVICE_LISTENER, True, True)
@RequiresMap('_transports', herald.SERVICE_TRANSPORT, herald.PROP_ACCESS_ID,
             False, False, True)
@Instantiate("herald-core")
class Herald(object):
    """
    Herald core service
    """
    def __init__(self):
        """
        Sets up members
        """
        # Herald core directory
        self._directory = None

        # Message listeners (dependency)
        self._listeners = []

        # Filter -> Listener (computed)
        self.__msg_listeners = {}

        # Thread safety for listeners
        self.__lock = threading.Lock()

        # Herald transports: access ID -> implementation
        self._transports = {}

        # Notification threads
        self.__pool = pelix.threadpool.ThreadPool(5, logname="HeraldNotify")

        # Events used for blocking "send()": UID -> EventData
        self.__waiting_events = {}

    @Validate
    def _validate(self, context):
        """
        Component validated
        """
        # Start the thread pool
        self.__pool.start()

    @Invalidate
    def _invalidate(self, context):
        """
        Component invalidated
        """
        # Stop the thread pool
        self.__pool.stop()

        # Clear waiting events (set them with no data)
        for event in tuple(self.__waiting_events.values()):
            event.set(None)
        self.__waiting_events.clear()

        # Clear the thread pool
        self.__pool.clear()

    def __compile_pattern(self, pattern):
        """
        Converts a file name pattern to a regular expression

        :param pattern: A file name pattern
        :return: A compiled regular expression
        """
        return re.compile(fnmatch.translate(pattern), re.IGNORECASE)

    @BindField('_listeners')
    def _bind_listener(self, _, listener, svc_ref):
        """
        A message listener has been bound
        """
        with self.__lock:
            re_filters = set(self.__compile_pattern(fn_filter)
                             for fn_filter
                             in svc_ref.get_property(herald.PROP_FILTERS)
                             or [])
            for re_filter in re_filters:
                self.__msg_listeners.setdefault(re_filter, set()) \
                    .add(listener)

    @UpdateField('_listeners')
    def _update_listener(self, _, listener, svc_ref, old_props):
        """
        The properties of a message listener have been updated
        """
        with self.__lock:
            # Get old and new filters as sets
            old_filters = set(self.__compile_pattern(fn_filter)
                              for fn_filter
                              in old_props.get(herald.PROP_FILTERS) or [])
            new_filters = set(self.__compile_pattern(fn_filter)
                              for fn_filter
                              in svc_ref.get_property(herald.PROP_FILTERS)
                              or [])

            # Compute differences
            added_filters = new_filters.difference(old_filters)
            removed_filters = old_filters.difference(new_filters)

            # Add new filters
            for re_filter in added_filters:
                self.__msg_listeners.setdefault(re_filter, set()) \
                    .add(listener)

            # Remove old ones
            for re_filter in removed_filters:
                try:
                    listeners = self.__msg_listeners[re_filter]
                    listeners.remove(listener)
                except KeyError:
                    # Filter or listener not found
                    pass
                else:
                    # Clean up dictionary if necessary
                    if not listeners:
                        del self.__msg_listeners[re_filter]

    @UnbindField('_listeners')
    def _unbind_listener(self, _, listener, svc_ref):
        """
        A message listener has gone away
        """
        with self.__lock:
            re_filters = set(self.__compile_pattern(fn_filter)
                             for fn_filter
                             in svc_ref.get_property(herald.PROP_FILTERS)
                             or [])
            for re_filter in re_filters:
                try:
                    listeners = self.__msg_listeners[re_filter]
                    listeners.remove(listener)
                except KeyError:
                    # Filter or listener not found
                    pass
                else:
                    # Clean up dictionary if necessary
                    if not listeners:
                        del self.__msg_listeners[re_filter]

    def handle_message(self, message):
        """
        Handles a message received from a transport implementation.

        Unlocks/calls back the senders of the message this one responds to.

        :param message: A MessageReceived bean forged by the transport
        """
        # User a tuple, because list can't be compared to tuples
        parts = tuple(part for part in message.subject.split('/') if part)
        if parts[:2] == ('herald', 'directory'):
            try:
                # Directory update message
                self._handle_directory_message(message, parts[2])
            except IndexError:
                # Not enough arguments for a directory update: ignore
                pass

        # Notify others of the message
        self.__notify(message)

    def _handle_directory_message(self, message, kind):
        """
        Handles a directory update message

        :param message: Message received from another peer
        :param kind: Kind of directory message
        """
        if kind == 'newcomer':
            # A new peer appears: register it
            self._directory.register(message.content)

            # Reply to it
            self.reply(message, self._directory.get_local_peer().dump(),
                       'herald/directory/welcome')

        elif kind == 'welcome':
            # A peer replied to our 'newcomer' event
            self._directory.register(message.content)

        elif kind == 'bye':
            # A peer is going away
            self._directory.unregister(message.content)

    def __notify(self, message):
        """
        Calls back message senders about responses or notifies the reception of
        a message

        :param message: The received message
        """
        if message.reply_to:
            try:
                # This is an answer to a message: unlock the sender
                self.__waiting_events.pop(message.reply_to).set(message)
            except KeyError:
                # Nobody was waiting for the event
                pass

        # Compute the list of listeners to notify
        msg_listeners = set()
        subject = message.subject

        with self.__lock:
            for re_filter, re_listeners in self.__msg_listeners.items():
                if re_filter.match(subject) is not None:
                    msg_listeners.update(re_listeners)

        # Call listeners in the thread pool
        for listener in msg_listeners:
            try:
                self.__pool.enqueue(listener.herald_message, herald, message)
            except (AttributeError, ValueError):
                # Invalid listener
                pass

    def _fire_reply(self, message, reply_to):
        """
        Tries to fire a reply to the given message

        :param message: Message to send as a reply
        :param reply_to: Message the first argument replies to
        :return: The UID of the sent message, None
        """
        # Use the message source peer
        try:
            transport = self._transports[reply_to.access]
        except KeyError:
            # Reception transport is not available anymore...
            raise NoTransport("No reply transport for access {0}"
                              .format(reply_to.access))
        else:
            # Try to get the Peer bean. If unknown, consider that the
            # "extra" data will help the transport to reply
            try:
                peer = self._directory.get_peer(reply_to.sender)
            except KeyError:
                peer = None

            try:
                # Send the reply
                transport.fire(peer, message, reply_to.extra)
            except InvalidPeerAccess:
                raise NoTransport("Can't reply to {0} using {1} transport"
                                  .format(peer, reply_to.access))
            else:
                # Reply sent. Stop here
                return message.uid

    def fire(self, target, message):
        """
        Fires (and forget) the given message to the target

        :param target: The UID of a Peer, or a Peer object
                       (ignored if reply_to is given)
        :param message: A Message bean
        :return: The UID of the message sent
        :raise KeyError: Unknown peer UID
        :raise NoTransport: No transport found to send the message
        """
        # Standard behavior
        # Get the Peer object
        if not isinstance(target, beans.Peer):
            peer = self._directory.get_peer(target)
        else:
            peer = target

        # Check if some transports are bound
        if not self._transports:
            raise NoTransport("No transport bound yet.")

        # Get accesses
        accesses = peer.get_accesses()
        for access in accesses:
            try:
                transport = self._transports[access]
            except KeyError:
                # No transport for this kind of access
                pass
            else:
                try:
                    # Call it
                    transport.fire(peer, message)
                except InvalidPeerAccess as ex:
                    # Transport can't read peer access data
                    _logger.debug("Error using transport %s: %s", access, ex)
                else:
                    # Success
                    break
        else:
            # No transport for those accesses
            raise NoTransport("No transport found for peer {0}".format(peer))

        return message.uid

    def send(self, target, message, timeout=None):
        """
        Sends a message, and waits for its reply

        :param target: The UID of a Peer, or a Peer object
                       (ignored if reply_to is given)
        :param message: A Message bean
        :param timeout: Maximum time to wait for an answer
        :return: The reply message bean
        :raise KeyError: Unknown peer UID
        :raise NoTransport: No transport found to send the message
        :raise HeraldTimeout: Timeout raised before getting an answer
        """
        # Prepare an event, which will be set when the answer will be received
        event = pelix.utilities.EventData()
        self.__waiting_events[message.uid] = event

        try:
            # Fire the message
            self.fire(target, message)

            # Message sent, wait for an answer
            if event.wait(timeout):
                if event.data is not None:
                    return event.data
                else:
                    # Message cancelled due to invalidation
                    raise HeraldTimeout("Herald stops listening to messages",
                                        message)
            else:
                raise HeraldTimeout("Timeout reached before receiving a reply",
                                    message)
        finally:
            try:
                # Clean up
                del self.__waiting_events[message.uid]
            except KeyError:
                # Ignore errors at this point
                pass

    def reply(self, message, content, subject=None):
        """
        Replies to a message

        :param message: Original message
        :param content: Content of the response
        :param subject: Reply message subject (same as request if None)
        :raise NoTransport: No transport/access found to send the reply
        """
        # Normalize subject
        if not subject:
            subject = message.subject

        try:
            # Try to reuse the same transport
            self._fire_reply(beans.Message(subject, content), message)
        except NoTransport:
            # Continue...
            pass
        else:
            # No error
            return

        # If not possible: fire a standard reply
        try:
            # Fire the reply
            self.fire(message.sender, beans.Message(subject, content))
        except KeyError:
            # Convert KeyError to NoTransport
            raise NoTransport("No access to reply to {0}"
                              .format(message.sender))
<|endoftext|>


<commit_before>import loader
loader.loadDisplay()

from machine import SPI, Pin
from st7920 import Screen
from faces.font_5x7 import font as smallFont
bigFont = smallFont

#TODO CH sanitise screen++ creation into loader.loadDisplay(), possibly also CockleRFID creation
# prepare screen
screen = Screen(spi=SPI(-1, baudrate=1800000, polarity=0, phase=0, miso=Pin(16), mosi=Pin(5), sck=Pin(4)),
                slaveSelectPin=None, resetDisplayPin=None)
blackPlotter = screen.create_plotter(True)
whitePlotter = screen.create_plotter(False)
smallFont.draw_line(b"Powering Up!", x=32, y=24, plotter=blackPlotter)
screen.redraw()

loader.loadOther()
loader.loadStory(loader.storyUid)

from mfrc522 import MFRC522
#from faces.font_timB14 import font as bigFont
from vault import BankVault
from engines import Engine, dictToCard, cardToDict
from host import Host
from milecastles import Box

class CockleRfid(BankVault):

    def readCard(self, cardUid, unselect=True):
        try:
            cardDict = self.readJson(cardUid, unselect)
            card = dictToCard(cardUid, cardDict)
            return card
        except AssertionError as e:
            return None

    def writeCard(self, card, unselect=True):
        return self.writeJson( cardToDict(card), card.uid, unselect)

def prepareHost(story, boxUid):
    # prepare reader
    readerSpi = SPI(1, baudrate=1800000, polarity=0, phase=0)
    readerSpi.init()
    reader = MFRC522(spi=readerSpi, gpioRst=None, gpioCs=2)
    # wrap hardware in platform-specific API and attach story
    rfid = CockleRfid(reader)
    box = story._get_table(Box)[boxUid]
    engine = Engine(box=box)
    engine.registerStory(story)
    powerPin = Pin(15, Pin.OUT)
    powerPin.value(0)

    # launch box host
    return Host(
        story=story,
        box=box,
        engine=engine,
        screen=screen,
        rfid=rfid,
        smallFont=smallFont,
        bigFont=bigFont,
        blackPlotter=blackPlotter,
        whitePlotter=whitePlotter,
        powerPin=powerPin
    )<commit_msg>Added overclock<commit_after>import loader
loader.loadDisplay()

from machine import SPI, Pin, freq
from st7920 import Screen
from faces.font_5x7 import font as smallFont
bigFont = smallFont

#TODO CH sanitise screen++ creation into loader.loadDisplay(), possibly also CockleRFID creation
# prepare screen
screen = Screen(spi=SPI(-1, baudrate=1800000, polarity=0, phase=0, miso=Pin(16), mosi=Pin(5), sck=Pin(4)),
                slaveSelectPin=None, resetDisplayPin=None)
blackPlotter = screen.create_plotter(True)
whitePlotter = screen.create_plotter(False)
smallFont.draw_line(b"Powering Up!", x=32, y=24, plotter=blackPlotter)
screen.redraw()

loader.loadOther()
loader.loadStory(loader.storyUid)

# TODO CH make conditional on being an interior box
# overclock for speed - sacrificing current (and stability?)
freq(160000000)

from mfrc522 import MFRC522
#from faces.font_timB14 import font as bigFont
from vault import BankVault
from engines import Engine, dictToCard, cardToDict
from host import Host
from milecastles import Box

class CockleRfid(BankVault):

    def readCard(self, cardUid, unselect=True):
        try:
            cardDict = self.readJson(cardUid, unselect)
            card = dictToCard(cardUid, cardDict)
            return card
        except AssertionError as e:
            return None

    def writeCard(self, card, unselect=True):
        return self.writeJson( cardToDict(card), card.uid, unselect)

def prepareHost(story, boxUid):
    # prepare reader
    readerSpi = SPI(1, baudrate=1800000, polarity=0, phase=0)
    readerSpi.init()
    reader = MFRC522(spi=readerSpi, gpioRst=None, gpioCs=2)
    # wrap hardware in platform-specific API and attach story
    rfid = CockleRfid(reader)
    box = story._get_table(Box)[boxUid]
    engine = Engine(box=box)
    engine.registerStory(story)
    powerPin = Pin(15, Pin.OUT)
    powerPin.value(0)

    # launch box host
    return Host(
        story=story,
        box=box,
        engine=engine,
        screen=screen,
        rfid=rfid,
        smallFont=smallFont,
        bigFont=bigFont,
        blackPlotter=blackPlotter,
        whitePlotter=whitePlotter,
        powerPin=powerPin
    )<|endoftext|>


<commit_before># Copyright 2008-2009 ITA Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import division

"""Test classes, the real meat.

Tests handle all data processing, error checking, and reporting.
"""

import re
import time

from twisted.internet import defer, reactor
from twisted.python import failure
from coil import struct

from nagcat import errors, filters, log, query, scheduler, trend, util

STATES = ["OK", "WARNING", "CRITICAL", "UNKNOWN"]

TEMPLATE_OK = """%(test)s %(state)s: %(summary)s
Port: %(port)s
Test: %(test)s

Full Output:
%(output)s

Extra Output:
%(extra)s

Documentation:
%(documentation)s

%(url)s
"""

TEMPLATE_BAD = """%(test)s %(state)s: %(summary)s
Port: %(port)s
Test: %(test)s

Error:
%(error)s
%(priority)s
Full Output:
%(output)s

Extra Output:
%(extra)s

Documentation:
%(documentation)s

Investigation:
%(investigation)s

%(url)s
"""

class BaseTest(scheduler.Runnable):
    """Shared base between SimpleTest and Test"""

    def __init__(self, conf):
        scheduler.Runnable.__init__(self, conf)

        self._port = conf.get('port', None)
        # used in return and report
        self._now = time.time()
        # Used by the save filter and report
        self.saved = {}

        # Create the filter objects
        filter_list = conf.get('filters', [])
        self._filters = [filters.Filter(self, x) for x in filter_list]

        # Add final critical and warning tests
        if 'critical' in conf:
            self._filters.append(filters.get_filter(
                self, 'critical', None, conf['critical']))
        if 'warning' in conf:
            self._filters.append(filters.get_filter(
                self, 'warning', None, conf['warning']))

    def _start(self):
        # Subclasses must override this and fire the deferred!
        self.saved.clear()

        deferred = defer.Deferred()

        for filter in self._filters:
            if filter.handle_errors:
                deferred.addBoth(filter.filter)
            else:
                deferred.addCallback(filter.filter)

        return deferred

class ChildError(errors.TestError):
    """This is used to tell Test._report that the error happened in
    a sub-test rather than in the top level Test code.
    """
    pass

class Test(BaseTest):
    """Main test class"""

    def __init__(self, conf):
        BaseTest.__init__(self, conf)

        self._test = conf.get('test', "")
        self._documentation = conf.get('documentation', "")
        self._investigation = conf.get('investigation', "")
        self._priority = conf.get('priority', "")
        self._url = conf.get('url', "")
        self._subtests = {}

        # If self._documentation is a list convert it to a string
        if isinstance(self._documentation, list):
            self._documentation = "\n".join(self._documentation)
        if isinstance(self._investigation, list):
            self._investigation = "\n".join(self._documentation)

        if self._priority:
            self._priority = "Priority: %s\n\n" % self._priority

        if conf['query.type'] == "compound":
            self._compound = True
            conf.expand(recursive=False)
            self._return = conf.get('query.return', None)

            for name, qconf in conf['query'].iteritems():
                if not isinstance(qconf, struct.Struct):
                    continue

                self._addDefaults(qconf)
                self._subtests[name] = query.FilteredQuery(qconf)
                self.addDependency(self._subtests[name])

            if not self._subtests:
                raise errors.ConfigError(conf['query'],
                        "compound query must have a sub-query")
            if self._return or len(self._subtests) > 1:
                if not self._return:
                    raise errors.ConfigError(conf['query'],
                            "return statement is required")

                # Convert $(subquery) to data['subquery']
                self._return = re.sub("\\$\\(([^\\)]+)\\)",
                        lambda m: "data['%s']" % m.group(1), self._return)

                test_values = {'NOW': util.MathString('9999')}
                for name in self._subtests:
                    #XXX this test string isn't fool-proof but will mostly work
                    test_values[name] = util.MathString('9999')

                try:
                    eval(self._return, {'data': test_values})
                except SyntaxError, ex:
                    raise errors.ConfigError(conf['query'],
                            "Syntax error in return: %s" % ex)
                except KeyError, ex:
                    raise errors.ConfigError(conf['query'],
                            "Unknown sub-query in return: %s" % ex)
        else:
            self._compound = False
            qconf = conf.get('query')
            self._addDefaults(qconf)
            self._subtests['query'] = query.FilteredQuery(qconf)
            self.addDependency(self._subtests['query'])

        self._report_callbacks = []
        trend.addTrend(self, conf)

    def _addDefaults(self, conf):
        """Add default values based on this test to a subtest config"""
        conf.setdefault('host', self.host)
        conf.setdefault('port', self._port)
        conf.setdefault('repeat', str(self.repeat))

        if conf['host'] == self.host:
            conf.setdefault('addr', self.addr)

    def _start(self):
        self._now = time.time()

        # All sub-tests are now complete, process them!
        deferred = BaseTest._start(self)
        deferred.addBoth(self._report)

        try:
            result = self._computeReturn()
        except:
            result = errors.Failure()

        deferred.callback(result)

        return deferred

    def _computeReturn(self):
        if self._compound:
            data = {'NOW': util.MathString(self._now)}
            for name, subtest in self._subtests.iteritems():
                if isinstance(subtest.result, failure.Failure):
                    raise ChildError()
                data[name] = util.MathString(subtest.result)

            log.debug("Evaluating return '%s' with data = %s",
                    self._return, data)

            result = str(eval(self._return, {'data': data}))
        else:
            subtest = self._subtests['query']
            if isinstance(subtest.result, failure.Failure):
                raise ChildError()
            else:
                result = subtest.result

        return result

    def addReportCallback(self, func, *args, **kwargs):
        """The given callback function will be called each time
        this test finishes and has a test to report.

        The first argument will be a dict representing the report.
        """

        assert callable(func)
        self._report_callbacks.append((func, args, kwargs))

    def _report(self, result):
        """Generate a report of the final result, pass that report off
        to all registered report callbacks. (ie nagios reporting)
        """

        def indent(string, prefix="    "):
            ret = ""
            for line in string.splitlines():
                if line.strip():
                    line = prefix+line
                ret += line+'\n'
            return ret

        # Choose what to report at the main result
        if isinstance(result, failure.Failure):
            if isinstance(result.value, ChildError):
                # A child failed, find the worst failure
                level = 0
                failed = None

                for subtest in self._subtests.itervalues():
                    if isinstance(subtest.result, failure.Failure):
                        if isinstance(subtest.result.value, errors.TestError):
                            if subtest.result.value.index > level:
                                level = subtest.result.value.index
                                failed = subtest.result
                        else:
                            # Unknown error, just use it
                            failed = subtest.result
                            break;

                assert failed is not None
            else:
                failed = result

            if (isinstance(failed, errors.Failure) and
                    failed.result is not errors.NO_RESULT):
                output = failed.result
            else:
                output = ""

            if isinstance(failed.value, errors.TestError):
                state = failed.value.state
            else:
                state = "UNKNOWN"

            error = str(failed.value)
            summary = error
        else:
            output = result
            state = "OK"
            error = ""
            summary = result

        # Grab the first 40 characters of the first line
        if summary:
            summary = summary.split('\n', 1)[0][:40]
            if state == "OK" and self.label:
                summary = "%s %s" % (summary, self.label)

        # Fill in the Extra Output area and all valid values
        extra = ""
        results = {}
        for subname, subtest in self._subtests.iteritems():
            subextra = ""
            for savedname, savedval in subtest.saved.iteritems():
                subextra += "    %s:\n" % savedname
                subextra += indent(str(savedval), " "*8)

            if isinstance(subtest.result, failure.Failure):
                results[subname] = ""

                if (isinstance(subtest.result, errors.Failure) and
                        subtest.result.result is not errors.NO_RESULT):
                    subout = str(subtest.result.result)
                else:
                    subout = ""

                if isinstance(subtest.result.value, errors.TestError):
                    suberr = str(subtest.result.value)
                else:
                    suberr = str(subtest.result)
            else:
                results[subname] = subtest.result
                subout = str(subtest.result)
                suberr = ""

            if subout and subout != output:
                subextra += "    Output:\n"
                subextra += indent(subout, " "*8)

            if suberr and suberr != error:
                subextra += "    Error:\n"
                subextra += indent(suberr, " "*8)

            if subextra:
                extra += indent("%s:\n%s" % (subname, subextra))

        assert state in STATES

        if state == "OK":
            text = TEMPLATE_OK
        else:
            text = TEMPLATE_BAD

        report = {
                'test': self._test,
                'state': state,
                'state_id': STATES.index(state),
                'summary': summary,
                'output': output,
                'error': error,
                'extra': extra,
                'host': self.host,
                'addr': self.addr,
                'port': self._port,
                'time': self._now,
                'documentation': self._documentation,
                'investigation': self._investigation,
                'priority': self._priority,
                'url': self._url,
                'results': results,
                }

        text = text % report
        report['text'] = text

        # Don't fire callbacks (which write out to stuff) during shutdown
        if reactor.running:
            for (func, args, kwargs) in self._report_callbacks:
                try:
                    func(report, *args, **kwargs)
                except:
                    log.error("Report callback failed: %s" % failure.Failure())

        return report
<commit_msg>Fix coil expansion in compound queries<commit_after># Copyright 2008-2009 ITA Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import division

"""Test classes, the real meat.

Tests handle all data processing, error checking, and reporting.
"""

import re
import time

from twisted.internet import defer, reactor
from twisted.python import failure
from coil import struct

from nagcat import errors, filters, log, query, scheduler, trend, util

STATES = ["OK", "WARNING", "CRITICAL", "UNKNOWN"]

TEMPLATE_OK = """%(test)s %(state)s: %(summary)s
Port: %(port)s
Test: %(test)s

Full Output:
%(output)s

Extra Output:
%(extra)s

Documentation:
%(documentation)s

%(url)s
"""

TEMPLATE_BAD = """%(test)s %(state)s: %(summary)s
Port: %(port)s
Test: %(test)s

Error:
%(error)s
%(priority)s
Full Output:
%(output)s

Extra Output:
%(extra)s

Documentation:
%(documentation)s

Investigation:
%(investigation)s

%(url)s
"""

class BaseTest(scheduler.Runnable):
    """Shared base between SimpleTest and Test"""

    def __init__(self, conf):
        scheduler.Runnable.__init__(self, conf)

        self._port = conf.get('port', None)
        # used in return and report
        self._now = time.time()
        # Used by the save filter and report
        self.saved = {}

        # Create the filter objects
        filter_list = conf.get('filters', [])
        self._filters = [filters.Filter(self, x) for x in filter_list]

        # Add final critical and warning tests
        if 'critical' in conf:
            self._filters.append(filters.get_filter(
                self, 'critical', None, conf['critical']))
        if 'warning' in conf:
            self._filters.append(filters.get_filter(
                self, 'warning', None, conf['warning']))

    def _start(self):
        # Subclasses must override this and fire the deferred!
        self.saved.clear()

        deferred = defer.Deferred()

        for filter in self._filters:
            if filter.handle_errors:
                deferred.addBoth(filter.filter)
            else:
                deferred.addCallback(filter.filter)

        return deferred

class ChildError(errors.TestError):
    """This is used to tell Test._report that the error happened in
    a sub-test rather than in the top level Test code.
    """
    pass

class Test(BaseTest):
    """Main test class"""

    def __init__(self, conf):
        BaseTest.__init__(self, conf)

        self._test = conf.get('test', "")
        self._documentation = conf.get('documentation', "")
        self._investigation = conf.get('investigation', "")
        self._priority = conf.get('priority', "")
        self._url = conf.get('url', "")
        self._subtests = {}

        # If self._documentation is a list convert it to a string
        if isinstance(self._documentation, list):
            self._documentation = "\n".join(self._documentation)
        if isinstance(self._investigation, list):
            self._investigation = "\n".join(self._documentation)

        if self._priority:
            self._priority = "Priority: %s\n\n" % self._priority

        if conf['query.type'] == "compound":
            self._compound = True
            conf['query'].expand(recursive=False)
            self._return = conf.get('query.return', None)

            for name, qconf in conf['query'].iteritems():
                if not isinstance(qconf, struct.Struct):
                    continue

                self._addDefaults(qconf)
                self._subtests[name] = query.FilteredQuery(qconf)
                self.addDependency(self._subtests[name])

            if not self._subtests:
                raise errors.ConfigError(conf['query'],
                        "compound query must have a sub-query")
            if self._return or len(self._subtests) > 1:
                if not self._return:
                    raise errors.ConfigError(conf['query'],
                            "return statement is required")

                # Convert $(subquery) to data['subquery']
                self._return = re.sub("\\$\\(([^\\)]+)\\)",
                        lambda m: "data['%s']" % m.group(1), self._return)

                test_values = {'NOW': util.MathString('9999')}
                for name in self._subtests:
                    #XXX this test string isn't fool-proof but will mostly work
                    test_values[name] = util.MathString('9999')

                try:
                    log.trace("Testing expr %r with data=%r" %
                            (self._return, test_values))
                    eval(self._return, {'data': test_values})
                except SyntaxError, ex:
                    raise errors.ConfigError(conf['query'],
                            "Syntax error in return: %s" % ex)
                except KeyError, ex:
                    raise errors.ConfigError(conf['query'],
                            "Unknown sub-query in return: %s" % ex)
        else:
            self._compound = False
            qconf = conf.get('query')
            self._addDefaults(qconf)
            self._subtests['query'] = query.FilteredQuery(qconf)
            self.addDependency(self._subtests['query'])

        self._report_callbacks = []
        trend.addTrend(self, conf)

    def _addDefaults(self, conf):
        """Add default values based on this test to a subtest config"""
        conf.setdefault('host', self.host)
        conf.setdefault('port', self._port)
        conf.setdefault('repeat', str(self.repeat))

        if conf['host'] == self.host:
            conf.setdefault('addr', self.addr)

    def _start(self):
        self._now = time.time()

        # All sub-tests are now complete, process them!
        deferred = BaseTest._start(self)
        deferred.addBoth(self._report)

        try:
            result = self._computeReturn()
        except:
            result = errors.Failure()

        deferred.callback(result)

        return deferred

    def _computeReturn(self):
        if self._compound:
            data = {'NOW': util.MathString(self._now)}
            for name, subtest in self._subtests.iteritems():
                if isinstance(subtest.result, failure.Failure):
                    raise ChildError()
                data[name] = util.MathString(subtest.result)

            log.debug("Evaluating return '%s' with data = %s",
                    self._return, data)

            result = str(eval(self._return, {'data': data}))
        else:
            subtest = self._subtests['query']
            if isinstance(subtest.result, failure.Failure):
                raise ChildError()
            else:
                result = subtest.result

        return result

    def addReportCallback(self, func, *args, **kwargs):
        """The given callback function will be called each time
        this test finishes and has a test to report.

        The first argument will be a dict representing the report.
        """

        assert callable(func)
        self._report_callbacks.append((func, args, kwargs))

    def _report(self, result):
        """Generate a report of the final result, pass that report off
        to all registered report callbacks. (ie nagios reporting)
        """

        def indent(string, prefix="    "):
            ret = ""
            for line in string.splitlines():
                if line.strip():
                    line = prefix+line
                ret += line+'\n'
            return ret

        # Choose what to report at the main result
        if isinstance(result, failure.Failure):
            if isinstance(result.value, ChildError):
                # A child failed, find the worst failure
                level = 0
                failed = None

                for subtest in self._subtests.itervalues():
                    if isinstance(subtest.result, failure.Failure):
                        if isinstance(subtest.result.value, errors.TestError):
                            if subtest.result.value.index > level:
                                level = subtest.result.value.index
                                failed = subtest.result
                        else:
                            # Unknown error, just use it
                            failed = subtest.result
                            break;

                assert failed is not None
            else:
                failed = result

            if (isinstance(failed, errors.Failure) and
                    failed.result is not errors.NO_RESULT):
                output = failed.result
            else:
                output = ""

            if isinstance(failed.value, errors.TestError):
                state = failed.value.state
            else:
                state = "UNKNOWN"

            error = str(failed.value)
            summary = error
        else:
            output = result
            state = "OK"
            error = ""
            summary = result

        # Grab the first 40 characters of the first line
        if summary:
            summary = summary.split('\n', 1)[0][:40]
            if state == "OK" and self.label:
                summary = "%s %s" % (summary, self.label)

        # Fill in the Extra Output area and all valid values
        extra = ""
        results = {}
        for subname, subtest in self._subtests.iteritems():
            subextra = ""
            for savedname, savedval in subtest.saved.iteritems():
                subextra += "    %s:\n" % savedname
                subextra += indent(str(savedval), " "*8)

            if isinstance(subtest.result, failure.Failure):
                results[subname] = ""

                if (isinstance(subtest.result, errors.Failure) and
                        subtest.result.result is not errors.NO_RESULT):
                    subout = str(subtest.result.result)
                else:
                    subout = ""

                if isinstance(subtest.result.value, errors.TestError):
                    suberr = str(subtest.result.value)
                else:
                    suberr = str(subtest.result)
            else:
                results[subname] = subtest.result
                subout = str(subtest.result)
                suberr = ""

            if subout and subout != output:
                subextra += "    Output:\n"
                subextra += indent(subout, " "*8)

            if suberr and suberr != error:
                subextra += "    Error:\n"
                subextra += indent(suberr, " "*8)

            if subextra:
                extra += indent("%s:\n%s" % (subname, subextra))

        assert state in STATES

        if state == "OK":
            text = TEMPLATE_OK
        else:
            text = TEMPLATE_BAD

        report = {
                'test': self._test,
                'state': state,
                'state_id': STATES.index(state),
                'summary': summary,
                'output': output,
                'error': error,
                'extra': extra,
                'host': self.host,
                'addr': self.addr,
                'port': self._port,
                'time': self._now,
                'documentation': self._documentation,
                'investigation': self._investigation,
                'priority': self._priority,
                'url': self._url,
                'results': results,
                }

        text = text % report
        report['text'] = text

        # Don't fire callbacks (which write out to stuff) during shutdown
        if reactor.running:
            for (func, args, kwargs) in self._report_callbacks:
                try:
                    func(report, *args, **kwargs)
                except:
                    log.error("Report callback failed: %s" % failure.Failure())

        return report
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Created on Sat Sep 05 04:16:12 2015

@author: Xiaomao
"""
import json
import sys
from pymongo import MongoClient

def main(argv):
    filename = sys.argv[1]
    json_data = open(filename).read()
    data = json.loads(json_data, object_hook=remove_dollarsign)

    mongoClient = MongoClient()
    db = mongoClient['MasterDB']
    db.drop_collection(sys.argv[2])
    collection = db[sys.argv[2]]

#    patientCount = 0;
#    while patientCount < len(data):
#        if len(data) > 1:
#            collection.insert_one(data[patientCount])
#        else :
#            collection.insert_one(data)
#        patientCount += 1
    for key in data.keys():
        if isinstance(data[key], dict):
            collection.insert_one(data[key])
        else:
            collection.insert_one(data)

    try:
        if isinstance(data[key], dict):
            formatDict = getFormatDict(data[key])
        else:
            formatDict = getFormatDict(data)
        #print(formatDict)
        # Make all necessary formatting edits here
        formatDict = legalDict(data, formatDict)
        #print(formatDict)
        db.drop_collection(sys.argv[2]+'format')
        formatColl = db[sys.argv[2]+'format']
        formatColl.insert_one(formatDict)
    except KeyError:
        # Maybe send empty string to DB
        pass

    with open('/public/MMLS/python/'+sys.argv[2]+'.txt','wb') as outFile:
        outFile.write(formatDict)
        outFile.flush()
        outFile.close()

def remove_dollarsign(obj):
    for key in obj.keys():
        new_key = key.replace('$','')
        if new_key!=key:
            obj[new_key] = obj[key]
            del obj[key]
    return obj

def getFormatDict(jsonData):
    keys = jsonData.keys()
    dictionary = dict((el, '') for el in keys)
    for key in keys:
        try:
            jsonData[key].keys()
            dictionary[key] = getFormatDict(jsonData[key])
        except AttributeError:
            pass
    return dictionary

def legalDict(data, formattedDict):
    acceptedDict = dict(formattedDict)
    for key in formattedDict:
        acceptedDict = legalDictInner(data, key, acceptedDict)
    return acceptedDict

def legalDictInner(data, key, acceptedDict):
    possibleSize = 0
    for points in data:
        try:
            acceptedDict[key].keys()
            acceptedDict[key] = legalDict(data[key], acceptedDict[key])
        except:
            pass
        if isinstance(data[key], list):
           del acceptedDict[key]
           return acceptedDict
        else:
            possibleSize += 1

    if len(data)/possibleSize < 5:
        del acceptedDict[key]

    acceptedDict[key] = possibleSize
    return acceptedDict

if __name__ == "__main__":
   main(sys.argv[1:])<commit_msg>PRINTasdf<commit_after># -*- coding: utf-8 -*-
"""
Created on Sat Sep 05 04:16:12 2015

@author: Xiaomao
"""
import json
import sys
from pymongo import MongoClient

def main(argv):
    filename = sys.argv[1]
    json_data = open(filename).read()
    data = json.loads(json_data, object_hook=remove_dollarsign)

    mongoClient = MongoClient()
    db = mongoClient['MasterDB']
    db.drop_collection(sys.argv[2])
    collection = db[sys.argv[2]]

#    patientCount = 0;
#    while patientCount < len(data):
#        if len(data) > 1:
#            collection.insert_one(data[patientCount])
#        else :
#            collection.insert_one(data)
#        patientCount += 1
    for key in data.keys():
        if isinstance(data[key], dict):
            collection.insert_one(data[key])
        else:
            collection.insert_one(data)

    try:
        if isinstance(data[key], dict):
            formatDict = getFormatDict(data[key])
        else:
            formatDict = getFormatDict(data)
        #print(formatDict)
        # Make all necessary formatting edits here
        formatDict = legalDict(data, formatDict)
        #print(formatDict)
        db.drop_collection(sys.argv[2]+'format')
        formatColl = db[sys.argv[2]+'format']
        formatColl.insert_one(formatDict)
    except KeyError:
        # Maybe send empty string to DB
        pass

    with open('/public/MMLS/python/'+sys.argv[2]+'.txt','wb') as outFile:
        outFile.write(json.dump(formatDict, indent=4, sort_keys=True))
        outFile.flush()
        outFile.close()

def remove_dollarsign(obj):
    for key in obj.keys():
        new_key = key.replace('$','')
        if new_key!=key:
            obj[new_key] = obj[key]
            del obj[key]
    return obj

def getFormatDict(jsonData):
    keys = jsonData.keys()
    dictionary = dict((el, '') for el in keys)
    for key in keys:
        try:
            jsonData[key].keys()
            dictionary[key] = getFormatDict(jsonData[key])
        except AttributeError:
            pass
    return dictionary

def legalDict(data, formattedDict):
    acceptedDict = dict(formattedDict)
    for key in formattedDict:
        acceptedDict = legalDictInner(data, key, acceptedDict)
    return acceptedDict

def legalDictInner(data, key, acceptedDict):
    possibleSize = 0
    for points in data:
        try:
            acceptedDict[key].keys()
            acceptedDict[key] = legalDict(data[key], acceptedDict[key])
        except:
            pass
        if isinstance(data[key], list):
           del acceptedDict[key]
           return acceptedDict
        else:
            possibleSize += 1

    if len(data)/possibleSize < 5:
        del acceptedDict[key]

    acceptedDict[key] = possibleSize
    return acceptedDict

if __name__ == "__main__":
   main(sys.argv[1:])<|endoftext|>


<commit_before># Copyright 2017, 2019 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.  If not, see <http://www.gnu.org/licenses/>.

"""
A library for reading VyOS running config data.

This library is used internally by all config scripts of VyOS,
but its API should be considered stable and safe to use
in user scripts.

Note that this module will not work outside VyOS.

Node taxonomy
#############

There are multiple types of config tree nodes in VyOS, each requires
its own set of operations.

*Leaf nodes* (such as "address" in interfaces) can have values, but cannot
have children.
Leaf nodes can have one value, multiple values, or no values at all.

For example, "system host-name" is a single-value leaf node,
"system name-server" is a multi-value leaf node (commonly abbreviated "multi node"),
and "system ip disable-forwarding" is a valueless leaf node.

Non-leaf nodes cannot have values, but they can have child nodes. They are divided into
two classes depending on whether the names of their children are fixed or not.
For example, under "system", the names of all valid child nodes are predefined
("login", "name-server" etc.).

To the contrary, children of the "system task-scheduler task" node can have arbitrary names.
Such nodes are called *tag nodes*. This terminology is confusing but we keep using it for lack
of a better word. No one remembers if the "tag" in "task Foo" is "task" or "Foo",
but the distinction is irrelevant in practice.

Configuration modes
###################

VyOS has two distinct modes: operational mode and configuration mode. When a user logins,
the CLI is in the operational mode. In this mode, only the running (effective) config is accessible for reading.

When a user enters the "configure" command, a configuration session is setup. Every config session
has its *proposed* (or *session*) config built on top of the current running config. When changes are commited, if commit succeeds,
the proposed config is merged into the running config.

In configuration mode, "base" functions like `exists`, `return_value` return values from the session config,
while functions prefixed "effective" return values from the running config.

In operational mode, all functions return values from the running config.

"""

import os
import re
import json
import subprocess

import vyos.configtree

class VyOSError(Exception):
    """
    Raised on config access errors, most commonly if the type of a config tree node
    in the system does not match the type of operation.

    """
    pass

class Config(object):
    """
    The class of config access objects.

    Internally, in the current implementation, this object is *almost* stateless,
    the only state it keeps is relative *config path* for convenient access to config
    subtrees.
    """
    def __init__(self, session_env=None):
        self._cli_shell_api = "/bin/cli-shell-api"
        self._level = []
        if session_env:
            self.__session_env = session_env
        else:
            self.__session_env = None

        # Running config can be obtained either from op or conf mode, it always succeeds
        # (if config system is initialized at all).
        if os.path.isfile('/tmp/vyos-config-status'):
            running_config_text = self._run([self._cli_shell_api, '--show-active-only', '--show-show-defaults', 'showConfig'])
        else:
            with open('/opt/vyatta/etc/config/config.boot') as f:
                running_config_text = f.read()

        # Session config ("active") only exists in conf mode.
        # In op mode, we'll just use the same running config for both active and session configs.
        if self.in_session():
            session_config_text = self._run([self._cli_shell_api, '--show-working-only', '--show-show-defaults', '--show-ignore-edit', 'showConfig'])
        else:
            session_config_text = running_config_text

        self._session_config = vyos.configtree.ConfigTree(session_config_text)
        self._running_config = vyos.configtree.ConfigTree(running_config_text)

    def _make_command(self, op, path):
        args = path.split()
        cmd = [self._cli_shell_api, op] + args
        return cmd

    def _make_path(self, path):
        # Backwards-compatibility stuff: original implementation used string paths
        # libvyosconfig paths are lists, but since node names cannot contain whitespace,
        # splitting at whitespace is reasonably safe.
        # It may cause problems with exists() when it's used for checking values,
        # since values may contain whitespace.
        if isinstance(path, str):
            path = re.split(r'\s*', path)
        elif isinstance(path, list):
            pass
        else:
            raise TypeError("Path must be a whitespace-separated string or a list")
        return (self._level + path)

    def _run(self, cmd):
        if self.__session_env:
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=self.__session_env)
        else:
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        out = p.stdout.read()
        p.wait()
        if p.returncode != 0:
            raise VyOSError()
        else:
            return out.decode('ascii')

    def set_level(self, path):
        """
        Set the *edit level*, that is, a relative config tree path.
        Once set, all operations will be relative to this path,
        for example, after ``set_level("system")``, calling
        ``exists("name-server")`` is equivalent to calling
        ``exists("system name-server"`` without ``set_level``.

        Args:
            path (str): relative config path
        """
        # Make sure there's always a space between default path (level)
        # and path supplied as method argument
        # XXX: for small strings in-place concatenation is not a problem
        if isinstance(path, str):
            if path:
                self._level = re.split(r'\s*', path)
            else:
                self._level = []
        elif isinstance(path, list):
            self._level = path
        else:
            raise TypeError("Level path must be either a whitespace-separated string or a list")

    def get_level(self):
        """
        Gets the current edit level.

        Returns:
            str: current edit level
        """
        return(self._level)

    def exists(self, path):
        """
        Checks if a node with given path exists in the running or proposed config

        Returns:
            True if node exists, False otherwise

        Note:
            This function cannot be used outside a configuration sessions.
            In operational mode scripts, use ``exists_effective``.
        """
        if self._session_config.exists(self._make_path(path)):
            return True
        else:
            # libvyosconfig exists() works only for _nodes_, not _values_
            # libvyattacfg one also worked for values, so we emulate that case here
            path = re.split(r'\s*', path)
            path_without_value = path[:-1]
            path_str = " ".join(path_without_value)
            try:
                value = self._session_config.return_value(self._make_path(path_str))
                return (value == path[-1])
            except vyos.configtree.ConfigTreeError:
                # node doesn't exist at all
                return False

    def session_changed(self):
        """
        Returns:
            True if the config session has uncommited changes, False otherwise.
        """
        try:
            self._run(self._make_command('sessionChanged', ''))
            return True
        except VyOSError:
            return False

    def in_session(self):
        """
        Returns:
            True if called from a configuration session, False otherwise.
        """
        try:
            self._run(self._make_command('inSession', ''))
            return True
        except VyOSError:
            return False

    def show_config(self, path=[], default=None):
        """
        Args:
            path (str list): Configuration tree path, or empty
            default (str): Default value to return

        Returns:
            str: working configuration
        """
        if isinstance(path, list):
            path = " ".join(path)
        try:
            out = self._run(self._make_command('showConfig', path))
            return out
        except VyOSError:
            return(default)

    def get_config_dict(self, path=[], effective=False):
        """
        Args: path (str list): Configuration tree path, can be empty
        Returns: a dict representation of the config
        """
        res = self.show_config(self._make_path(path))
        config_tree = vyos.configtree.ConfigTree(res)
        config_dict = json.loads(config_tree.to_json())
        return config_dict

    def is_multi(self, path):
        """
        Args:
            path (str): Configuration tree path

        Returns:
            True if a node can have multiple values, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isMulti', path))
            return True
        except VyOSError:
            return False

    def is_tag(self, path):
        """
         Args:
            path (str): Configuration tree path

        Returns:
            True if a node is a tag node, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isTag', path))
            return True
        except VyOSError:
            return False

    def is_leaf(self, path):
        """
         Args:
            path (str): Configuration tree path

        Returns:
            True if a node is a leaf node, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isLeaf', path))
            return True
        except VyOSError:
            return False

    def return_value(self, path, default=None):
        """
        Retrieve a value of single-value leaf node in the running or proposed config

        Args:
            path (str): Configuration tree path
            default (str): Default value to return if node does not exist

        Returns:
            str: Node value, if it has any
            None: if node is valueless *or* if it doesn't exist

        Note:
            Due to the issue with treatment of valueless nodes by this function,
            valueless nodes should be checked with ``exists`` instead.

            This function cannot be used outside a configuration session.
            In operational mode scripts, use ``return_effective_value``.
        """
        try:
            value = self._session_config.return_value(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            value = None

        if not value:
            return(default)
        else:
            return(value)

    def return_values(self, path, default=[]):
        """
        Retrieve all values of a multi-value leaf node in the running or proposed config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: Node values, if it has any
            []: if node does not exist

        Note:
            This function cannot be used outside a configuration session.
            In operational mode scripts, use ``return_effective_values``.
        """
        try:
            values = self._session_config.return_values(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            values = []

        if not values:
            return(default)
        else:
            return(values)

    def list_nodes(self, path, default=[]):
        """
        Retrieve names of all children of a tag node in the running or proposed config

        Args:
            path (str): Configuration tree path

        Returns:
            string list: child node names

        """
        try:
            nodes = self._session_config.list_nodes(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            nodes = []

        if not nodes:
            return(default)
        else:
            return(nodes)

    def exists_effective(self, path):
        """
        Check if a node exists in the running (effective) config

        Args:
            path (str): Configuration tree path

        Returns:
            True if node exists in the running config, False otherwise

        Note:
            This function is safe to use in operational mode. In configuration mode,
            it ignores uncommited changes.
        """
        return(self._running_config.exists(self._make_path(path)))

    def return_effective_value(self, path, default=None):
        """
        Retrieve a values of a single-value leaf node in a running (effective) config

        Args:
            path (str): Configuration tree path
            default (str): Default value to return if node does not exist

        Returns:
            str: Node value
        """
        try:
            value = self._running_config.return_value(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            value = None

        if not value:
            return(default)
        else:
            return(value)

    def return_effective_values(self, path, default=[]):
        """
        Retrieve all values of a multi-value node in a running (effective) config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: A list of values
        """
        try:
            values = self._running_config.return_values(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            values = []

        if not values:
            return(default)
        else:
            return(values)

    def list_effective_nodes(self, path, default=[]):
        """
        Retrieve names of all children of a tag node in the running config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: child node names

        Raises:
            VyOSError: if the node is not a tag node
        """
        try:
            nodes = self._running_config.list_nodes(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            nodes = []

        if not nodes:
            return(default)
        else:
            return(nodes)
<commit_msg>vyos.config: T1764: allow for list argument to exists, in value case<commit_after># Copyright 2017, 2019 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.  If not, see <http://www.gnu.org/licenses/>.

"""
A library for reading VyOS running config data.

This library is used internally by all config scripts of VyOS,
but its API should be considered stable and safe to use
in user scripts.

Note that this module will not work outside VyOS.

Node taxonomy
#############

There are multiple types of config tree nodes in VyOS, each requires
its own set of operations.

*Leaf nodes* (such as "address" in interfaces) can have values, but cannot
have children.
Leaf nodes can have one value, multiple values, or no values at all.

For example, "system host-name" is a single-value leaf node,
"system name-server" is a multi-value leaf node (commonly abbreviated "multi node"),
and "system ip disable-forwarding" is a valueless leaf node.

Non-leaf nodes cannot have values, but they can have child nodes. They are divided into
two classes depending on whether the names of their children are fixed or not.
For example, under "system", the names of all valid child nodes are predefined
("login", "name-server" etc.).

To the contrary, children of the "system task-scheduler task" node can have arbitrary names.
Such nodes are called *tag nodes*. This terminology is confusing but we keep using it for lack
of a better word. No one remembers if the "tag" in "task Foo" is "task" or "Foo",
but the distinction is irrelevant in practice.

Configuration modes
###################

VyOS has two distinct modes: operational mode and configuration mode. When a user logins,
the CLI is in the operational mode. In this mode, only the running (effective) config is accessible for reading.

When a user enters the "configure" command, a configuration session is setup. Every config session
has its *proposed* (or *session*) config built on top of the current running config. When changes are commited, if commit succeeds,
the proposed config is merged into the running config.

In configuration mode, "base" functions like `exists`, `return_value` return values from the session config,
while functions prefixed "effective" return values from the running config.

In operational mode, all functions return values from the running config.

"""

import os
import re
import json
import subprocess

import vyos.configtree

class VyOSError(Exception):
    """
    Raised on config access errors, most commonly if the type of a config tree node
    in the system does not match the type of operation.

    """
    pass

class Config(object):
    """
    The class of config access objects.

    Internally, in the current implementation, this object is *almost* stateless,
    the only state it keeps is relative *config path* for convenient access to config
    subtrees.
    """
    def __init__(self, session_env=None):
        self._cli_shell_api = "/bin/cli-shell-api"
        self._level = []
        if session_env:
            self.__session_env = session_env
        else:
            self.__session_env = None

        # Running config can be obtained either from op or conf mode, it always succeeds
        # (if config system is initialized at all).
        if os.path.isfile('/tmp/vyos-config-status'):
            running_config_text = self._run([self._cli_shell_api, '--show-active-only', '--show-show-defaults', 'showConfig'])
        else:
            with open('/opt/vyatta/etc/config/config.boot') as f:
                running_config_text = f.read()

        # Session config ("active") only exists in conf mode.
        # In op mode, we'll just use the same running config for both active and session configs.
        if self.in_session():
            session_config_text = self._run([self._cli_shell_api, '--show-working-only', '--show-show-defaults', '--show-ignore-edit', 'showConfig'])
        else:
            session_config_text = running_config_text

        self._session_config = vyos.configtree.ConfigTree(session_config_text)
        self._running_config = vyos.configtree.ConfigTree(running_config_text)

    def _make_command(self, op, path):
        args = path.split()
        cmd = [self._cli_shell_api, op] + args
        return cmd

    def _make_path(self, path):
        # Backwards-compatibility stuff: original implementation used string paths
        # libvyosconfig paths are lists, but since node names cannot contain whitespace,
        # splitting at whitespace is reasonably safe.
        # It may cause problems with exists() when it's used for checking values,
        # since values may contain whitespace.
        if isinstance(path, str):
            path = re.split(r'\s*', path)
        elif isinstance(path, list):
            pass
        else:
            raise TypeError("Path must be a whitespace-separated string or a list")
        return (self._level + path)

    def _run(self, cmd):
        if self.__session_env:
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=self.__session_env)
        else:
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        out = p.stdout.read()
        p.wait()
        if p.returncode != 0:
            raise VyOSError()
        else:
            return out.decode('ascii')

    def set_level(self, path):
        """
        Set the *edit level*, that is, a relative config tree path.
        Once set, all operations will be relative to this path,
        for example, after ``set_level("system")``, calling
        ``exists("name-server")`` is equivalent to calling
        ``exists("system name-server"`` without ``set_level``.

        Args:
            path (str): relative config path
        """
        # Make sure there's always a space between default path (level)
        # and path supplied as method argument
        # XXX: for small strings in-place concatenation is not a problem
        if isinstance(path, str):
            if path:
                self._level = re.split(r'\s*', path)
            else:
                self._level = []
        elif isinstance(path, list):
            self._level = path
        else:
            raise TypeError("Level path must be either a whitespace-separated string or a list")

    def get_level(self):
        """
        Gets the current edit level.

        Returns:
            str: current edit level
        """
        return(self._level)

    def exists(self, path):
        """
        Checks if a node with given path exists in the running or proposed config

        Returns:
            True if node exists, False otherwise

        Note:
            This function cannot be used outside a configuration sessions.
            In operational mode scripts, use ``exists_effective``.
        """
        if self._session_config.exists(self._make_path(path)):
            return True
        else:
            # libvyosconfig exists() works only for _nodes_, not _values_
            # libvyattacfg one also worked for values, so we emulate that case here
            if isinstance(path, str):
                path = re.split(r'\s*', path)
            path_without_value = path[:-1]
            path_str = " ".join(path_without_value)
            try:
                value = self._session_config.return_value(self._make_path(path_str))
                return (value == path[-1])
            except vyos.configtree.ConfigTreeError:
                # node doesn't exist at all
                return False

    def session_changed(self):
        """
        Returns:
            True if the config session has uncommited changes, False otherwise.
        """
        try:
            self._run(self._make_command('sessionChanged', ''))
            return True
        except VyOSError:
            return False

    def in_session(self):
        """
        Returns:
            True if called from a configuration session, False otherwise.
        """
        try:
            self._run(self._make_command('inSession', ''))
            return True
        except VyOSError:
            return False

    def show_config(self, path=[], default=None):
        """
        Args:
            path (str list): Configuration tree path, or empty
            default (str): Default value to return

        Returns:
            str: working configuration
        """
        if isinstance(path, list):
            path = " ".join(path)
        try:
            out = self._run(self._make_command('showConfig', path))
            return out
        except VyOSError:
            return(default)

    def get_config_dict(self, path=[], effective=False):
        """
        Args: path (str list): Configuration tree path, can be empty
        Returns: a dict representation of the config
        """
        res = self.show_config(self._make_path(path))
        config_tree = vyos.configtree.ConfigTree(res)
        config_dict = json.loads(config_tree.to_json())
        return config_dict

    def is_multi(self, path):
        """
        Args:
            path (str): Configuration tree path

        Returns:
            True if a node can have multiple values, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isMulti', path))
            return True
        except VyOSError:
            return False

    def is_tag(self, path):
        """
         Args:
            path (str): Configuration tree path

        Returns:
            True if a node is a tag node, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isTag', path))
            return True
        except VyOSError:
            return False

    def is_leaf(self, path):
        """
         Args:
            path (str): Configuration tree path

        Returns:
            True if a node is a leaf node, False otherwise.

        Note:
            It also returns False if node doesn't exist.
        """
        try:
            path = " ".join(self._level) + " " + path
            self._run(self._make_command('isLeaf', path))
            return True
        except VyOSError:
            return False

    def return_value(self, path, default=None):
        """
        Retrieve a value of single-value leaf node in the running or proposed config

        Args:
            path (str): Configuration tree path
            default (str): Default value to return if node does not exist

        Returns:
            str: Node value, if it has any
            None: if node is valueless *or* if it doesn't exist

        Note:
            Due to the issue with treatment of valueless nodes by this function,
            valueless nodes should be checked with ``exists`` instead.

            This function cannot be used outside a configuration session.
            In operational mode scripts, use ``return_effective_value``.
        """
        try:
            value = self._session_config.return_value(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            value = None

        if not value:
            return(default)
        else:
            return(value)

    def return_values(self, path, default=[]):
        """
        Retrieve all values of a multi-value leaf node in the running or proposed config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: Node values, if it has any
            []: if node does not exist

        Note:
            This function cannot be used outside a configuration session.
            In operational mode scripts, use ``return_effective_values``.
        """
        try:
            values = self._session_config.return_values(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            values = []

        if not values:
            return(default)
        else:
            return(values)

    def list_nodes(self, path, default=[]):
        """
        Retrieve names of all children of a tag node in the running or proposed config

        Args:
            path (str): Configuration tree path

        Returns:
            string list: child node names

        """
        try:
            nodes = self._session_config.list_nodes(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            nodes = []

        if not nodes:
            return(default)
        else:
            return(nodes)

    def exists_effective(self, path):
        """
        Check if a node exists in the running (effective) config

        Args:
            path (str): Configuration tree path

        Returns:
            True if node exists in the running config, False otherwise

        Note:
            This function is safe to use in operational mode. In configuration mode,
            it ignores uncommited changes.
        """
        return(self._running_config.exists(self._make_path(path)))

    def return_effective_value(self, path, default=None):
        """
        Retrieve a values of a single-value leaf node in a running (effective) config

        Args:
            path (str): Configuration tree path
            default (str): Default value to return if node does not exist

        Returns:
            str: Node value
        """
        try:
            value = self._running_config.return_value(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            value = None

        if not value:
            return(default)
        else:
            return(value)

    def return_effective_values(self, path, default=[]):
        """
        Retrieve all values of a multi-value node in a running (effective) config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: A list of values
        """
        try:
            values = self._running_config.return_values(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            values = []

        if not values:
            return(default)
        else:
            return(values)

    def list_effective_nodes(self, path, default=[]):
        """
        Retrieve names of all children of a tag node in the running config

        Args:
            path (str): Configuration tree path

        Returns:
            str list: child node names

        Raises:
            VyOSError: if the node is not a tag node
        """
        try:
            nodes = self._running_config.list_nodes(self._make_path(path))
        except vyos.configtree.ConfigTreeError:
            nodes = []

        if not nodes:
            return(default)
        else:
            return(nodes)
<|endoftext|>