<commit_before>import os
import re

class ConfigAction(object):
    @classmethod
    def matches(cls, filename):
        raise Exception('Not implemented')

    def __init__(self, config, relpath, source, dest):
        self.source = source
        self.dest = dest
        self.relpath = relpath
        self.config = config

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => '+self.dest

    def check(self):
        raise Exception('Not implemented')

    def sync(self):
        raise Exception('Not implemented')

    def dest_path(self):
        return os.path.normpath(\
            os.path.join(self.config.dest, self.relpath, self.dest))

    def source_path(self):
        return os.path.normpath(\
            os.path.join(self.config.source, self.relpath, self.source))

    def _makedirs(self):
        dir = os.path.dirname(self.dest_path())
        if not os.path.isdir(dir):
            os.makedirs(dir)

class SymlinkConfigAction(ConfigAction):
    @classmethod
    def matches(cls, filename):
        return filename

    def check(self):
        source = self.source_path()
        if not os.path.exists(source):
            raise Exception("Source does not exists")

        dest = self.dest_path()
        if os.path.lexists(dest):
            if not os.path.islink(dest):
                raise Exception("Destination exists and is not a link")

    def sync(self):
        source = self.source_path()
        dest = self.dest_path()
        if os.path.lexists(dest):
            if os.path.islink(dest):
                if not os.path.samefile(dest, source):
                    os.unlink(dest)
                    print "Link target altered"
                else:
                    return
        else:
            self._makedirs()

        print "Created new link: "+dest+" => "+source
        os.symlink(source, dest)

class CopyConfigAction(ConfigAction):
    pass #TODO

class ProgrammableConfigAction(ConfigAction):
    matched = re.compile("\.p\.py$")

    @classmethod
    def matches(cls, filename):
        if cls.matched.search(filename):
            return cls.matched.sub("", filename)
        return False

    def check(self):
        #TODO ignore()
        class SymlinkForwarder(Exception):
            def __init__(self, filename):
                self.filename = filename

        def redirect(filename):
            raise SymlinkForwarder("_"+filename)

        exec_env = \
        {
            "options": self.config.options,
            "redirect": redirect
        }

        source = self.source_path()
        try:
            exec compile(open(source).read(), source, 'exec') \
                in exec_env
        except SymlinkForwarder as e:
            self.proxy = SymlinkConfigAction(self.config, self.relpath, e.filename, self.dest)
        else:
            raise Exception("Unknown result")

        return self.proxy.check()

    def sync(self):
        return self.proxy.sync()

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => PROXY '+repr(self.proxy)

class IgnoreConfigAction(ConfigAction):
    ignored = re.compile("_|\.git$|\.gitignore$")

    @classmethod
    def matches(cls, filename):
        if cls.ignored.match(filename):
            return None
        return False

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => IGNORED'

class ConfigSource(object):
    def __init__(self, source, dest, classes = None, options = None):
        # handle '~'
        self.source = os.path.expanduser(source)
        self.dest = os.path.expanduser(dest)

        if classes:
            self.classes = classes
        else:
            self.classes = [
                ProgrammableConfigAction,
                IgnoreConfigAction,
                SymlinkConfigAction,
            ]

        if options:
            self.options = options
        else:
            self.options = []

    def analyze(self):
        def walker(_, path, files):
            relpath = os.path.relpath(path, self.source)
            for filename in (file for file in files \
            if not os.path.isdir(os.path.join(path, file))):
                self.add(relpath, filename)

        self.tree = {}
        os.path.walk(self.source, walker, None)

    def add(self, relpath, filename):
        def get_file_class(filename):
            for cls in self.classes:
                dest = cls.matches(filename)
                if dest is not False:
                    return (cls, dest)
            raise Exception("No class found")

        cls, dest = get_file_class(filename)

        if dest is not None:
            files = self.tree.setdefault(relpath, {})
            if files.has_key(dest):
                raise Exception('Conflict: '+filename+' with '+files[dest])
            files[dest] = cls(self, relpath, filename, dest)

    def check(self):
        for file in self:
            file.check()

    def sync(self):
        for file in self:
            file.sync()

    def __iter__(self):
        for files in self.tree.itervalues():
            for file in files.itervalues():
                yield file

    def __repr__(self):
        return "\n".join(\
            (action.relpath+': '+repr(action) for action in self))

<commit_msg>ignore() function for ProgrammableConfigAction<commit_after>import os
import re

class ConfigAction(object):
    @classmethod
    def matches(cls, filename):
        raise Exception('Not implemented')

    def __init__(self, config, relpath, source, dest):
        self.source = source
        self.dest = dest
        self.relpath = relpath
        self.config = config

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => '+self.dest

    def check(self):
        raise Exception('Not implemented')

    def sync(self):
        raise Exception('Not implemented')

    def dest_path(self):
        return os.path.normpath(\
            os.path.join(self.config.dest, self.relpath, self.dest))

    def source_path(self):
        return os.path.normpath(\
            os.path.join(self.config.source, self.relpath, self.source))

    def _makedirs(self):
        dir = os.path.dirname(self.dest_path())
        if not os.path.isdir(dir):
            os.makedirs(dir)

class SymlinkConfigAction(ConfigAction):
    @classmethod
    def matches(cls, filename):
        return filename

    def check(self):
        source = self.source_path()
        if not os.path.exists(source):
            raise Exception("Source does not exists")

        dest = self.dest_path()
        if os.path.lexists(dest):
            if not os.path.islink(dest):
                raise Exception("Destination exists and is not a link")

    def sync(self):
        source = self.source_path()
        dest = self.dest_path()
        if os.path.lexists(dest):
            if os.path.islink(dest):
                if not os.path.samefile(dest, source):
                    os.unlink(dest)
                    print "Link target altered"
                else:
                    return
        else:
            self._makedirs()

        print "Created new link: "+dest+" => "+source
        os.symlink(source, dest)

class CopyConfigAction(ConfigAction):
    pass #TODO

class ProgrammableConfigAction(ConfigAction):
    matched = re.compile("\.p\.py$")

    @classmethod
    def matches(cls, filename):
        if cls.matched.search(filename):
            return cls.matched.sub("", filename)
        return False

    def check(self):
        #TODO ignore()
        class SymlinkForwarder(Exception):
            def __init__(self, filename):
                self.filename = filename

        class IgnoreForwarder(Exception):
            pass

        def redirect(filename):
            raise SymlinkForwarder("_"+filename)

        def ignore():
            raise IgnoreForwarder()

        exec_env = \
        {
            "options": self.config.options,
            "redirect": redirect,
            "ignore": ignore,
        }

        source = self.source_path()
        try:
            exec compile(open(source).read(), source, 'exec') \
                in exec_env
        except SymlinkForwarder as e:
            self.proxy = SymlinkConfigAction(self.config, self.relpath, e.filename, self.dest)
        except IgnoreForwarder as e:
            self.proxy = None
        else:
            raise Exception("Unknown result")

        if not self.proxy is None:
            return self.proxy.check()

    def sync(self):
        if not self.proxy is None:
            return self.proxy.sync()

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => PROXY '+repr(self.proxy)

class IgnoreConfigAction(ConfigAction):
    ignored = re.compile("_|\.git$|\.gitignore$")

    @classmethod
    def matches(cls, filename):
        if cls.ignored.match(filename):
            return None
        return False

    def __repr__(self):
        return self.__class__.__name__+': '+self.source+' => IGNORED'

class ConfigSource(object):
    def __init__(self, source, dest, classes = None, options = None):
        # handle '~'
        self.source = os.path.expanduser(source)
        self.dest = os.path.expanduser(dest)

        if classes:
            self.classes = classes
        else:
            self.classes = [
                ProgrammableConfigAction,
                IgnoreConfigAction,
                SymlinkConfigAction,
            ]

        if options:
            self.options = options
        else:
            self.options = []

    def analyze(self):
        def walker(_, path, files):
            relpath = os.path.relpath(path, self.source)
            for filename in (file for file in files \
            if not os.path.isdir(os.path.join(path, file))):
                self.add(relpath, filename)

        self.tree = {}
        os.path.walk(self.source, walker, None)

    def add(self, relpath, filename):
        def get_file_class(filename):
            for cls in self.classes:
                dest = cls.matches(filename)
                if dest is not False:
                    return (cls, dest)
            raise Exception("No class found")

        cls, dest = get_file_class(filename)

        if dest is not None:
            files = self.tree.setdefault(relpath, {})
            if files.has_key(dest):
                raise Exception('Conflict: '+filename+' with '+files[dest])
            files[dest] = cls(self, relpath, filename, dest)

    def check(self):
        for file in self:
            file.check()

    def sync(self):
        for file in self:
            file.sync()

    def __iter__(self):
        for files in self.tree.itervalues():
            for file in files.itervalues():
                yield file

    def __repr__(self):
        return "\n".join(\
            (action.relpath+': '+repr(action) for action in self))

<|endoftext|>


<commit_before>#!/usr/bin/python
# Copyright 2013 Google Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

'''
GCE external inventory script
=================================

Generates inventory that Ansible can understand by making API requests
Google Compute Engine via the libcloud library.  Full install/configuration
instructions for the gce* modules can be found in the comments of
ansible/test/gce_tests.py.

When run against a specific host, this script returns the following variables
based on the data obtained from the libcloud Node object:
 - gce_uuid
 - gce_id
 - gce_image
 - gce_machine_type
 - gce_private_ip
 - gce_public_ip
 - gce_name
 - gce_description
 - gce_status
 - gce_zone
 - gce_tags
 - gce_metadata
 - gce_network

When run in --list mode, instances are grouped by the following categories:
 - zone:
   zone group name examples are us-central1-b, europe-west1-a, etc.
 - instance tags:
   An entry is created for each tag.  For example, if you have two instances
   with a common tag called 'foo', they will both be grouped together under
   the 'tag_foo' name.
 - network name:
   the name of the network is appended to 'network_' (e.g. the 'default'
   network will result in a group named 'network_default')
 - machine type
   types follow a pattern like n1-standard-4, g1-small, etc.
 - running status:
   group name prefixed with 'status_' (e.g. status_running, status_stopped,..)
 - image:
   when using an ephemeral/scratch disk, this will be set to the image name
   used when creating the instance (e.g. debian-7-wheezy-v20130816).  when
   your instance was created with a root persistent disk it will be set to
   'persistent_disk' since there is no current way to determine the image.

Examples:
  Execute uname on all instances in the us-central1-a zone
  $ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"

  Use the GCE inventory script to print out instance specific information
  $ plugins/inventory/gce.py --host my_instance

Author: Eric Johnson <erjohnso@google.com>
Version: 0.0.1
'''

USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
USER_AGENT_VERSION="v1"

import sys
import os
import argparse
import ConfigParser

try:
    import json
except ImportError:
    import simplejson as json

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    _ = Provider.GCE
except:
    print("GCE inventory script requires libcloud >= 0.13")
    sys.exit(1)

class GceInventory(object):
    def __init__(self):
        # Read settings and parse CLI arguments
        self.parse_cli_args()
        self.driver = self.get_gce_driver()

        # Just display data for specific host
        if self.args.host:
            print self.json_format_dict(self.node_to_dict(
                    self.get_instance(self.args.host)))
            sys.exit(0)

        # Otherwise, assume user wants all instances grouped
        print(self.json_format_dict(self.group_instances()))
        sys.exit(0)

    def get_gce_driver(self):
        """Determine the GCE authorization settings and return a
        libcloud driver.
        """
        gce_ini_default_path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "gce.ini")
        gce_ini_path = os.environ.get('GCE_INI_PATH', gce_ini_default_path)

        # Create a ConfigParser.
        # This provides empty defaults to each key, so that environment
        # variable configuration (as opposed to INI configuration) is able
        # to work.
        config = ConfigParser.SafeConfigParser(defaults={
            'gce_service_account_email_address': '',
            'gce_service_account_pem_file_path': '',
            'gce_project_id': '',
            'libcloud_secrets': '',
        })
        if 'gce' not in config.sections():
            config.add_section('gce')
        config.read(gce_ini_path)

        # Attempt to get GCE params from a configuration file, if one
        # exists.
        secrets_path = config.get('gce', 'libcloud_secrets')
        secrets_found = False
        try:
            import secrets
            args = list(getattr(secrets, 'GCE_PARAMS', []))
            kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
            secrets_found = True
        except:
            pass

        if not secrets_found and secrets_path:
            if not secrets_path.endswith('secrets.py'):
                err = "Must specify libcloud secrets file as "
                err += "/absolute/path/to/secrets.py"
                print(err)
                sys.exit(1)
            sys.path.append(os.path.dirname(secrets_path))
            try:
                import secrets
                args = list(getattr(secrets, 'GCE_PARAMS', []))
                kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
                secrets_found = True
            except:
                pass
        if not secrets_found:
            args = [
                config.get('gce','gce_service_account_email_address'),
                config.get('gce','gce_service_account_pem_file_path')
            ]
            kwargs = {'project': config.get('gce', 'gce_project_id')}

        # If the appropriate environment variables are set, they override
        # other configuration; process those into our args and kwargs.
        args[0] = os.environ.get('GCE_EMAIL', args[0])
        args[1] = os.environ.get('GCE_PEM_FILE_PATH', args[1])
        kwargs['project'] = os.environ.get('GCE_PROJECT', kwargs['project'])

        # Retrieve and return the GCE driver.
        gce = get_driver(Provider.GCE)(*args, **kwargs)
        gce.connection.user_agent_append(
            '%s/%s' % (USER_AGENT_PRODUCT, USER_AGENT_VERSION),
        )
        return gce

    def parse_cli_args(self):
        ''' Command line argument processing '''

        parser = argparse.ArgumentParser(
                description='Produce an Ansible Inventory file based on GCE')
        parser.add_argument('--list', action='store_true', default=True,
                           help='List instances (default: True)')
        parser.add_argument('--host', action='store',
                           help='Get all information about an instance')
        self.args = parser.parse_args()

    def node_to_dict(self, inst):
        md = {}

        if inst is None:
            return {}

        if inst.extra['metadata'].has_key('items'):
            for entry in inst.extra['metadata']['items']:
                md[entry['key']] = entry['value']

        net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
        return {
            'gce_uuid': inst.uuid,
            'gce_id': inst.id,
            'gce_image': inst.image,
            'gce_machine_type': inst.size,
            'gce_private_ip': inst.private_ips[0],
            'gce_public_ip': inst.public_ips[0],
            'gce_name': inst.name,
            'gce_description': inst.extra['description'],
            'gce_status': inst.extra['status'],
            'gce_zone': inst.extra['zone'].name,
            'gce_tags': inst.extra['tags'],
            'gce_metadata': md,
            'gce_network': net,
            # Hosts don't have a public name, so we add an IP
            'ansible_ssh_host': inst.public_ips[0]
        }

    def get_instance(self, instance_name):
        '''Gets details about a specific instance '''
        try:
            return self.driver.ex_get_node(instance_name)
        except Exception, e:
            return None

    def group_instances(self):
        '''Group all instances'''
        groups = {}
        for node in self.driver.list_nodes():
            name = node.name

            zone = node.extra['zone'].name
            if groups.has_key(zone): groups[zone].append(name)
            else: groups[zone] = [name]

            tags = node.extra['tags']
            for t in tags:
                tag = 'tag_%s' % t
                if groups.has_key(tag): groups[tag].append(name)
                else: groups[tag] = [name]

            net = node.extra['networkInterfaces'][0]['network'].split('/')[-1]
            net = 'network_%s' % net
            if groups.has_key(net): groups[net].append(name)
            else: groups[net] = [name]

            machine_type = node.size
            if groups.has_key(machine_type): groups[machine_type].append(name)
            else: groups[machine_type] = [name]

            image = node.image and node.image or 'persistent_disk'
            if groups.has_key(image): groups[image].append(name)
            else: groups[image] = [name]

            status = node.extra['status']
            stat = 'status_%s' % status.lower()
            if groups.has_key(stat): groups[stat].append(name)
            else: groups[stat] = [name]
        return groups

    def json_format_dict(self, data, pretty=False):
        ''' Converts a dict to a JSON object and dumps it as a formatted
        string '''

        if pretty:
            return json.dumps(data, sort_keys=True, indent=2)
        else:
            return json.dumps(data)

# Run the script
GceInventory()
<commit_msg>Add --pretty option for gce inventory plugin<commit_after>#!/usr/bin/python
# Copyright 2013 Google Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

'''
GCE external inventory script
=================================

Generates inventory that Ansible can understand by making API requests
Google Compute Engine via the libcloud library.  Full install/configuration
instructions for the gce* modules can be found in the comments of
ansible/test/gce_tests.py.

When run against a specific host, this script returns the following variables
based on the data obtained from the libcloud Node object:
 - gce_uuid
 - gce_id
 - gce_image
 - gce_machine_type
 - gce_private_ip
 - gce_public_ip
 - gce_name
 - gce_description
 - gce_status
 - gce_zone
 - gce_tags
 - gce_metadata
 - gce_network

When run in --list mode, instances are grouped by the following categories:
 - zone:
   zone group name examples are us-central1-b, europe-west1-a, etc.
 - instance tags:
   An entry is created for each tag.  For example, if you have two instances
   with a common tag called 'foo', they will both be grouped together under
   the 'tag_foo' name.
 - network name:
   the name of the network is appended to 'network_' (e.g. the 'default'
   network will result in a group named 'network_default')
 - machine type
   types follow a pattern like n1-standard-4, g1-small, etc.
 - running status:
   group name prefixed with 'status_' (e.g. status_running, status_stopped,..)
 - image:
   when using an ephemeral/scratch disk, this will be set to the image name
   used when creating the instance (e.g. debian-7-wheezy-v20130816).  when
   your instance was created with a root persistent disk it will be set to
   'persistent_disk' since there is no current way to determine the image.

Examples:
  Execute uname on all instances in the us-central1-a zone
  $ ansible -i gce.py us-central1-a -m shell -a "/bin/uname -a"

  Use the GCE inventory script to print out instance specific information
  $ plugins/inventory/gce.py --host my_instance

Author: Eric Johnson <erjohnso@google.com>
Version: 0.0.1
'''

USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
USER_AGENT_VERSION="v1"

import sys
import os
import argparse
import ConfigParser

try:
    import json
except ImportError:
    import simplejson as json

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    _ = Provider.GCE
except:
    print("GCE inventory script requires libcloud >= 0.13")
    sys.exit(1)

class GceInventory(object):
    def __init__(self):
        # Read settings and parse CLI arguments
        self.parse_cli_args()
        self.driver = self.get_gce_driver()

        # Just display data for specific host
        if self.args.host:
            print self.json_format_dict(self.node_to_dict(
                    self.get_instance(self.args.host)),
                    pretty=self.args.pretty)
            sys.exit(0)

        # Otherwise, assume user wants all instances grouped
        print(self.json_format_dict(self.group_instances(),
            pretty=self.args.pretty))
        sys.exit(0)

    def get_gce_driver(self):
        """Determine the GCE authorization settings and return a
        libcloud driver.
        """
        gce_ini_default_path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "gce.ini")
        gce_ini_path = os.environ.get('GCE_INI_PATH', gce_ini_default_path)

        # Create a ConfigParser.
        # This provides empty defaults to each key, so that environment
        # variable configuration (as opposed to INI configuration) is able
        # to work.
        config = ConfigParser.SafeConfigParser(defaults={
            'gce_service_account_email_address': '',
            'gce_service_account_pem_file_path': '',
            'gce_project_id': '',
            'libcloud_secrets': '',
        })
        if 'gce' not in config.sections():
            config.add_section('gce')
        config.read(gce_ini_path)

        # Attempt to get GCE params from a configuration file, if one
        # exists.
        secrets_path = config.get('gce', 'libcloud_secrets')
        secrets_found = False
        try:
            import secrets
            args = list(getattr(secrets, 'GCE_PARAMS', []))
            kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
            secrets_found = True
        except:
            pass

        if not secrets_found and secrets_path:
            if not secrets_path.endswith('secrets.py'):
                err = "Must specify libcloud secrets file as "
                err += "/absolute/path/to/secrets.py"
                print(err)
                sys.exit(1)
            sys.path.append(os.path.dirname(secrets_path))
            try:
                import secrets
                args = list(getattr(secrets, 'GCE_PARAMS', []))
                kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
                secrets_found = True
            except:
                pass
        if not secrets_found:
            args = [
                config.get('gce','gce_service_account_email_address'),
                config.get('gce','gce_service_account_pem_file_path')
            ]
            kwargs = {'project': config.get('gce', 'gce_project_id')}

        # If the appropriate environment variables are set, they override
        # other configuration; process those into our args and kwargs.
        args[0] = os.environ.get('GCE_EMAIL', args[0])
        args[1] = os.environ.get('GCE_PEM_FILE_PATH', args[1])
        kwargs['project'] = os.environ.get('GCE_PROJECT', kwargs['project'])

        # Retrieve and return the GCE driver.
        gce = get_driver(Provider.GCE)(*args, **kwargs)
        gce.connection.user_agent_append(
            '%s/%s' % (USER_AGENT_PRODUCT, USER_AGENT_VERSION),
        )
        return gce

    def parse_cli_args(self):
        ''' Command line argument processing '''

        parser = argparse.ArgumentParser(
                description='Produce an Ansible Inventory file based on GCE')
        parser.add_argument('--list', action='store_true', default=True,
                           help='List instances (default: True)')
        parser.add_argument('--host', action='store',
                           help='Get all information about an instance')
        parser.add_argument('--pretty', action='store_true', default=False,
                           help='Pretty format (default: False)')
        self.args = parser.parse_args()

    def node_to_dict(self, inst):
        md = {}

        if inst is None:
            return {}

        if inst.extra['metadata'].has_key('items'):
            for entry in inst.extra['metadata']['items']:
                md[entry['key']] = entry['value']

        net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
        return {
            'gce_uuid': inst.uuid,
            'gce_id': inst.id,
            'gce_image': inst.image,
            'gce_machine_type': inst.size,
            'gce_private_ip': inst.private_ips[0],
            'gce_public_ip': inst.public_ips[0],
            'gce_name': inst.name,
            'gce_description': inst.extra['description'],
            'gce_status': inst.extra['status'],
            'gce_zone': inst.extra['zone'].name,
            'gce_tags': inst.extra['tags'],
            'gce_metadata': md,
            'gce_network': net,
            # Hosts don't have a public name, so we add an IP
            'ansible_ssh_host': inst.public_ips[0]
        }

    def get_instance(self, instance_name):
        '''Gets details about a specific instance '''
        try:
            return self.driver.ex_get_node(instance_name)
        except Exception, e:
            return None

    def group_instances(self):
        '''Group all instances'''
        groups = {}
        for node in self.driver.list_nodes():
            name = node.name

            zone = node.extra['zone'].name
            if groups.has_key(zone): groups[zone].append(name)
            else: groups[zone] = [name]

            tags = node.extra['tags']
            for t in tags:
                tag = 'tag_%s' % t
                if groups.has_key(tag): groups[tag].append(name)
                else: groups[tag] = [name]

            net = node.extra['networkInterfaces'][0]['network'].split('/')[-1]
            net = 'network_%s' % net
            if groups.has_key(net): groups[net].append(name)
            else: groups[net] = [name]

            machine_type = node.size
            if groups.has_key(machine_type): groups[machine_type].append(name)
            else: groups[machine_type] = [name]

            image = node.image and node.image or 'persistent_disk'
            if groups.has_key(image): groups[image].append(name)
            else: groups[image] = [name]

            status = node.extra['status']
            stat = 'status_%s' % status.lower()
            if groups.has_key(stat): groups[stat].append(name)
            else: groups[stat] = [name]
        return groups

    def json_format_dict(self, data, pretty=False):
        ''' Converts a dict to a JSON object and dumps it as a formatted
        string '''

        if pretty:
            return json.dumps(data, sort_keys=True, indent=2)
        else:
            return json.dumps(data)

# Run the script
GceInventory()
<|endoftext|>


<commit_before>#Pyjsdl - Copyright (C) 2013 James Garnon

#from __future__ import division
from surface import Surface
from time import Time
import env
import pyjsdl.event
from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.FocusPanel import FocusPanel
try:    ###0.15
    from pyjamas.Canvas.HTML5Canvas import HTML5Canvas      ###>IE9
except ImportError:
    from pyjamas.Canvas.GWTCanvas import GWTCanvas as HTML5Canvas
from pyjamas.Canvas import Color
from pyjamas.Canvas.ImageLoader import loadImages
from pyjamas.ui import Event
from pyjamas import DOM
import locals as Const

__docformat__ = 'restructuredtext'

class Canvas(HTML5Canvas):    ###0.15

    def __init__(self, size):
        HTML5Canvas.__init__(self, size[0], size[1])      ###0.15
        self.width = size[0]
        self.height = size[1]
        self.surface = Surface(size)
        self.resize(self.width, self.height)
        self.images = {}
        self.image_list = None
        self.loop = None
        self.run = self._run
        self.time_hold = 1
        self.time_wait = 0
        self.time = Time()
        self.event = pyjsdl.event
        self.addMouseListener(self)
        self.addKeyboardListener(self)
        self.sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP| Event.ONMOUSEMOVE | Event.ONMOUSEOUT | Event.ONKEYDOWN | Event.ONKEYPRESS | Event.ONKEYUP)
        self.modKey = pyjsdl.event.modKey
        self.specialKey = pyjsdl.event.specialKey

    def onMouseMove(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mouseMove['x'], self.event.mouseMove['y'] = x, y
        self.event._updateQueue(event)

    def onMouseDown(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mousePress[event.button] = True
        self.event._updateQueue(event)

    def onMouseUp(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mousePress[event.button] = False
        self.event._updateQueue(event)

    def onMouseLeave(self, sender):
        self.event.mousePress[0], self.event.mousePress[1], self.event.mousePress[2] = False, False, False
        self.event.mouseMove['x'], self.event.mouseMove['y'] = -1, -1
        self.event.mouseMoveRel['x'], self.event.mouseMoveRel['y'] = None, None
        self.event.keyPress['a'], self.event.keyPress['c'], self.event.keyPress['s'] = False, False, False

    def onKeyDown(self, sender, keycode, modifiers):
        if keycode in self.modKey:
            event = DOM.eventGetCurrentEvent()
            self.event.keyPress[keycode] = True
            self.event._updateQueue(event)
            DOM.eventPreventDefault(event)
        elif keycode in self.specialKey:
            event = DOM.eventGetCurrentEvent()
            self.event._updateQueue(event)
            DOM.eventPreventDefault(event)

    def onKeyPress(self, sender, keycode, modifiers):
        event = DOM.eventGetCurrentEvent()
        if not (event.keyCode and event.keyCode in self.specialKey):
            self.event._updateQueue(event)
        DOM.eventPreventDefault(event)

    def onKeyUp(self, sender, keycode, modifiers):
        event = DOM.eventGetCurrentEvent()
        if keycode in self.modKey:
            self.event.keyPress[keycode] = False
        self.event._updateQueue(event)

    def set_loop(self, loop):
        self.loop = loop

    def load_images(self, images):
        if images:
            self.image_list = images
            loadImages(images, self)
        else:
            self.start()

    def set_timeout(self, change):
        self.time_hold += change
        if self.time_hold < 1:
            self.time_hold = 1

    def start(self):
        self.time.timeout(self.time_hold, self)

    def onImagesLoaded(self, images):
        for i, image in enumerate(self.image_list):
            self.images[image] = images[i]
        self.start()

    def set_timeWait(self, time):
        if time:
            self.time_wait = time
            self.run = lambda: None
        else:
            self.time_wait = 0
            self.run = self._run
            self.run()

    def _run(self):
        self.loop()
        self.time.timeout(self.time_hold, self)

class Display(object):
    """
    **pyjsdl.display**

    * pyjsdl.display.init
    * pyjsdl.display.set_mode
    * pyjsdl.display.get_surface
    * pyjsdl.display.get_canvas
    * pyjsdl.display.get_panel
    * pyjsdl.display.quit
    * pyjsdl.display.get_init
    * pyjsdl.display.set_caption
    * pyjsdl.display.clear
    * pyjsdl.display.flip
    * pyjsdl.display.update
    """

    def __init__(self):
        """
        Initialize Display module.

        Module initialization creates pyjsdl.display instance.
        """
        self._initialized = False
        self.init()

    def init(self):
        """
        Initialize display.
        """
        if not self._initialized:
            self.caption = ''
            self.icon = None
            self._nonimplemented_methods()
            self._initialized = True

    def set_mode(self, size):
        """
        Return a display Surface.
        Argument: size (x,y) of surface.
        """
        self.canvas = Canvas(size)
        env.canvas = self.canvas
        panel = FocusPanel(Widget=self.canvas)
        RootPanel().add(panel)
        self.panel = panel
        self.surface = self.canvas.surface
        self.surface._display = self
        return self.surface

    def setup(self, loop, images=None):
        """
        Initialize Canvas for script execution.
        Argument include loop function to run and optional images list to preload.
        """
        self.canvas.set_loop(loop)
        self.canvas.load_images(images)

    def get_surface(self):
        """
        Return display Surface.
        """
        return self.surface

    def get_canvas(self):
        """
        Return Canvas.
        """
        return self.canvas

    def get_panel(self):
        """
        Return Panel.
        """
        return self.panel

    def quit(self):
        """
        Uninitialize display.
        """
        self._initialized = False
        return None

    def get_init(self):
        """
        Check that display module is initialized.
        """
        return self._initialized

    def set_caption(self, caption):
        """
        Set Display.caption.
        Argument: caption text.
        """
        self.caption = caption
        return None

    def clear(self):
        """
        Clear display surface.
        """
        self.surface.beginPath()
        self.surface.setFillStyle(Color.Color(0,0,0))   ###0.15
        self.surface.fillRect(0, 0, self.surface.width, self.surface.height)

    def _nonimplemented_methods(self):
        """
        Non-implemented methods.
        """
        self.set_icon = lambda *arg: None

    def flip(self):
        """
        Repaint display.
        """
        self.canvas.drawImage(self.canvas.surface.canvas, 0, 0)   ###pyjs0.8 problem
#        self.canvas.drawImage(self.canvas.surface, 0, 0)

    def update(self, rect_list=None):
        """
        Repaint display.
        An optional rect_list to specify regions to repaint.
        """
        for rect in rect_list:
            try:
                self.canvas.drawImage(self.canvas.surface.canvas, rect[0], rect[1], rect[2], rect[3], rect[0], rect[1], rect[2], rect[3])   ###pyjs -O no attribute checking of Rect obj
#                self.canvas.drawImage(self.canvas.surface, rect[0], rect[1], rect[2], rect[3], rect[0], rect[1], rect[2], rect[3]) ###pyjs0.8 *.canvas
            except (TypeError, AttributeError): ###pyjs -O TypeError -S AttributeError
                if rect is None:
                    continue
                elif rect_list:
                    self.canvas.drawImage(self.canvas.surface.canvas, rect_list[0], rect_list[1], rect_list[2], rect_list[3], rect_list[0], rect_list[1], rect_list[2], rect_list[3])
#                    self.canvas.drawImage(self.canvas.surface, rect_list[0], rect_list[1], rect_list[2], rect_list[3], rect_list[0], rect_list[1], rect_list[2], rect_list[3]) ###pyjs0.8 *.canvas
                    break
                else:
                    self.canvas.drawImage(self.canvas.surface.canvas, 0, 0)
#                    self.canvas.drawImage(self.canvas.surface, 0, 0) ###pyjs0.8 *.canvas
                    break
        return None

<commit_msg>updated display.update rect processing<commit_after>#Pyjsdl - Copyright (C) 2013 James Garnon

#from __future__ import division
from surface import Surface
from time import Time
import env
import pyjsdl.event
from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.FocusPanel import FocusPanel
try:    ###0.15
    from pyjamas.Canvas.HTML5Canvas import HTML5Canvas      ###>IE9
except ImportError:
    from pyjamas.Canvas.GWTCanvas import GWTCanvas as HTML5Canvas
from pyjamas.Canvas import Color
from pyjamas.Canvas.ImageLoader import loadImages
from pyjamas.ui import Event
from pyjamas import DOM
import locals as Const

__docformat__ = 'restructuredtext'

class Canvas(HTML5Canvas):    ###0.15

    def __init__(self, size):
        HTML5Canvas.__init__(self, size[0], size[1])      ###0.15
        self.width = size[0]
        self.height = size[1]
        self.surface = Surface(size)
        self.resize(self.width, self.height)
        self.images = {}
        self.image_list = None
        self.loop = None
        self.run = self._run
        self.time_hold = 1
        self.time_wait = 0
        self.time = Time()
        self.event = pyjsdl.event
        self.addMouseListener(self)
        self.addKeyboardListener(self)
        self.sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP| Event.ONMOUSEMOVE | Event.ONMOUSEOUT | Event.ONKEYDOWN | Event.ONKEYPRESS | Event.ONKEYUP)
        self.modKey = pyjsdl.event.modKey
        self.specialKey = pyjsdl.event.specialKey

    def onMouseMove(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mouseMove['x'], self.event.mouseMove['y'] = x, y
        self.event._updateQueue(event)

    def onMouseDown(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mousePress[event.button] = True
        self.event._updateQueue(event)

    def onMouseUp(self, sender, x, y):
        event = DOM.eventGetCurrentEvent()
        event.pos = (x, y)
        self.event.mousePress[event.button] = False
        self.event._updateQueue(event)

    def onMouseLeave(self, sender):
        self.event.mousePress[0], self.event.mousePress[1], self.event.mousePress[2] = False, False, False
        self.event.mouseMove['x'], self.event.mouseMove['y'] = -1, -1
        self.event.mouseMoveRel['x'], self.event.mouseMoveRel['y'] = None, None
        self.event.keyPress['a'], self.event.keyPress['c'], self.event.keyPress['s'] = False, False, False

    def onKeyDown(self, sender, keycode, modifiers):
        if keycode in self.modKey:
            event = DOM.eventGetCurrentEvent()
            self.event.keyPress[keycode] = True
            self.event._updateQueue(event)
            DOM.eventPreventDefault(event)
        elif keycode in self.specialKey:
            event = DOM.eventGetCurrentEvent()
            self.event._updateQueue(event)
            DOM.eventPreventDefault(event)

    def onKeyPress(self, sender, keycode, modifiers):
        event = DOM.eventGetCurrentEvent()
        if not (event.keyCode and event.keyCode in self.specialKey):
            self.event._updateQueue(event)
        DOM.eventPreventDefault(event)

    def onKeyUp(self, sender, keycode, modifiers):
        event = DOM.eventGetCurrentEvent()
        if keycode in self.modKey:
            self.event.keyPress[keycode] = False
        self.event._updateQueue(event)

    def set_loop(self, loop):
        self.loop = loop

    def load_images(self, images):
        if images:
            self.image_list = images
            loadImages(images, self)
        else:
            self.start()

    def set_timeout(self, change):
        self.time_hold += change
        if self.time_hold < 1:
            self.time_hold = 1

    def start(self):
        self.time.timeout(self.time_hold, self)

    def onImagesLoaded(self, images):
        for i, image in enumerate(self.image_list):
            self.images[image] = images[i]
        self.start()

    def set_timeWait(self, time):
        if time:
            self.time_wait = time
            self.run = lambda: None
        else:
            self.time_wait = 0
            self.run = self._run
            self.run()

    def _run(self):
        self.loop()
        self.time.timeout(self.time_hold, self)

class Display(object):
    """
    **pyjsdl.display**

    * pyjsdl.display.init
    * pyjsdl.display.set_mode
    * pyjsdl.display.get_surface
    * pyjsdl.display.get_canvas
    * pyjsdl.display.get_panel
    * pyjsdl.display.quit
    * pyjsdl.display.get_init
    * pyjsdl.display.set_caption
    * pyjsdl.display.clear
    * pyjsdl.display.flip
    * pyjsdl.display.update
    """

    def __init__(self):
        """
        Initialize Display module.

        Module initialization creates pyjsdl.display instance.
        """
        self._initialized = False
        self.init()

    def init(self):
        """
        Initialize display.
        """
        if not self._initialized:
            self.caption = ''
            self.icon = None
            self._nonimplemented_methods()
            self._initialized = True

    def set_mode(self, size):
        """
        Return a display Surface.
        Argument: size (x,y) of surface.
        """
        self.canvas = Canvas(size)
        env.canvas = self.canvas
        panel = FocusPanel(Widget=self.canvas)
        RootPanel().add(panel)
        self.panel = panel
        self.surface = self.canvas.surface
        self.surface._display = self
        return self.surface

    def setup(self, loop, images=None):
        """
        Initialize Canvas for script execution.
        Argument include loop function to run and optional images list to preload.
        """
        self.canvas.set_loop(loop)
        self.canvas.load_images(images)

    def get_surface(self):
        """
        Return display Surface.
        """
        return self.surface

    def get_canvas(self):
        """
        Return Canvas.
        """
        return self.canvas

    def get_panel(self):
        """
        Return Panel.
        """
        return self.panel

    def quit(self):
        """
        Uninitialize display.
        """
        self._initialized = False
        return None

    def get_init(self):
        """
        Check that display module is initialized.
        """
        return self._initialized

    def set_caption(self, caption):
        """
        Set Display.caption.
        Argument: caption text.
        """
        self.caption = caption
        return None

    def clear(self):
        """
        Clear display surface.
        """
        self.surface.beginPath()
        self.surface.setFillStyle(Color.Color(0,0,0))   ###0.15
        self.surface.fillRect(0, 0, self.surface.width, self.surface.height)

    def _nonimplemented_methods(self):
        """
        Non-implemented methods.
        """
        self.set_icon = lambda *arg: None

    def flip(self):
        """
        Repaint display.
        """
        self.canvas.drawImage(self.canvas.surface.canvas, 0, 0)   ###pyjs0.8 problem
#        self.canvas.drawImage(self.canvas.surface, 0, 0)

    def update_rect(self, rect_list):       ###0.16
        """
        Repaint display.
        Argument rect_list specifies a list of Rect to repaint.
        """
        for rect in rect_list:
            self.canvas.drawImage(self.canvas.surface.canvas, rect.x,rect.y,rect.width,rect.height, rect.x,rect.y,rect.width,rect.height)
        return None

    def update(self, rect_list=None):
        """
        Repaint display.
        An optional rect_list to specify regions to repaint.
        """
        if not isinstance(rect_list, list):     ###0.16
            if not rect_list:
                self.canvas.drawImage(self.canvas.surface.canvas, 0, 0)
#                self.canvas.drawImage(self.canvas.surface, 0, 0) ##pyjs0.8 *.canvas
                return None
            else:
                rect_list = [rect_list]
        for rect in rect_list:
            try:
                x, y, w, h = rect[0], rect[1], rect[2], rect[3]
                #pyjs -O no attribute checking of Rect obj
                self.canvas.drawImage(self.canvas.surface.canvas, x,y,w,h, x,y,w,h)
#                self.canvas.drawImage(self.canvas.surface, x,y,w,h, x,y,w,h) ###pyjs0.8 *.canvas
            except (TypeError, AttributeError): ###pyjs -O TypeError -S AttributeError
                continue    #rect is None
        return None

<|endoftext|>


<commit_before>import dlvhex
from dlvhex import ID
import hexlite

import atexit
import logging
import os
import re
import sys
import threading
import time

# this requires jpype to be installed and it requires a working Java runtime environment
import jpype
from jpype import java
from jpype.types import *
logging.debug("starting JVM")
java_args = os.environ.get('HEXLITE_JAVA_ARGUMENTS', [])
if java_args != []:
	java_args = java_args.split(' ')
jpype.startJVM(*java_args, convertStrings=False)

def logJavaExceptionWithStacktrace(ex):
	logging.error("Java exception: %s", ex.toString())
	st = ex.getStackTrace()
	for ste in st:
		logging.error("\t at %s", ste.toString())
	#sb.append(ex.getClass().getName() + ": " + ex.getMessage() + "\n");

# this loads the hexlite-API-specific classes (from hexlite-java-plugin-api-XYZ.jar)
IPluginAtom = JClass("at.ac.tuwien.kr.hexlite.api.IPluginAtom")
ISolverContext = JClass("at.ac.tuwien.kr.hexlite.api.ISolverContext")
JStoreAtomException = JClass("at.ac.tuwien.kr.hexlite.api.ISolverContext.StoreAtomException")
IInterpretation = JClass("at.ac.tuwien.kr.hexlite.api.IInterpretation")
ISymbol = JClass("at.ac.tuwien.kr.hexlite.api.ISymbol")

class JavaPluginHolder:
	def __init__(self, classname, jplugin):
		self.classname = classname
		self.jplugin = jplugin

loadedPlugins = []

def convertExtSourceProperties(jesp):
	# convert only those parts that are implemented in hexlite
	ret = dlvhex.ExtSourceProperties()
	ret.setProvidesPartialAnswer(jesp.getProvidesPartialAnswer())
	ret.setDoInputOutputLearning(jesp.getDoInputOutputLearning())
	return ret

def convertInputArguments(jinputarguments):
	def convertOne(t):
		if t == IPluginAtom.InputType.PREDICATE:
			return dlvhex.PREDICATE
		elif t == IPluginAtom.InputType.CONSTANT:
			return dlvhex.CONSTANT
		elif t == IPluginAtom.InputType.TUPLE:
			return dlvhex.TUPLE
		else:
			raise ValueError("unknown input argument type "+repr(t))
	ret = tuple([ convertOne(t) for t in jinputarguments ])
	#logging.debug("XXX converted jinputarguments %s to %s", jinputarguments, ret)
	return ret

@jpype.JImplements(ISymbol)
class JavaSymbolImpl:
	# a JavaSymbolImpl mainly holds a hid (hexlite.ID)
	# (concretely at the moment it always holds a hexlite.clingobackend.ClingoID)
	def __init__(self, hid=None):
		assert(isinstance(hid,ID))
		self.hid = hid
		self.__valuecache = hid.value()
		#logging.info("JavaSymbolImpl with hid %s %s", self.hid, self.__valuecache)

	@jpype.JOverride
	def negate(self):
		#logging.info("want to negate %s", self.hid)
		return JavaSymbolImpl(self.hid.negate())

	@jpype.JOverride
	def value(self):
		#logging.info("value of %s", self.hid)
		return self.hid.value()

	@jpype.JOverride
	def intValue(self):
		#logging.info("intvalue of %s", self.hid)
		return self.hid.intValue()

	@jpype.JOverride
	def isTrue(self):
		#logging.info("isTrue of %s", self.hid)
		return self.hid.isTrue()

	@jpype.JOverride
	def isFalse(self):
		#logging.info("isFalse of %s", self.hid)
		return self.hid.isFalse()

	@jpype.JOverride
	def isAssigned(self):
		#logging.info("isAssigned of %s", self.hid)
		return self.hid.isAssigned()

	@jpype.JOverride
	def tuple(self):
		ret = java.util.ArrayList()
		#logging.info("want to get tuple of %s", self.hid)
		for e in self.hid.tuple():
			ret.add(JavaSymbolImpl(e))
		return ret

	@jpype.JOverride
	def extension(self):
		#logging.info("creating hashset")
		ret = java.util.HashSet()
		#logging.info("filling hashset")
		for e in self.hid.extension():
			#logging.info("adding tuple %s from extension", e)
			#ret.add(e)
			tup = java.util.ArrayList()
			for t in e:
				#jsym = jpype.JObject(JavaSymbolImpl(t))
				jsym = JavaSymbolImpl(t)
				#logging.info("adding symbol %s %s as %s", t, t.__class__, repr(jsym))
				tup.add(jsym)
			#logging.info("adding tuple %s to result as %s", tup, repr(tup))
			ret.add(tup)
		#logging.info("returning %s %s", ret, ret.__class__)
		return ret

	@jpype.JOverride
	def hashCode(self):
		#logging.info("returning hash code for %s", self)
		return int(hash(self.__valuecache) & 0x7FFFFFFF)

	def __eq__(self, other):
		#logging.info("__eq__ got called on %s vs repr(%s)", self.hid, repr(other))
		if not isinstance(other, JavaSymbolImpl):
			return False
		return self.hid == other.hid

	@jpype.JOverride
	def equals(self, other):
		# we could just write self == other, but let's make it explicit that we call above method
		# reminder:
		#   in Java, == only compares memory locations, and content comparison is done with equals()
		#   in Python, == is the same as __eq__ and it may do whatever it wants
		return self.__eq__(other)

	def __str__(self):
		#logging.info("__str__ of %s %s", self.hid, self.__valuecache)
		return str(self.hid)

	@jpype.JOverride
	def toString(self):
		#logging.info("toString of %s %s", self.hid, self.__valuecache)
		return str(self.hid)

@jpype.JImplements(IInterpretation)
class JavaInterpretationImpl:
	def __init__(self):
		pass

	@jpype.JOverride
	def getTrueInputAtoms(self):
		return self._adapt(dlvhex.getTrueInputAtoms())

	@jpype.JOverride
	def getInputAtoms(self):
		return self._adapt(dlvhex.getInputAtoms())

	def _adapt(self, items):
		ret = java.util.HashSet()
		# each atom from dlvhex.getInputAtoms() is converted
		# from hexlite to a java ISymbol
		for x in items:
			#logging.warning("adapting %s", x)
			ret.add(JObject(JavaSymbolImpl(x)))
		return ret

@jpype.JImplements(IPluginAtom.IQuery)
class JavaQueryImpl:
	def __init__(self, arguments):
		self.jinputTuple = jpype.JClass("java.util.ArrayList")()
		# each argument is converted from hexlite to a java ISymbol
		# each argument is an ID or a tuple
		# we follow the structure of the argument
		for arg in arguments:
			#logging.debug("argument is %s", repr(arg))
			assert(isinstance(arg, (JavaSymbolImpl,ISymbol)))
			self.jinputTuple.add(arg)

	@jpype.JOverride
	def getInterpretation(self):
		return JavaInterpretationImpl()

	@jpype.JOverride
	def getInput(self):
		return self.jinputTuple

rValidConstant = re.compile(r'^[a-z][a-z0-9A-Z_]+$')
@jpype.JImplements(ISolverContext)
class JavaSolverContextImpl:
	def __init__(self):
		pass

	@jpype.JOverride
	def storeOutputAtom(self, otuple):
		# all the otuple elements are ISymbol s
		#logging.info("jSC.storeOutputAtom %s", otuple)
		try:
			s = dlvhex.storeOutputAtom([ x.hid for x in otuple ])
		except dlvhex.StoreAtomException as e:
			raise JStoreAtomException(str(e))
		r = JavaSymbolImpl(s)
		ret = jpype.JObject(r, ISymbol)
		#logging.info("jSC.storeOutputAtom %s returns %s with type %s", otuple, repr(ret), type(ret))
		return ret

	@jpype.JOverride
	def getInstantiatedOutputAtoms(self):
		ret = java.util.ArrayList()
		for oa in dlvhex.getInstantiatedOutputAtoms():
			ret.add(jpype.JObject(JavaSymbolImpl(oa), ISymbol))
		#logging.info("jSC.getInstantiatedOutputAtoms returns %s", repr(ret))
		return ret

	@jpype.JOverride
	def storeAtom(self, tuple_):
		# all the tuple_ elements are ISymbol s
		#logging.info("jSC.storeAtom %s", tuple_)
		try:
			s = dlvhex.storeAtom([ x.hid for x in tuple_ ])
		except dlvhex.StoreAtomException as e:
			raise JStoreAtomException(str(e))
		r = JavaSymbolImpl(s)
		ret = jpype.JObject(r, ISymbol)
		#logging.info("jSC.storeAtom %s returns %s with type %s", tuple_, repr(ret), type(ret))
		return ret

	@jpype.JOverride
	def storeConstant(self, s):
		# convert to python string, otherwise various string operations done within hexlite will fail on the java strings
		pythonstr = str(s)
		if len(pythonstr) == 0 or (pythonstr[0] != '"' and pythonstr[-1] != '"' and not rValidConstant.match(pythonstr)):
			raise ValueError("cannot storeConstant for term '{}' with is probably a string (use storeString)".format(pythonstr))
		r = jpype.JObject(JavaSymbolImpl(dlvhex.storeConstant(pythonstr)), ISymbol)
		#logging.info("storeConstant %s returns %s with type %s", s, repr(r), type(r))
		return r

	@jpype.JOverride
	def storeString(self, s):
		pythonstr = str(s)
		r = jpype.JObject(JavaSymbolImpl(dlvhex.storeString(pythonstr)), ISymbol)
		#logging.info("storeString %s returns %s with type %s", s, repr(r), type(r))
		return r

	@jpype.JOverride
	def storeInteger(self, i):
		return JavaSymbolImpl(dlvhex.storeInteger(s))

	@jpype.JOverride
	def learn(self, nogood):
		logging.info("java learns nogood %s", nogood.toString())
		dlvhex.learn([ x.hid for x in nogood ])

def convertArguments(pyArguments):
	# all ID classes stay the same way
	# all tuples become unfolded (only at the end of the list)
	# this is necessary because we do not want Java to get either Tuple or ISymbol as arguments
	if len(pyArguments) == 0:
		return []

	assert(all([ isinstance(a, ID) for a in pyArguments[:-1] ]))
	assert(isinstance(pyArguments[-1], (ID, tuple)))
	ret = [ JavaSymbolImpl(a) for a in pyArguments[:-1] ]
	if isinstance(pyArguments[-1], ID):
		# convert last element as one
		ret.append( JavaSymbolImpl(pyArguments[-1]) )
	else:
		# extend list from converted list of parts of last element (variable length argument list)
		ret.extend([ JavaSymbolImpl(a) for a in pyArguments[-1] ])
	return ret

class JavaPluginCallWrapper:
	def __init__(self, eatomname, pluginholder, pluginatom):
		self.eatomname = eatomname
		self.pluginholder = pluginholder
		self.pluginatom = pluginatom

	def __call__(self, *arguments):
		try:
			logging.debug("executing java __call__ for %s with %d arguments", self.eatomname, len(arguments))
			jsc = JavaSolverContextImpl()
			jq = JavaQueryImpl(convertArguments(arguments))
			#logging.info("executing retrieve")
			janswer = self.pluginatom.retrieve(jsc, jq)
			#logging.debug("retrieved")
			tt = janswer.getTrueTuples()
			#logging.info("true tuples")
			if __debug__:
				# sort for reproducable runs (java hashing is not stable across runs)
				tt = sorted(tt, key=lambda t: t.toString())
			for t in tt:
				logging.debug("true tuple = %s %s", repr(t), t.toString())
				for idx, elem in enumerate(t):
					logging.debug(" idx %d = %s %s", idx, repr(elem), elem.toString())
				assert(all([ isinstance(e, JavaSymbolImpl) for e in t ]))
				tupleOfID = tuple([ e.hid for e in t ])
				#logging.warning("retrieve created output %s for java output %s", tupleOfID, t.toString())
				dlvhex.output(tupleOfID)
		except jpype.JException as e:
			logging.error("plugin call wrapper got Java exception %s %s", e, e.__class__)
			logJavaExceptionWithStacktrace(e)
			raise
		except Exception as e:
			logging.error("plugin call wrapper got exception that is not a JException %s %s", e, e.__class__)
			raise

def register(arguments):
	logging.info("Java API loaded with arguments %s", arguments)

	global loadedPlugins
	for classname in arguments:
		logging.info("loading Java Plugin %s", classname)
		try:
			jclass = jpype.JClass(classname)
			assert(jclass is not None)
			logging.debug("instantiating Plugin")
			jinst = jclass()
			assert(jinst is not None)
			jholder = JavaPluginHolder(classname, jinst)
			assert(jholder is not None)
			loadedPlugins.append(jholder)
			logging.info("registering atoms of plugin %s with name %s", classname, jinst.getName())
			for jpluginatom in jholder.jplugin.createAtoms():
				pred = str(jpluginatom.getPredicate())
				inputArguments = jpluginatom.getInputArguments()
				outputArguments = jpluginatom.getOutputArguments()
				jesp = jpluginatom.getExtSourceProperties()
				prop = convertExtSourceProperties(jesp)
				if pred in globals():
					logging.error("trying to override '%s' in globals - duplicate external atom name or conflict with python internal names", pred)
				else:
					globals()[pred] = JavaPluginCallWrapper(pred, jholder, jpluginatom)
					dlvhex.addAtom(pred, convertInputArguments(inputArguments), int(outputArguments), prop)
		except JException as e:
			logJavaExceptionWithStacktrace(e)
			raise

	logging.info("loaded %d Java plugins", len(loadedPlugins))

def teardown():
	logging.info("teardown: destructing plugins")
	global loadedPlugins
	for p in loadedPlugins:
		try:
			logging.info("teardown on %s", p.classname)
			p.jplugin.teardown()
		except JException as e:
			logJavaExceptionWithStacktrace(e)

	logging.info("teardown: JVM shutdown")
	def watchdog():
		logging.info("watchdog started")
		time.sleep(1)
		logging.info("watchdog still alive -> killing")
		os._exit(-1)
	stt = threading.Thread(target=watchdog, daemon=True)
	stt.start()
	jpype.shutdownJVM()
	logging.info("JVM shutdown successful")

<commit_msg>deactivating jvm shutdown (still hangs)<commit_after>import dlvhex
from dlvhex import ID
import hexlite

import atexit
import logging
import os
import re
import sys
import threading
import time

# this requires jpype to be installed and it requires a working Java runtime environment
import jpype
from jpype import java
from jpype.types import *
logging.debug("starting JVM")
java_args = os.environ.get('HEXLITE_JAVA_ARGUMENTS', [])
if java_args != []:
	java_args = java_args.split(' ')
jpype.startJVM(*java_args, convertStrings=False)

def logJavaExceptionWithStacktrace(ex):
	logging.error("Java exception: %s", ex.toString())
	st = ex.getStackTrace()
	for ste in st:
		logging.error("\t at %s", ste.toString())
	#sb.append(ex.getClass().getName() + ": " + ex.getMessage() + "\n");

# this loads the hexlite-API-specific classes (from hexlite-java-plugin-api-XYZ.jar)
IPluginAtom = JClass("at.ac.tuwien.kr.hexlite.api.IPluginAtom")
ISolverContext = JClass("at.ac.tuwien.kr.hexlite.api.ISolverContext")
JStoreAtomException = JClass("at.ac.tuwien.kr.hexlite.api.ISolverContext.StoreAtomException")
IInterpretation = JClass("at.ac.tuwien.kr.hexlite.api.IInterpretation")
ISymbol = JClass("at.ac.tuwien.kr.hexlite.api.ISymbol")

class JavaPluginHolder:
	def __init__(self, classname, jplugin):
		self.classname = classname
		self.jplugin = jplugin

loadedPlugins = []

def convertExtSourceProperties(jesp):
	# convert only those parts that are implemented in hexlite
	ret = dlvhex.ExtSourceProperties()
	ret.setProvidesPartialAnswer(jesp.getProvidesPartialAnswer())
	ret.setDoInputOutputLearning(jesp.getDoInputOutputLearning())
	return ret

def convertInputArguments(jinputarguments):
	def convertOne(t):
		if t == IPluginAtom.InputType.PREDICATE:
			return dlvhex.PREDICATE
		elif t == IPluginAtom.InputType.CONSTANT:
			return dlvhex.CONSTANT
		elif t == IPluginAtom.InputType.TUPLE:
			return dlvhex.TUPLE
		else:
			raise ValueError("unknown input argument type "+repr(t))
	ret = tuple([ convertOne(t) for t in jinputarguments ])
	#logging.debug("XXX converted jinputarguments %s to %s", jinputarguments, ret)
	return ret

@jpype.JImplements(ISymbol)
class JavaSymbolImpl:
	# a JavaSymbolImpl mainly holds a hid (hexlite.ID)
	# (concretely at the moment it always holds a hexlite.clingobackend.ClingoID)
	def __init__(self, hid=None):
		assert(isinstance(hid,ID))
		self.hid = hid
		self.__valuecache = hid.value()
		#logging.info("JavaSymbolImpl with hid %s %s", self.hid, self.__valuecache)

	@jpype.JOverride
	def negate(self):
		#logging.info("want to negate %s", self.hid)
		return JavaSymbolImpl(self.hid.negate())

	@jpype.JOverride
	def value(self):
		#logging.info("value of %s", self.hid)
		return self.hid.value()

	@jpype.JOverride
	def intValue(self):
		#logging.info("intvalue of %s", self.hid)
		return self.hid.intValue()

	@jpype.JOverride
	def isTrue(self):
		#logging.info("isTrue of %s", self.hid)
		return self.hid.isTrue()

	@jpype.JOverride
	def isFalse(self):
		#logging.info("isFalse of %s", self.hid)
		return self.hid.isFalse()

	@jpype.JOverride
	def isAssigned(self):
		#logging.info("isAssigned of %s", self.hid)
		return self.hid.isAssigned()

	@jpype.JOverride
	def tuple(self):
		ret = java.util.ArrayList()
		#logging.info("want to get tuple of %s", self.hid)
		for e in self.hid.tuple():
			ret.add(JavaSymbolImpl(e))
		return ret

	@jpype.JOverride
	def extension(self):
		#logging.info("creating hashset")
		ret = java.util.HashSet()
		#logging.info("filling hashset")
		for e in self.hid.extension():
			#logging.info("adding tuple %s from extension", e)
			#ret.add(e)
			tup = java.util.ArrayList()
			for t in e:
				#jsym = jpype.JObject(JavaSymbolImpl(t))
				jsym = JavaSymbolImpl(t)
				#logging.info("adding symbol %s %s as %s", t, t.__class__, repr(jsym))
				tup.add(jsym)
			#logging.info("adding tuple %s to result as %s", tup, repr(tup))
			ret.add(tup)
		#logging.info("returning %s %s", ret, ret.__class__)
		return ret

	@jpype.JOverride
	def hashCode(self):
		#logging.info("returning hash code for %s", self)
		return int(hash(self.__valuecache) & 0x7FFFFFFF)

	def __eq__(self, other):
		#logging.info("__eq__ got called on %s vs repr(%s)", self.hid, repr(other))
		if not isinstance(other, JavaSymbolImpl):
			return False
		return self.hid == other.hid

	@jpype.JOverride
	def equals(self, other):
		# we could just write self == other, but let's make it explicit that we call above method
		# reminder:
		#   in Java, == only compares memory locations, and content comparison is done with equals()
		#   in Python, == is the same as __eq__ and it may do whatever it wants
		return self.__eq__(other)

	def __str__(self):
		#logging.info("__str__ of %s %s", self.hid, self.__valuecache)
		return str(self.hid)

	@jpype.JOverride
	def toString(self):
		#logging.info("toString of %s %s", self.hid, self.__valuecache)
		return str(self.hid)

@jpype.JImplements(IInterpretation)
class JavaInterpretationImpl:
	def __init__(self):
		pass

	@jpype.JOverride
	def getTrueInputAtoms(self):
		return self._adapt(dlvhex.getTrueInputAtoms())

	@jpype.JOverride
	def getInputAtoms(self):
		return self._adapt(dlvhex.getInputAtoms())

	def _adapt(self, items):
		ret = java.util.HashSet()
		# each atom from dlvhex.getInputAtoms() is converted
		# from hexlite to a java ISymbol
		for x in items:
			#logging.warning("adapting %s", x)
			ret.add(JObject(JavaSymbolImpl(x)))
		return ret

@jpype.JImplements(IPluginAtom.IQuery)
class JavaQueryImpl:
	def __init__(self, arguments):
		self.jinputTuple = jpype.JClass("java.util.ArrayList")()
		# each argument is converted from hexlite to a java ISymbol
		# each argument is an ID or a tuple
		# we follow the structure of the argument
		for arg in arguments:
			#logging.debug("argument is %s", repr(arg))
			assert(isinstance(arg, (JavaSymbolImpl,ISymbol)))
			self.jinputTuple.add(arg)

	@jpype.JOverride
	def getInterpretation(self):
		return JavaInterpretationImpl()

	@jpype.JOverride
	def getInput(self):
		return self.jinputTuple

rValidConstant = re.compile(r'^[a-z][a-z0-9A-Z_]+$')
@jpype.JImplements(ISolverContext)
class JavaSolverContextImpl:
	def __init__(self):
		pass

	@jpype.JOverride
	def storeOutputAtom(self, otuple):
		# all the otuple elements are ISymbol s
		#logging.info("jSC.storeOutputAtom %s", otuple)
		try:
			s = dlvhex.storeOutputAtom([ x.hid for x in otuple ])
		except dlvhex.StoreAtomException as e:
			raise JStoreAtomException(str(e))
		r = JavaSymbolImpl(s)
		ret = jpype.JObject(r, ISymbol)
		#logging.info("jSC.storeOutputAtom %s returns %s with type %s", otuple, repr(ret), type(ret))
		return ret

	@jpype.JOverride
	def getInstantiatedOutputAtoms(self):
		ret = java.util.ArrayList()
		for oa in dlvhex.getInstantiatedOutputAtoms():
			ret.add(jpype.JObject(JavaSymbolImpl(oa), ISymbol))
		#logging.info("jSC.getInstantiatedOutputAtoms returns %s", repr(ret))
		return ret

	@jpype.JOverride
	def storeAtom(self, tuple_):
		# all the tuple_ elements are ISymbol s
		#logging.info("jSC.storeAtom %s", tuple_)
		try:
			s = dlvhex.storeAtom([ x.hid for x in tuple_ ])
		except dlvhex.StoreAtomException as e:
			raise JStoreAtomException(str(e))
		r = JavaSymbolImpl(s)
		ret = jpype.JObject(r, ISymbol)
		#logging.info("jSC.storeAtom %s returns %s with type %s", tuple_, repr(ret), type(ret))
		return ret

	@jpype.JOverride
	def storeConstant(self, s):
		# convert to python string, otherwise various string operations done within hexlite will fail on the java strings
		pythonstr = str(s)
		if len(pythonstr) == 0 or (pythonstr[0] != '"' and pythonstr[-1] != '"' and not rValidConstant.match(pythonstr)):
			raise ValueError("cannot storeConstant for term '{}' with is probably a string (use storeString)".format(pythonstr))
		r = jpype.JObject(JavaSymbolImpl(dlvhex.storeConstant(pythonstr)), ISymbol)
		#logging.info("storeConstant %s returns %s with type %s", s, repr(r), type(r))
		return r

	@jpype.JOverride
	def storeString(self, s):
		pythonstr = str(s)
		r = jpype.JObject(JavaSymbolImpl(dlvhex.storeString(pythonstr)), ISymbol)
		#logging.info("storeString %s returns %s with type %s", s, repr(r), type(r))
		return r

	@jpype.JOverride
	def storeInteger(self, i):
		return JavaSymbolImpl(dlvhex.storeInteger(s))

	@jpype.JOverride
	def learn(self, nogood):
		logging.info("java learns nogood %s", nogood.toString())
		dlvhex.learn([ x.hid for x in nogood ])

def convertArguments(pyArguments):
	# all ID classes stay the same way
	# all tuples become unfolded (only at the end of the list)
	# this is necessary because we do not want Java to get either Tuple or ISymbol as arguments
	if len(pyArguments) == 0:
		return []

	assert(all([ isinstance(a, ID) for a in pyArguments[:-1] ]))
	assert(isinstance(pyArguments[-1], (ID, tuple)))
	ret = [ JavaSymbolImpl(a) for a in pyArguments[:-1] ]
	if isinstance(pyArguments[-1], ID):
		# convert last element as one
		ret.append( JavaSymbolImpl(pyArguments[-1]) )
	else:
		# extend list from converted list of parts of last element (variable length argument list)
		ret.extend([ JavaSymbolImpl(a) for a in pyArguments[-1] ])
	return ret

class JavaPluginCallWrapper:
	def __init__(self, eatomname, pluginholder, pluginatom):
		self.eatomname = eatomname
		self.pluginholder = pluginholder
		self.pluginatom = pluginatom

	def __call__(self, *arguments):
		try:
			logging.debug("executing java __call__ for %s with %d arguments", self.eatomname, len(arguments))
			jsc = JavaSolverContextImpl()
			jq = JavaQueryImpl(convertArguments(arguments))
			#logging.info("executing retrieve")
			janswer = self.pluginatom.retrieve(jsc, jq)
			#logging.debug("retrieved")
			tt = janswer.getTrueTuples()
			#logging.info("true tuples")
			if __debug__:
				# sort for reproducable runs (java hashing is not stable across runs)
				tt = sorted(tt, key=lambda t: t.toString())
			for t in tt:
				logging.debug("true tuple = %s %s", repr(t), t.toString())
				for idx, elem in enumerate(t):
					logging.debug(" idx %d = %s %s", idx, repr(elem), elem.toString())
				assert(all([ isinstance(e, JavaSymbolImpl) for e in t ]))
				tupleOfID = tuple([ e.hid for e in t ])
				#logging.warning("retrieve created output %s for java output %s", tupleOfID, t.toString())
				dlvhex.output(tupleOfID)
		except jpype.JException as e:
			logging.error("plugin call wrapper got Java exception %s %s", e, e.__class__)
			logJavaExceptionWithStacktrace(e)
			raise
		except Exception as e:
			logging.error("plugin call wrapper got exception that is not a JException %s %s", e, e.__class__)
			raise

def register(arguments):
	logging.info("Java API loaded with arguments %s", arguments)

	global loadedPlugins
	for classname in arguments:
		logging.info("loading Java Plugin %s", classname)
		try:
			jclass = jpype.JClass(classname)
			assert(jclass is not None)
			logging.debug("instantiating Plugin")
			jinst = jclass()
			assert(jinst is not None)
			jholder = JavaPluginHolder(classname, jinst)
			assert(jholder is not None)
			loadedPlugins.append(jholder)
			logging.info("registering atoms of plugin %s with name %s", classname, jinst.getName())
			for jpluginatom in jholder.jplugin.createAtoms():
				pred = str(jpluginatom.getPredicate())
				inputArguments = jpluginatom.getInputArguments()
				outputArguments = jpluginatom.getOutputArguments()
				jesp = jpluginatom.getExtSourceProperties()
				prop = convertExtSourceProperties(jesp)
				if pred in globals():
					logging.error("trying to override '%s' in globals - duplicate external atom name or conflict with python internal names", pred)
				else:
					globals()[pred] = JavaPluginCallWrapper(pred, jholder, jpluginatom)
					dlvhex.addAtom(pred, convertInputArguments(inputArguments), int(outputArguments), prop)
		except JException as e:
			logJavaExceptionWithStacktrace(e)
			raise

	logging.info("loaded %d Java plugins", len(loadedPlugins))

def teardown():
	logging.info("teardown: destructing plugins")
	global loadedPlugins
	for p in loadedPlugins:
		try:
			logging.info("teardown on %s", p.classname)
			p.jplugin.teardown()
		except JException as e:
			logJavaExceptionWithStacktrace(e)

	# logging.info("teardown: JVM shutdown")
	# def watchdog():
	# 	logging.info("watchdog started")
	# 	time.sleep(1)
	# 	logging.info("watchdog still alive -> killing")
	# 	os._exit(-1)
	# stt = threading.Thread(target=watchdog, daemon=True)
	# stt.start()
	# jpype.shutdownJVM()
	# logging.info("JVM shutdown successful")

<|endoftext|>