<commit_before># Base game file
# version 1.4

import sys
import time
import os

# Global Variables, Sort of
global playerHP
global enemyHP
global playerBal
global baitCount
global fishCount
global cookedFish
playerBal = 1000
playerHP = 10
enemyHP = 20
baitCount = 0
fishCount = 0
cookedFish = 0

# Functions
def actionPrompt():
   print("What would you like to do?")
   print("Enter 'help' for more options.")
   action=input("Action: ")
   if (action in actionList):
       if (action==actionList[0]):
           fight()
       elif(action==actionList[1]):
           fish()
       elif(action==actionList[2]):
           #Jesse, we need to-
           cook()
       elif(action==actionList[3]):
           heal()
       elif(action==actionList[4]):
           help_func()
       else:
           print("Something's up...")

def help_func():
    print("Fight: Begins a fight")
    print("Fish: Try to fish for food")
    print("Cook: Cook the fish, or other food")
    print("Help: Gives general information about commands")
    actionPrompt()
def attackOpt():
    print("You attacked!")
    global enemyHP
    global playerHP
    print("Enemy has lost 5 HP")
    time.sleep(0.3)
    print("Enemy attacks! You lost 1 HP")
    enemyHP -= 5
    playerHP -= 1
def run():
    print("You have run away!")
    time.sleep(0.1)
    actionPrompt()
def heal():
    global playerHP
    print("You have healed 2 HP!")
    playerHP += 2
    print("Your health is %i" % playerHP)
def fight():
    while(enemyHP > 1):
        fightActionList = ["a","h","r"]
        print("Your health: %i, enemy health: %i " % (playerHP, enemyHP))
        print("Type 'a' to attack, 'h' to heal, or 'r' to run!")
        action=input("Fight action: ")
        if (action in fightActionList):
             if (action==fightActionList[0]):
                attackOpt()
             elif(action==fightActionList[1]):
                heal()
             elif(action==fightActionList[2]):
                run()
             else:
                print("Something's up...")

def fish():
    global baitCount
    global fishCount
    global playerBal
    print("What would you like to do?")
    print("Type 'f' to fish, 'b' to buy bait, or 'e' to exit")
    fishAction=input("Fish action:")
    if(fishAction=="f"):
        if(baitCount == 0):
            print("You don't have any bait!")
            fish()
        else:
            print("You cast your line..")
            time.sleep(0.4)
            print("You feel a bite!")
            time.sleep(0.2)
            print("You reel in your fish!")
            print("You have caught: %s!" % "a fish")
            baitCount -= 1
            fishCount += 1
            fish()
    if(fishAction=="b"):
        print("Would you like to purchase some bait for 3gp?")
        print("Your balance: %i" % playerBal)
        print("Type 'y' to buy, or 'n' to cancel.")
        buyBait=input("Buy?: ")
        if (buyBait == "y"):
            if (playerBal>=3):
                print("You have bought some bait!")
                playerBal -= 3
                baitCount += 1
                fish()
            else:
                print("You don't have enough gp!")
        elif (buyBait == "n"):
            print("That's not an option!")
            fish()
        else:
            fish()
    if(fishAction=="e"):
        actionPrompt()
    else:
        print("Invalid action!")
        return

def cook():
    global fishCount
    global cookedFish
    print("You have %i raw fish & %i cooked fish." % (fishCount, cookedFish))
    print("Type the amount you'd like to cook, or 'e' to exit!")
    cookAction=input("Cook action: ")
    if(cookAction=="n"):
        actionPrompt()
    else:
        if(int(cookAction) > fishCount):
            print("You don't have that many fish!")
        elif(int(cookAction) <= fishCount):
            fishCount -= int(cookAction)
            cookedFish += int(cookAction)
            print("You have cooked %i fish!" % int(cookAction))
            print("You now have %i raw fish & %i cooked fish!" % (fishCount, cookedFish))
            actionPrompt()
#

# Actions
actionList = ["fight", "fish", "cook", "heal", "help"]
#

#    Variables
# User skills
global attack
global strength
global defence
global health
global fishing
global cooking

attack = 1
strength = 1
defence = 1
health = 1
fishing = 1
cooking = 1
#

# Monsters
monsterGoblin = ["Goblin", "50", "8", "25", "3", "Bones"]
#

# Run program
actionPrompt()
<commit_msg>fishing & monster updates<commit_after># Base game file
# version 1.4

import sys
import time
import os

# Global Variables, Sort of
global playerHP
global enemyHP
global playerBal
global baitCount
global fishCount
global cookedFish
playerBal = 1000
playerHP = 10
enemyHP = 20
baitCount = 0
fishCount = 0
cookedFish = 0

# Functions

def fightMonster():

def actionPrompt():
   print("What would you like to do?")
   print("Enter 'help' for more options.")
   action=input("Action: ")
   if (action in actionList):
       if (action==actionList[0]):
           fight()
       elif(action==actionList[1]):
           fish()
       elif(action==actionList[2]):
           #Jesse, we need to-
           cook()
       elif(action==actionList[3]):
           heal()
       elif(action==actionList[4]):
           help_func()
       else:
           print("Something's up...")

def help_func():
    print("Fight: Begins a fight")
    print("Fish: Try to fish for food")
    print("Cook: Cook the fish, or other food")
    print("Help: Gives general information about commands")
    actionPrompt()
def attackOpt():
    print("You attacked!")
    global enemyHP
    global playerHP
    print("Enemy has lost 5 HP")
    time.sleep(0.3)
    print("Enemy attacks! You lost 1 HP")
    enemyHP -= 5
    playerHP -= 1
def run():
    print("You have run away!")
    time.sleep(0.1)
    actionPrompt()
def heal():
    global cookedFish
    global playerHP
    if(cookedFish > 0):
        print("You have healed 5 HP! You have %i cooked fish left" % cookedFish)
        playerHP += 5
        print("Your health is %i" % playerHP)
        cookedFish -= 1
    else:
        print("Not enough cooked fish!")
def fight():
    while(enemyHP > 1):
        fightActionList = ["a","h","r"]
        print("Your health: %i, enemy health: %i " % (playerHP, enemyHP))
        print("Type 'a' to attack, 'h' to heal, or 'r' to run!")
        action=input("Fight action: ")
        if (action in fightActionList):
             if (action==fightActionList[0]):
                attackOpt()
             elif(action==fightActionList[1]):
                heal()
             elif(action==fightActionList[2]):
                run()
             else:
                print("Something's up...")

def fish():
    global baitCount
    global fishCount
    global playerBal
    print("What would you like to do?")
    print("Type 'f' to fish, 'b' to buy bait, or 'e' to exit")
    fishAction=input("Fish action:")
    if(fishAction=="f"):
        if(baitCount == 0):
            print("You don't have any bait!")
            fish()
        else:
            print("You cast your line..")
            time.sleep(0.4)
            print("You feel a bite!")
            time.sleep(0.2)
            print("You reel in your fish!")
            print("You have caught: %s!" % "a fish")
            baitCount -= 1
            fishCount += 1
            fish()
    if(fishAction=="b"):
        print("Would you like to purchase some bait for 3gp?")
        print("Your balance: %i" % playerBal)
        print("Type 'y' to buy, or 'n' to cancel.")
        buyBait=input("Buy?: ")
        if (buyBait == "y"):
            if (playerBal>=3):
                print("You have bought some bait!")
                playerBal -= 3
                baitCount += 1
                fish()
            else:
                print("You don't have enough gp!")
        elif (buyBait == "n"):
            print("That's not an option!")
            fish()
        else:
            fish()
    if(fishAction=="e"):
        actionPrompt()
    else:
        print("Invalid action!")
        return

def cook():
    global fishCount
    global cookedFish
    print("You have %i raw fish & %i cooked fish." % (fishCount, cookedFish))
    print("Type the amount you'd like to cook, or 'e' to exit!")
    cookAction=input("Cook action: ")
    if(cookAction=="n"):
        actionPrompt()
    else:
        if(int(cookAction) > fishCount):
            print("You don't have that many fish!")
        elif(int(cookAction) <= fishCount):
            fishCount -= int(cookAction)
            cookedFish += int(cookAction)
            print("You have cooked %i fish!" % int(cookAction))
            print("You now have %i raw fish & %i cooked fish!" % (fishCount, cookedFish))
            actionPrompt()
#

# Actions
actionList = ["fight", "fish", "cook", "heal", "help"]
#

#    Variables
# User skills
global attack
global strength
global defence
global health
global fishing
global cooking

attack = 1
strength = 1
defence = 1
health = 1
fishing = 1
cooking = 1
#

# Monsters
global monsterList
monsterList["monsterGoblin","monsterSkeleton","monsterRat","monsterThief"]

global monsterGoblin
global monsterSkeleton
global monsterRat
global monsterThief
monsterGoblin =   ["Goblin",   "50", "8",  "25", "3",    "Bones"]
monsterSkeleton = ["Skeleton", "30", "10", "15", "5",    "Bones"]
monsterRat =      ["Rat",      "10", "1",  "5",  "0",    "Bones"]
monsterThief =    ["Thief",    "75", "15", "38", "100",  "Bones"]
#

# Run program
actionPrompt()
<|endoftext|>


<commit_before>#   Foremast - Pipeline Tooling
#
#   Copyright 2016 Gogo, LLC
#
#   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.

import logging
import zipfile

import boto3
from tryagain import retries

from ..exceptions import RequiredKeyNotFound
from ..utils import get_details, get_properties, get_role_arn, get_security_group_id, get_subnets

LOG = logging.getLogger(__name__)

class LambdaFunction(object):
    """Manipulate Lambda function."""

    def __init__(self, app, env, region, prop_path):
        """Lambda function object.

        Args:
            app (str): Application name
            env (str): Environment/Account
            region (str): AWS Region
            prop_path (str): Path of environment property file
        """
        self.app_name = app
        self.env = env
        self.region = region
        self.properties = get_properties(prop_path)
        generated = get_details(app=self.app_name)
        self.group = generated.data['project']

        try:
            self.pipeline = self.properties['pipeline']['lambda']
        except KeyError:
            raise RequiredKeyNotFound("Lambda key in pipeline.json is required.")

        self.runtime = self.pipeline['runtime']
        self.description = self.pipeline['app_description']
        self.handler = self.pipeline['handler']
        self.vpc_enabled = self.pipeline['vpc_enabled']

        app = self.properties[self.env]['app']
        self.lambda_environment = app['lambda_environment']
        self.memory = app['lambda_memory']
        self.role = app.get('lambda_role') or generated.iam()['lambda_role']
        self.timeout = app['lambda_timeout']

        self.role_arn = get_role_arn(self.role, self.env, self.region)

        self.session = boto3.Session(profile_name=self.env, region_name=self.region)
        self.lambda_client = self.session.client('lambda')

    def _check_lambda(self):
        """Check if lambda function exists.

        Returns:
            True if function does exist
            False if function does not exist
        """
        exists = False
        try:
            self.lambda_client.get_function(FunctionName=self.app_name)
            exists = True
        except boto3.exceptions.botocore.exceptions.ClientError:
            pass
        return exists

    def _check_lambda_alias(self):
        """Check if lambda alias exists.

        Returns:
            True if alias exists
            False if alias does not exist
        """
        aliases = self.lambda_client.list_aliases(FunctionName=self.app_name)

        for alias in aliases['Aliases']:
            if alias['Name'] == self.env:
                LOG.info('Found alias %s for function %s', self.env, self.app_name)
                return True
        else:
            LOG.info('No alias %s found for function %s', self.env, self.app_name)
            return False

    def _vpc_config(self):
        """Get VPC config."""
        if self.vpc_enabled:
            subnets = get_subnets(env=self.env, region=self.region, purpose='internal')['subnet_ids'][self.region]
            security_groups = self._get_sg_ids()

            vpc_config = {'SubnetIds': subnets, 'SecurityGroupIds': security_groups}
        else:
            vpc_config = {'SubnetIds': [], 'SecurityGroupIds': []}
        LOG.debug("Lambda VPC config setup: %s", vpc_config)
        return vpc_config

    def _get_sg_ids(self):
        """Get IDs for all defined security groups.

        Returns:
            list: security group IDs for all lambda_extras
        """
        try:
            lambda_extras = self.properties[self.env]['security_groups']['lambda_extras']
        except KeyError:
            lambda_extras = []

        security_groups = [self.app_name] + lambda_extras
        sg_ids = []
        for sg in security_groups:
            sg_id = get_security_group_id(name=sg, env=self.env, region=self.region)
            sg_ids.append(sg_id)
        return sg_ids

    @retries(max_attempts=3, wait=1, exceptions=(boto3.exceptions.botocore.exceptions.ClientError))
    def create_alias(self):
        """Create lambda alias with env name and points it to $LATEST."""
        LOG.info('Creating alias %s', self.env)

        try:
            self.lambda_client.create_alias(
                FunctionName=self.app_name,
                Name=self.env,
                FunctionVersion='$LATEST',
                Description='Alias for {}'.format(self.env)
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            LOG.debug('Create alias error: %s', error)
            LOG.info("Alias creation failed. Retrying...")
            raise

    @retries(max_attempts=3, wait=1, exceptions=(boto3.exceptions.botocore.exceptions.ClientError))
    def update_alias(self):
        """Update lambda alias to point to $LATEST."""
        LOG.info('Updating alias %s to point to $LATEST', self.env)

        try:
            self.lambda_client.update_alias(
                FunctionName=self.app_name,
                Name=self.env,
                FunctionVersion='$LATEST'
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            LOG.debug('Update alias error: %s', error)
            LOG.info("Alias update failed. Retrying...")
            raise

    @retries(max_attempts=3, wait=1, exceptions=(SystemExit))
    def update_function_configuration(self, vpc_config):
        """Update existing Lambda function configuration.

        Args:
            vpc_config (dict): Dictionary of SubnetIds and SecurityGroupsIds for using
                               a VPC in lambda
        """
        LOG.info('Updating configuration for lambda function: %s', self.app_name)

        try:
            self.lambda_client.update_function_configuration(
                Environment=self.lambda_environment,
                FunctionName=self.app_name,
                Runtime=self.runtime,
                Role=self.role_arn,
                Handler=self.handler,
                Description=self.description,
                Timeout=int(self.timeout),
                MemorySize=int(self.memory),
                VpcConfig=vpc_config
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            if 'CreateNetworkInterface' in error.response['Error']['Message']:
                message = '{0} is missing "ec2:CreateNetworkInterface"'.format(self.role_arn)
                LOG.debug(message)
                raise SystemExit(message)

            raise

        LOG.info("Successfully updated Lambda configuration.")

    @retries(max_attempts=3, wait=1, exceptions=(SystemExit))
    def create_function(self, vpc_config):
        """Create lambda function, configures lambda parameters.

        We need to upload non-zero zip when creating function. Uploading
        hello_world python lambda function since AWS doesn't care which
        executable is in ZIP.

        Args:
            vpc_config (dict): Dictionary of SubnetIds and SecurityGroupsIds for using
                               a VPC in lambda
        """
        zip_file = 'lambda-holder.zip'
        with zipfile.ZipFile(zip_file, mode='w') as z:
            z.writestr('index.py', 'print "Hello world"')

        contents = ''
        with open('lambda-holder.zip', 'rb') as openfile:
            contents = openfile.read()

        LOG.info('Creating lambda function: %s', self.app_name)

        try:
            self.lambda_client.create_function(
                Environment=self.lambda_environment,
                FunctionName=self.app_name,
                Runtime=self.runtime,
                Role=self.role_arn,
                Handler=self.handler,
                Code={
                    'ZipFile': contents
                },
                Description=self.description,
                Timeout=int(self.timeout),
                MemorySize=int(self.memory),
                Publish=False,
                VpcConfig=vpc_config,
                Tags={
                    'app_group': self.group,
                    'app_name': self.app_name
                })
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            if 'CreateNetworkInterface' in error.response['Error']['Message']:
                message = '{0} is missing "ec2:CreateNetworkInterface"'.format(self.role_arn)
                LOG.critical(message)
                raise SystemExit(message)

            raise

        LOG.info("Successfully created Lambda function and alias")

    def create_lambda_function(self):
        """Create or update Lambda function."""
        vpc_config = self._vpc_config()

        if self._check_lambda():
            self.update_function_configuration(vpc_config)
        else:
            self.create_function(vpc_config)

        if self._check_lambda_alias():
            self.update_alias()
        else:
            self.create_alias()
<commit_msg>feat: on function update also update tags<commit_after>#   Foremast - Pipeline Tooling
#
#   Copyright 2016 Gogo, LLC
#
#   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.

import logging
import zipfile

import boto3
from tryagain import retries

from ..exceptions import RequiredKeyNotFound
from ..utils import (get_details, get_lambda_arn, get_properties, get_role_arn,
                     get_security_group_id, get_subnets)

LOG = logging.getLogger(__name__)

class LambdaFunction(object):
    """Manipulate Lambda function."""

    def __init__(self, app, env, region, prop_path):
        """Lambda function object.

        Args:
            app (str): Application name
            env (str): Environment/Account
            region (str): AWS Region
            prop_path (str): Path of environment property file
        """
        self.app_name = app
        self.env = env
        self.region = region
        self.properties = get_properties(prop_path)
        generated = get_details(app=self.app_name)
        self.group = generated.data['project']

        try:
            self.pipeline = self.properties['pipeline']['lambda']
        except KeyError:
            raise RequiredKeyNotFound("Lambda key in pipeline.json is required.")

        self.runtime = self.pipeline['runtime']
        self.description = self.pipeline['app_description']
        self.handler = self.pipeline['handler']
        self.vpc_enabled = self.pipeline['vpc_enabled']

        app = self.properties[self.env]['app']
        self.lambda_environment = app['lambda_environment']
        self.memory = app['lambda_memory']
        self.role = app.get('lambda_role') or generated.iam()['lambda_role']
        self.timeout = app['lambda_timeout']

        self.role_arn = get_role_arn(self.role, self.env, self.region)

        self.session = boto3.Session(profile_name=self.env, region_name=self.region)
        self.lambda_client = self.session.client('lambda')

    def _check_lambda(self):
        """Check if lambda function exists.

        Returns:
            True if function does exist
            False if function does not exist
        """
        exists = False
        try:
            self.lambda_client.get_function(FunctionName=self.app_name)
            exists = True
        except boto3.exceptions.botocore.exceptions.ClientError:
            pass
        return exists

    def _check_lambda_alias(self):
        """Check if lambda alias exists.

        Returns:
            True if alias exists
            False if alias does not exist
        """
        aliases = self.lambda_client.list_aliases(FunctionName=self.app_name)

        for alias in aliases['Aliases']:
            if alias['Name'] == self.env:
                LOG.info('Found alias %s for function %s', self.env, self.app_name)
                return True
        else:
            LOG.info('No alias %s found for function %s', self.env, self.app_name)
            return False

    def _vpc_config(self):
        """Get VPC config."""
        if self.vpc_enabled:
            subnets = get_subnets(env=self.env, region=self.region, purpose='internal')['subnet_ids'][self.region]
            security_groups = self._get_sg_ids()

            vpc_config = {'SubnetIds': subnets, 'SecurityGroupIds': security_groups}
        else:
            vpc_config = {'SubnetIds': [], 'SecurityGroupIds': []}
        LOG.debug("Lambda VPC config setup: %s", vpc_config)
        return vpc_config

    def _get_sg_ids(self):
        """Get IDs for all defined security groups.

        Returns:
            list: security group IDs for all lambda_extras
        """
        try:
            lambda_extras = self.properties[self.env]['security_groups']['lambda_extras']
        except KeyError:
            lambda_extras = []

        security_groups = [self.app_name] + lambda_extras
        sg_ids = []
        for sg in security_groups:
            sg_id = get_security_group_id(name=sg, env=self.env, region=self.region)
            sg_ids.append(sg_id)
        return sg_ids

    @retries(max_attempts=3, wait=1, exceptions=(boto3.exceptions.botocore.exceptions.ClientError))
    def create_alias(self):
        """Create lambda alias with env name and points it to $LATEST."""
        LOG.info('Creating alias %s', self.env)

        try:
            self.lambda_client.create_alias(
                FunctionName=self.app_name,
                Name=self.env,
                FunctionVersion='$LATEST',
                Description='Alias for {}'.format(self.env)
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            LOG.debug('Create alias error: %s', error)
            LOG.info("Alias creation failed. Retrying...")
            raise

    @retries(max_attempts=3, wait=1, exceptions=(boto3.exceptions.botocore.exceptions.ClientError))
    def update_alias(self):
        """Update lambda alias to point to $LATEST."""
        LOG.info('Updating alias %s to point to $LATEST', self.env)

        try:
            self.lambda_client.update_alias(
                FunctionName=self.app_name,
                Name=self.env,
                FunctionVersion='$LATEST'
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            LOG.debug('Update alias error: %s', error)
            LOG.info("Alias update failed. Retrying...")
            raise

    @retries(max_attempts=3, wait=1, exceptions=(SystemExit))
    def update_function_configuration(self, vpc_config):
        """Update existing Lambda function configuration.

        Args:
            vpc_config (dict): Dictionary of SubnetIds and SecurityGroupsIds for using
                               a VPC in lambda
        """
        LOG.info('Updating configuration for lambda function: %s', self.app_name)

        try:
            self.lambda_client.update_function_configuration(
                Environment=self.lambda_environment,
                FunctionName=self.app_name,
                Runtime=self.runtime,
                Role=self.role_arn,
                Handler=self.handler,
                Description=self.description,
                Timeout=int(self.timeout),
                MemorySize=int(self.memory),
                VpcConfig=vpc_config
            )
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            if 'CreateNetworkInterface' in error.response['Error']['Message']:
                message = '{0} is missing "ec2:CreateNetworkInterface"'.format(self.role_arn)
                LOG.debug(message)
                raise SystemExit(message)

            raise
        LOG.info('Updating Lambda function tags')

        lambda_arn = get_lambda_arn(self.app_name, self.env, self.region)
        self.lambda_client.tag_resource(
            Resource = lambda_arn,
            Tags={
                'app_group': self.group,
                'app_name': self.app_name
            })

        LOG.info("Successfully updated Lambda configuration.")

    @retries(max_attempts=3, wait=1, exceptions=(SystemExit))
    def create_function(self, vpc_config):
        """Create lambda function, configures lambda parameters.

        We need to upload non-zero zip when creating function. Uploading
        hello_world python lambda function since AWS doesn't care which
        executable is in ZIP.

        Args:
            vpc_config (dict): Dictionary of SubnetIds and SecurityGroupsIds for using
                               a VPC in lambda
        """
        zip_file = 'lambda-holder.zip'
        with zipfile.ZipFile(zip_file, mode='w') as z:
            z.writestr('index.py', 'print "Hello world"')

        contents = ''
        with open('lambda-holder.zip', 'rb') as openfile:
            contents = openfile.read()

        LOG.info('Creating lambda function: %s', self.app_name)

        try:
            self.lambda_client.create_function(
                Environment=self.lambda_environment,
                FunctionName=self.app_name,
                Runtime=self.runtime,
                Role=self.role_arn,
                Handler=self.handler,
                Code={
                    'ZipFile': contents
                },
                Description=self.description,
                Timeout=int(self.timeout),
                MemorySize=int(self.memory),
                Publish=False,
                VpcConfig=vpc_config,
                Tags={
                    'app_group': self.group,
                    'app_name': self.app_name
                })
        except boto3.exceptions.botocore.exceptions.ClientError as error:
            if 'CreateNetworkInterface' in error.response['Error']['Message']:
                message = '{0} is missing "ec2:CreateNetworkInterface"'.format(self.role_arn)
                LOG.critical(message)
                raise SystemExit(message)

            raise

        LOG.info("Successfully created Lambda function and alias")

    def create_lambda_function(self):
        """Create or update Lambda function."""
        vpc_config = self._vpc_config()

        if self._check_lambda():
            self.update_function_configuration(vpc_config)
        else:
            self.create_function(vpc_config)

        if self._check_lambda_alias():
            self.update_alias()
        else:
            self.create_alias()
<|endoftext|>


<commit_before>import unittest
import unittest.mock as mock

from ogn.parser import parse
from ogn.client.client import create_aprs_login, AprsClient
from ogn.client.settings import APRS_APP_NAME, APRS_APP_VER

class AprsClientTest(unittest.TestCase):
    def test_create_aprs_login(self):
        basic_login = create_aprs_login('klaus', -1, 'myApp', '0.1')
        self.assertEqual('user klaus pass -1 vers myApp 0.1\n', basic_login)

        login_with_filter = create_aprs_login('klaus', -1, 'myApp', '0.1', 'r/48.0/11.0/100')
        self.assertEqual('user klaus pass -1 vers myApp 0.1 filter r/48.0/11.0/100\n', login_with_filter)

    def test_initialisation(self):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        self.assertEqual(client.aprs_user, 'testuser')
        self.assertEqual(client.aprs_filter, '')

    @mock.patch('ogn.client.client.socket')
    def test_connect_full_feed(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        client.sock.send.assert_called_once_with('user testuser pass -1 vers {} {}\n'.format(
                                                 APRS_APP_NAME, APRS_APP_VER).encode('ascii'))
        client.sock.makefile.assert_called_once_with('rw')

    @mock.patch('ogn.client.client.socket')
    def test_connect_client_defined_filter(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='r/50.4976/9.9495/100')
        client.connect()
        client.sock.send.assert_called_once_with('user testuser pass -1 vers {} {} filter r/50.4976/9.9495/100\n'.format(
                                                 APRS_APP_NAME, APRS_APP_VER).encode('ascii'))
        client.sock.makefile.assert_called_once_with('rw')

    @mock.patch('ogn.client.client.socket')
    def test_disconnect(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        client.disconnect()
        client.sock.shutdown.assert_called_once_with(0)
        client.sock.close.assert_called_once_with()
        self.assertTrue(client._kill)

    @mock.patch('ogn.client.client.socket')
    def test_run(self, mock_socket):
        import socket
        mock_socket.error = socket.error

        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()

        client.sock_file.readline = mock.MagicMock()
        client.sock_file.readline.side_effect = ['Normal text blabla',
                                                 'my weird character ¥',
                                                 UnicodeDecodeError('funnycodec', b'\x00\x00', 1, 2, 'This is just a fake reason!'),
                                                 '... show must go on',
                                                 BrokenPipeError(),
                                                 '... and on',
                                                 socket.error(),
                                                 '... and on',
                                                 KeyboardInterrupt()]

        try:
            client.run(callback=lambda msg: print("got: {}".format(msg)), autoreconnect=True)
        except KeyboardInterrupt:
            pass
        finally:
            client.disconnect()

    def test_reset_kill_reconnect(self):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()

        # .run() should be allowed to execute after .connect()
        mock_callback = mock.MagicMock(
            side_effect=lambda raw_msg: client.disconnect())

        self.assertFalse(client._kill)
        client.run(callback=mock_callback, autoreconnect=True)

        # After .disconnect(), client._kill should be True
        self.assertTrue(client._kill)
        self.assertEqual(mock_callback.call_count, 1)

        # After we reconnect, .run() should be able to run again
        mock_callback.reset_mock()
        client.connect()
        client.run(callback=mock_callback, autoreconnect=True)
        self.assertEqual(mock_callback.call_count, 1)

    def test_50_live_messages(self):
        print("Enter")
        self.remaining_messages = 50

        def process_message(raw_message):
            if raw_message[0] == '#':
                return
            try:
                message = parse(raw_message)
                print("{}: {}".format(message['beacon_type'], raw_message))
            except NotImplementedError as e:
                print("{}: {}".format(e, raw_message))
                return
            if self.remaining_messages > 0:
                self.remaining_messages -= 1
            else:
                raise KeyboardInterrupt

        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        try:
            client.run(callback=process_message, autoreconnect=True)
        except KeyboardInterrupt:
            pass
        finally:
            client.disconnect()
        self.assert_(True)
<commit_msg>Test AprsClient gets an empty string<commit_after>import unittest
import unittest.mock as mock

from ogn.parser import parse
from ogn.client.client import create_aprs_login, AprsClient
from ogn.client.settings import APRS_APP_NAME, APRS_APP_VER

class AprsClientTest(unittest.TestCase):
    def test_create_aprs_login(self):
        basic_login = create_aprs_login('klaus', -1, 'myApp', '0.1')
        self.assertEqual('user klaus pass -1 vers myApp 0.1\n', basic_login)

        login_with_filter = create_aprs_login('klaus', -1, 'myApp', '0.1', 'r/48.0/11.0/100')
        self.assertEqual('user klaus pass -1 vers myApp 0.1 filter r/48.0/11.0/100\n', login_with_filter)

    def test_initialisation(self):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        self.assertEqual(client.aprs_user, 'testuser')
        self.assertEqual(client.aprs_filter, '')

    @mock.patch('ogn.client.client.socket')
    def test_connect_full_feed(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        client.sock.send.assert_called_once_with('user testuser pass -1 vers {} {}\n'.format(
                                                 APRS_APP_NAME, APRS_APP_VER).encode('ascii'))
        client.sock.makefile.assert_called_once_with('rw')

    @mock.patch('ogn.client.client.socket')
    def test_connect_client_defined_filter(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='r/50.4976/9.9495/100')
        client.connect()
        client.sock.send.assert_called_once_with('user testuser pass -1 vers {} {} filter r/50.4976/9.9495/100\n'.format(
                                                 APRS_APP_NAME, APRS_APP_VER).encode('ascii'))
        client.sock.makefile.assert_called_once_with('rw')

    @mock.patch('ogn.client.client.socket')
    def test_disconnect(self, mock_socket):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        client.disconnect()
        client.sock.shutdown.assert_called_once_with(0)
        client.sock.close.assert_called_once_with()
        self.assertTrue(client._kill)

    @mock.patch('ogn.client.client.socket')
    def test_run(self, mock_socket):
        import socket
        mock_socket.error = socket.error

        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()

        client.sock_file.readline = mock.MagicMock()
        client.sock_file.readline.side_effect = ['Normal text blabla',
                                                 'my weird character ¥',
                                                 UnicodeDecodeError('funnycodec', b'\x00\x00', 1, 2, 'This is just a fake reason!'),
                                                 '... show must go on',
                                                 BrokenPipeError(),
                                                 '... and on',
                                                 socket.error(),
                                                 '... and on',
                                                 '',
                                                 '... and on',
                                                 KeyboardInterrupt()]

        try:
            client.run(callback=lambda msg: print("got: {}".format(msg)), autoreconnect=True)
        except KeyboardInterrupt:
            pass
        finally:
            client.disconnect()

    def test_reset_kill_reconnect(self):
        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()

        # .run() should be allowed to execute after .connect()
        mock_callback = mock.MagicMock(
            side_effect=lambda raw_msg: client.disconnect())

        self.assertFalse(client._kill)
        client.run(callback=mock_callback, autoreconnect=True)

        # After .disconnect(), client._kill should be True
        self.assertTrue(client._kill)
        self.assertEqual(mock_callback.call_count, 1)

        # After we reconnect, .run() should be able to run again
        mock_callback.reset_mock()
        client.connect()
        client.run(callback=mock_callback, autoreconnect=True)
        self.assertEqual(mock_callback.call_count, 1)

    def test_50_live_messages(self):
        print("Enter")
        self.remaining_messages = 50

        def process_message(raw_message):
            if raw_message[0] == '#':
                return
            try:
                message = parse(raw_message)
                print("{}: {}".format(message['beacon_type'], raw_message))
            except NotImplementedError as e:
                print("{}: {}".format(e, raw_message))
                return
            if self.remaining_messages > 0:
                self.remaining_messages -= 1
            else:
                raise KeyboardInterrupt

        client = AprsClient(aprs_user='testuser', aprs_filter='')
        client.connect()
        try:
            client.run(callback=process_message, autoreconnect=True)
        except KeyboardInterrupt:
            pass
        finally:
            client.disconnect()
        self.assert_(True)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
from FeatureProcessing import *
from ClassificationModule import ClassificationModule
from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.optimizers import Adam
import numpy as np

class basicneuralnetwork(ClassificationModule):
    """A basic feedforward neural network"""

    description = "A basic feedforward neural network"

    def __init__(self, text_corpus, num_hidden_layers=3):

        # Create vectorizer and fit on all available Descriptions
        self.vectorizer = getTextVectorizer(3000) # Maximum of different columns
        corpus = []
        for description in text_corpus:
            corpus.append(process_text(description))
        self.vectorizer.fit(corpus)

        # Set input-size and output_size
        self.input_size = len(self.vectorizer.get_feature_names())
        self.output_size = 7 # Hardcoded for 6 classes

        # Create model
        model = Sequential()
        # Add input-layer
        model.add(Dense(self.input_size, input_dim=self.input_size, init='uniform'))
        model.add(Activation('relu'))

        # Add hidden layers
        for _ in xrange(num_hidden_layers):
            model.add(Dense(self.input_size, init='uniform'))
            model.add(Activation('relu'))

        # Add output layer and normalize probablities with softmax
        model.add(Dense(self.output_size, init='uniform'))
        model.add(Activation('softmax'))

        # Compile model and use Adam as optimizer
        adam = Adam()
        model.compile(metrics=['accuracy'], loss='categorical_crossentropy', optimizer=adam)

        self.model = model
        print('Model build and ready')

    def resetAllTraining(self):
        """Reset classification module to status before training"""
        self.model.compile(metrics=['accuracy'], loss='categorical_crossentropy', optimizer=Adam())

    def trainOnSample(self, sample, nb_epoch=10, shuffle=True, verbose=True):
        """Trainiere (inkrementell) mit Sample. Evtl zusätzlich mit best. Menge alter Daten, damit overfitten auf neue Daten verhindert wird."""
        description_vec = self.formatInputData(sample)
        label_index = getLabelIndex(sample)
        label_one_hot = np.expand_dims(oneHot(label_index), axis=0) # [1, 0, 0, ..] -> [[1, 0, 0, ..]] Necessary for keras
        self.model.fit(description_vec, label_one_hot, nb_epoch=nb_epoch, shuffle=shuffle, verbose=verbose) # TODO: think about nb_epoch-value

    def train(self, samples, nb_epoch=10, shuffle=True, verbose=True):
        """Trainiere mit Liste von Daten. Evtl weitere Paramter nötig (nb_epoch, learning_rate, ...)"""
        train_samples = []
        train_lables = []
        for sample in samples:
            formatted_sample = self.formatInputData(sample)[0].tolist()
            train_samples.append(formatted_sample)
            train_lables.append(oneHot(getLabelIndex(sample)))
        train_lables = np.asarray(train_lables)
        return self.model.fit(train_samples, train_lables, nb_epoch=nb_epoch, shuffle=shuffle, verbose=verbose)

    def predictLabel(self, sample):
        """Gibt zurück, wie der Klassifikator ein gegebenes Sample klassifizieren würde"""
        sample = self.formatInputData(sample)
        return np.argmax(self.model.predict(sample))

    def predictLabelAndProbability(self, sample):
        """Return the probability the module assignes each label"""
        sample = self.formatInputData(sample)
        return self.model.predict(sample)[0] # [0] So 1-D array is returned

    def formatInputData(self, data):
        """Extract description and transform to vector"""
        sd = getDescription(data)
        # Returns numpy array which contains 1 array with features
        return self.vectorizer.transform([sd]).toarray()
<commit_msg>Fixed return-value in neural net<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
from FeatureProcessing import *
from ClassificationModule import ClassificationModule
from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.optimizers import Adam
import numpy as np

class basicneuralnetwork(ClassificationModule):
    """A basic feedforward neural network"""

    description = "A basic feedforward neural network"

    def __init__(self, text_corpus, num_hidden_layers=3):

        # Create vectorizer and fit on all available Descriptions
        self.vectorizer = getTextVectorizer(3000) # Maximum of different columns
        corpus = []
        for description in text_corpus:
            corpus.append(process_text(description))
        self.vectorizer.fit(corpus)

        # Set input-size and output_size
        self.input_size = len(self.vectorizer.get_feature_names())
        self.output_size = 7 # Hardcoded for 6 classes

        # Create model
        model = Sequential()
        # Add input-layer
        model.add(Dense(self.input_size, input_dim=self.input_size, init='uniform'))
        model.add(Activation('relu'))

        # Add hidden layers
        for _ in xrange(num_hidden_layers):
            model.add(Dense(self.input_size, init='uniform'))
            model.add(Activation('relu'))

        # Add output layer and normalize probablities with softmax
        model.add(Dense(self.output_size, init='uniform'))
        model.add(Activation('softmax'))

        # Compile model and use Adam as optimizer
        adam = Adam()
        model.compile(metrics=['accuracy'], loss='categorical_crossentropy', optimizer=adam)

        self.model = model
        print('Model build and ready')

    def resetAllTraining(self):
        """Reset classification module to status before training"""
        self.model.compile(metrics=['accuracy'], loss='categorical_crossentropy', optimizer=Adam())

    def trainOnSample(self, sample, nb_epoch=10, shuffle=True, verbose=True):
        """Trainiere (inkrementell) mit Sample. Evtl zusätzlich mit best. Menge alter Daten, damit overfitten auf neue Daten verhindert wird."""
        description_vec = self.formatInputData(sample)
        label_index = getLabelIndex(sample)
        label_one_hot = np.expand_dims(oneHot(label_index), axis=0) # [1, 0, 0, ..] -> [[1, 0, 0, ..]] Necessary for keras
        self.model.fit(description_vec, label_one_hot, nb_epoch=nb_epoch, shuffle=shuffle, verbose=verbose) # TODO: think about nb_epoch-value

    def train(self, samples, nb_epoch=10, shuffle=True, verbose=True):
        """Trainiere mit Liste von Daten. Evtl weitere Paramter nötig (nb_epoch, learning_rate, ...)"""
        train_samples = []
        train_lables = []
        for sample in samples:
            formatted_sample = self.formatInputData(sample)[0].tolist()
            train_samples.append(formatted_sample)
            train_lables.append(oneHot(getLabelIndex(sample)))
        train_lables = np.asarray(train_lables)
        return self.model.fit(train_samples, train_lables, nb_epoch=nb_epoch, shuffle=shuffle, verbose=verbose)

    def predictLabel(self, sample):
        """Gibt zurück, wie der Klassifikator ein gegebenes Sample klassifizieren würde"""
        sample = self.formatInputData(sample)
        return np.argmax(self.model.predict(sample))

    def predictLabelAndProbability(self, sample):
        """Return the probability the module assignes each label"""
        sample = self.formatInputData(sample)
        prediction = self.model.predict(sample)[0]
        return [np.argmax(prediction)] + prediction # [0] So 1-D array is returned

    def formatInputData(self, data):
        """Extract description and transform to vector"""
        sd = getDescription(data)
        # Returns numpy array which contains 1 array with features
        return self.vectorizer.transform([sd]).toarray()
<|endoftext|>


<commit_before># Copyright 2014 NEC Corporation.  All rights reserved.
#
#    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 magnumclient.common import base
from magnumclient.common import utils
from magnumclient import exceptions

# FIXME: Modify correct attributes.
CREATION_ATTRIBUTES = ['description']

class Pod(base.Resource):
    def __repr__(self):
        return "<Pod %s>" % self._info

class PodManager(base.Manager):
    resource_class = Pod

    @staticmethod
    def _path(id=None):
        return '/v1/pods/%s' % id if id else '/v1/pods'

    def list(self, marker=None, limit=None, sort_key=None,
             sort_dir=None, detail=False):
        """Retrieve a list of pods.

        :param marker: Optional, the UUID of a pods, eg the last
                       pods from a previous result set. Return
                       the next result set.
        :param limit: The maximum number of results to return per
                      request, if:

            1) limit > 0, the maximum number of pods to return.
            2) limit == 0, return the entire list of pods.
            3) limit param is NOT specified (None), the number of items
               returned respect the maximum imposed by the Magnum API
               (see Magnum's api.max_limit option).

        :param sort_key: Optional, field used for sorting.

        :param sort_dir: Optional, direction of sorting, either 'asc' (the
                         default) or 'desc'.

        :param detail: Optional, boolean whether to return detailed information
                       about pods.

        :returns: A list of pods.

        """
        if limit is not None:
            limit = int(limit)

        filters = utils.common_filters(marker, limit, sort_key, sort_dir)

        path = ''
        if detail:
            path += 'detail'
        if filters:
            path += '?' + '&'.join(filters)

        if limit is None:
            return self._list(self._path(path), "pods")
        else:
            return self._list_pagination(self._path(path), "pods",
                                         limit=limit)

    def get(self, pod_id):
        try:
            return self._list(self._path(pod_id))[0]
        except IndexError:
            return None

    def create(self, **kwargs):
        new = {}
        for (key, value) in kwargs.items():
            if key in CREATION_ATTRIBUTES:
                new[key] = value
            else:
                raise exceptions.InvalidAttribute()
        return self._create(self._path(), new)

    def delete(self, pod_id):
        return self._delete(self._path(pod_id))

    def update(self, pod_id, patch):
        return self._update(self._path(pod_id), patch)<commit_msg>Make pod objects work in ReST Client API<commit_after># Copyright 2014 NEC Corporation.  All rights reserved.
#
#    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 magnumclient.common import base
from magnumclient.common import utils
from magnumclient import exceptions

# FIXME: Modify correct attributes.
CREATION_ATTRIBUTES = ['name', 'description']

class Pod(base.Resource):
    def __repr__(self):
        return "<Pod %s>" % self._info

class PodManager(base.Manager):
    resource_class = Pod

    @staticmethod
    def _path(id=None):
        return '/v1/pods/%s' % id if id else '/v1/pods'

    def list(self, marker=None, limit=None, sort_key=None,
             sort_dir=None, detail=False):
        """Retrieve a list of pods.

        :param marker: Optional, the UUID of a pods, eg the last
                       pods from a previous result set. Return
                       the next result set.
        :param limit: The maximum number of results to return per
                      request, if:

            1) limit > 0, the maximum number of pods to return.
            2) limit == 0, return the entire list of pods.
            3) limit param is NOT specified (None), the number of items
               returned respect the maximum imposed by the Magnum API
               (see Magnum's api.max_limit option).

        :param sort_key: Optional, field used for sorting.

        :param sort_dir: Optional, direction of sorting, either 'asc' (the
                         default) or 'desc'.

        :param detail: Optional, boolean whether to return detailed information
                       about pods.

        :returns: A list of pods.

        """
        if limit is not None:
            limit = int(limit)

        filters = utils.common_filters(marker, limit, sort_key, sort_dir)

        path = ''
        if detail:
            path += 'detail'
        if filters:
            path += '?' + '&'.join(filters)

        if limit is None:
            # TODO(yuanying): if endpoint returns "map",
            # change None to response_key
            return self._list(self._path(path), None)
        else:
            # TODO(yuanying): if endpoint returns "map",
            # change None to response_key
            return self._list_pagination(self._path(path), None,
                                         limit=limit)

    def get(self, id):
        try:
            return self._list(self._path(id))[0]
        except IndexError:
            return None

    def create(self, **kwargs):
        new = {}
        for (key, value) in kwargs.items():
            if key in CREATION_ATTRIBUTES:
                new[key] = value
            else:
                raise exceptions.InvalidAttribute()
        return self._create(self._path(), new)

    def delete(self, id):
        return self._delete(self._path(id))

    def update(self, id, patch):
        return self._update(self._path(id), patch)
<|endoftext|>


<commit_before>import hsgame.targeting
from hsgame.constants import CHARACTER_CLASS, CARD_RARITY, MINION_TYPE
from hsgame.game_objects import MinionCard, Minion, Card
from hsgame.cards.battlecries import silence, deal_two_damage

class KeeperOfTheGrove(MinionCard):
    def __init__(self):
        super().__init__("Keeper of the Grove", 4, CHARACTER_CLASS.DRUID, CARD_RARITY.RARE,
                         hsgame.targeting.find_minion_battlecry_target)

    def create_minion(self, player):

        class Moonfire(Card):
            def __init__(self):
                super().__init__("Moonfire", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class Dispel(Card):
            def __init__(self):
                super().__init__("Dispel", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        moonfire = Moonfire()
        dispell = Dispel()
        option = player.agent.choose_option(moonfire, dispell)
        minion = Minion(2, 4)
        if option == moonfire:
            minion.battlecry = deal_two_damage
        else:
            minion.battlecry = silence

        return minion

class DruidOfTheClaw(MinionCard):
    def __init__(self):
        super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.COMMON)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class CatForm(Card):
            def __init__(self):
                super().__init__("Cat Form", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class BearForm(Card):
            def __init__(self):
                super().__init__("Bear Form", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        cat = CatForm()
        bear = BearForm()
        option = player.agent.choose_option(cat, bear)
        if option is cat:
            class CatDruid(MinionCard):
                def __init__(self):
                    super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

                def create_minion(self, p):
                    return Minion(4, 4, charge=True)

            druid = CatDruid()
        else:
            class BearDruid(MinionCard):
                def __init__(self):
                    super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

                def create_minion(self, p):
                    return Minion(4, 6, taunt=True)
            druid = BearDruid()

        def set_card(m):
            minion.card = druid
        minion = druid.create_minion(player)
        player.bind_once("minion_played", set_card)
        return minion

class AncientOfLore(MinionCard):
    def __init__(self):
        super().__init__("Ancient of Lore", 7, CHARACTER_CLASS.DRUID, CARD_RARITY.EPIC)

    def create_minion(self, player):
        # These are basically placeholders to give the agent something to
        # choose.  Note the lack of call to super().use()
        class AncientSecrets(Card):
            def __init__(self):
                super().__init__("Ancient Secrets", 0, CHARACTER_CLASS.DRUID,
                                 CARD_RARITY.SPECIAL)

            def use(self, player, game):
                player.hero.heal(player.effective_heal_power(5), self)

        class AncientTeachings(Card):
            def __init__(self):
                super().__init__("Ancient  Teachings", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                player.draw()
                player.draw()

        option = player.agent.choose_option(AncientSecrets(),
                                            AncientTeachings())
        option.use(player, player.game)

        return Minion(5, 5)

class AncientOfWar(MinionCard):
    def __init__(self):
        super().__init__("Ancient of War", 7, CHARACTER_CLASS.DRUID, CARD_RARITY.EPIC)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class Health(Card):
            def __init__(self):
                super().__init__("+5 Health and Taunt", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class Attack(Card):
            def __init__(self):
                super().__init__("+5 Attack", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        health = Health()
        attack = Attack()
        option = player.agent.choose_option(health, attack)
        minion = Minion(5, 5)
        if option is health:
            minion.increase_health(5)
            minion.taunt = True
        else:
            minion.change_attack(5)

        return minion

class IronbarkProtector(MinionCard):
    def __init__(self):
        super().__init__("Ironbark Protector", 8, CHARACTER_CLASS.DRUID,
                         CARD_RARITY.COMMON)

    def create_minion(self, player):
        return Minion(8, 8, taunt=True)

class Cenarius(MinionCard):
    def __init__(self):
        super().__init__("Cenarius", 9, CHARACTER_CLASS.DRUID,
                         CARD_RARITY.LEGENDARY)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class IncreaseStats(Card):
            def __init__(self):
                super().__init__("Give your other minions +2/+2 and taunt", 0,
                                 CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                for minion in player.minions:
                    if minion is not cenarius:
                        minion.change_attack(2)
                        minion.increase_health(2)
                        minion.taunt = True

            def invoke(self, minion, index):
                self.use(minion.player, minion.game)

        class SummonTreants(Card):
            def __init__(self):
                super().__init__("Summon two 2/2 Treants with taunt", 0,
                                 CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                class Treant(MinionCard):
                    def __init__(self):
                        super().__init__("Treant", 1, CHARACTER_CLASS.DRUID,
                                         CARD_RARITY.COMMON)

                    def create_minion(self, p):
                        minion = Minion(2, 2, MINION_TYPE.NONE)
                        minion.taunt = True
                        return minion
                # TODO Check if Cenarius summons the minions before or after
                # himself
                ltreant = Treant()
                ltreant.summon(player, game, cenarius.index)
                rtreant = Treant()
                rtreant.summon(player, game, cenarius.index + 1)

            def invoke(self, minion, index):
                self.use(minion.player, minion.game)

        option = player.agent.choose_option(IncreaseStats(), SummonTreants())
        cenarius = Minion(5, 8)
        cenarius.bind_once("added_to_board", option.invoke)
        return cenarius
<commit_msg>Fixed a bug related to the ordering of Keeper of the Grove choice<commit_after>import hsgame.targeting
from hsgame.constants import CHARACTER_CLASS, CARD_RARITY, MINION_TYPE
from hsgame.game_objects import MinionCard, Minion, Card
from hsgame.cards.battlecries import silence, deal_two_damage

class KeeperOfTheGrove(MinionCard):
    def __init__(self):
        super().__init__("Keeper of the Grove", 4, CHARACTER_CLASS.DRUID, CARD_RARITY.RARE)

    def create_minion(self, player):

        class Moonfire(Card):
            def __init__(self):
                super().__init__("Moonfire", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class Dispel(Card):
            def __init__(self):
                super().__init__("Dispel", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        moonfire = Moonfire()
        dispell = Dispel()
        option = player.agent.choose_option(moonfire, dispell)
        minion = Minion(2, 4)
        if option == moonfire:
            minion.battlecry = deal_two_damage
            targets = hsgame.targeting.find_battlecry_target(player.game, lambda m: not m.stealth)
        else:
            minion.battlecry = silence
            targets = hsgame.targeting.find_minion_battlecry_target(player.game, lambda m: not m.stealth)

        if targets is not None:
            self.target = player.agent.choose_target(targets)

        return minion

class DruidOfTheClaw(MinionCard):
    def __init__(self):
        super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.COMMON)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class CatForm(Card):
            def __init__(self):
                super().__init__("Cat Form", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class BearForm(Card):
            def __init__(self):
                super().__init__("Bear Form", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        cat = CatForm()
        bear = BearForm()
        option = player.agent.choose_option(cat, bear)
        if option is cat:
            class CatDruid(MinionCard):
                def __init__(self):
                    super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

                def create_minion(self, p):
                    return Minion(4, 4, charge=True)

            druid = CatDruid()
        else:
            class BearDruid(MinionCard):
                def __init__(self):
                    super().__init__("Druid of the Claw", 5, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

                def create_minion(self, p):
                    return Minion(4, 6, taunt=True)
            druid = BearDruid()

        def set_card(m):
            m.card = druid
        minion = druid.create_minion(player)
        player.bind_once("minion_played", set_card)
        return minion

class AncientOfLore(MinionCard):
    def __init__(self):
        super().__init__("Ancient of Lore", 7, CHARACTER_CLASS.DRUID, CARD_RARITY.EPIC)

    def create_minion(self, player):
        # These are basically placeholders to give the agent something to
        # choose.  Note the lack of call to super().use()
        class AncientSecrets(Card):
            def __init__(self):
                super().__init__("Ancient Secrets", 0, CHARACTER_CLASS.DRUID,
                                 CARD_RARITY.SPECIAL)

            def use(self, player, game):
                player.hero.heal(player.effective_heal_power(5), self)

        class AncientTeachings(Card):
            def __init__(self):
                super().__init__("Ancient  Teachings", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                player.draw()
                player.draw()

        option = player.agent.choose_option(AncientSecrets(),
                                            AncientTeachings())
        option.use(player, player.game)

        return Minion(5, 5)

class AncientOfWar(MinionCard):
    def __init__(self):
        super().__init__("Ancient of War", 7, CHARACTER_CLASS.DRUID, CARD_RARITY.EPIC)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class Health(Card):
            def __init__(self):
                super().__init__("+5 Health and Taunt", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        class Attack(Card):
            def __init__(self):
                super().__init__("+5 Attack", 0, CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

        health = Health()
        attack = Attack()
        option = player.agent.choose_option(health, attack)
        minion = Minion(5, 5)
        if option is health:
            minion.increase_health(5)
            minion.taunt = True
        else:
            minion.change_attack(5)

        return minion

class IronbarkProtector(MinionCard):
    def __init__(self):
        super().__init__("Ironbark Protector", 8, CHARACTER_CLASS.DRUID,
                         CARD_RARITY.COMMON)

    def create_minion(self, player):
        return Minion(8, 8, taunt=True)

class Cenarius(MinionCard):
    def __init__(self):
        super().__init__("Cenarius", 9, CHARACTER_CLASS.DRUID,
                         CARD_RARITY.LEGENDARY)

    def create_minion(self, player):

        # These are basically placeholders to give the agent something to
        # choose
        class IncreaseStats(Card):
            def __init__(self):
                super().__init__("Give your other minions +2/+2 and taunt", 0,
                                 CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                for minion in player.minions:
                    if minion is not cenarius:
                        minion.change_attack(2)
                        minion.increase_health(2)
                        minion.taunt = True

            def invoke(self, minion, index):
                self.use(minion.player, minion.game)

        class SummonTreants(Card):
            def __init__(self):
                super().__init__("Summon two 2/2 Treants with taunt", 0,
                                 CHARACTER_CLASS.DRUID, CARD_RARITY.SPECIAL)

            def use(self, player, game):
                class Treant(MinionCard):
                    def __init__(self):
                        super().__init__("Treant", 1, CHARACTER_CLASS.DRUID,
                                         CARD_RARITY.COMMON)

                    def create_minion(self, p):
                        minion = Minion(2, 2, MINION_TYPE.NONE)
                        minion.taunt = True
                        return minion
                # TODO Check if Cenarius summons the minions before or after
                # himself
                ltreant = Treant()
                ltreant.summon(player, game, cenarius.index)
                rtreant = Treant()
                rtreant.summon(player, game, cenarius.index + 1)

            def invoke(self, minion, index):
                self.use(minion.player, minion.game)

        option = player.agent.choose_option(IncreaseStats(), SummonTreants())
        cenarius = Minion(5, 8)
        cenarius.bind_once("added_to_board", option.invoke)
        return cenarius
<|endoftext|>


<commit_before># -*- coding: utf-8 -*- {{{
# vim: set fenc=utf-8 ft=python sw=4 ts=4 sts=4 et:

# Copyright (c) 2015, Battelle Memorial Institute
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation
# are those of the authors and should not be interpreted as representing
# official policies, either expressed or implied, of the FreeBSD
# Project.
#
# This material was prepared as an account of work sponsored by an
# agency of the United States Government.  Neither the United States
# Government nor the United States Department of Energy, nor Battelle,
# nor any of their employees, nor any jurisdiction or organization that
# has cooperated in the development of these materials, makes any
# warranty, express or implied, or assumes any legal liability or
# responsibility for the accuracy, completeness, or usefulness or any
# information, apparatus, product, software, or process disclosed, or
# represents that its use would not infringe privately owned rights.
#
# Reference herein to any specific commercial product, process, or
# service by trade name, trademark, manufacturer, or otherwise does not
# necessarily constitute or imply its endorsement, recommendation, or
# favoring by the United States Government or any agency thereof, or
# Battelle Memorial Institute. The views and opinions of authors
# expressed herein do not necessarily state or reflect those of the
# United States Government or any agency thereof.
#
# PACIFIC NORTHWEST NATIONAL LABORATORY
# operated by BATTELLE for the UNITED STATES DEPARTMENT OF ENERGY
# under Contract DE-AC05-76RL01830

# }}}

import logging
import glob
import os
import os.path
import errno
from string import whitespace
from csv import DictReader
from StringIO import StringIO

from zmq.utils import jsonapi
from gevent.lock import Semaphore

from volttron.utils.persistance import PersistentDict
from volttron.platform.agent.utils import parse_json_config
from volttron.platform.vip.agent import errors

from .vip.agent import Agent, Core, RPC

_log = logging.getLogger(__name__)

store_ext = ".store"
link_prefix = "config://"

def strip_config_name(config_name):
    return config_name.strip(whitespace + r'\/')

def _list_unique_links(config):
    """Returns a set of config files referenced in this configuration"""
    results = set()
    if isinstance(config, dict):
        values = config.values()
    elif isinstance(config, list):
        values = config
    else:
        #Raw config has no links
        return results

    for value in values:
        if isinstance(value, (dict,list)):
            results.update(list_unique_links(value))
        elif isinstance(value, str):
            if value.startswith(link_prefix):
                config_name = value.replace(link_prefix, '', 1)
                config_name = strip_config_name(config_name)
                results.add(config_name)

    return results

def check_for_recursion(new_config_name, new_config, existing_configs):
    return _follow_links(set(), new_config_name, new_config_name, new_config, existing_configs)

def _follow_links(seen, new_config_name, current_config_name, current_config, existing_configs):
    children = _list_unique_links(current_config)

    if new_config_name in children:
        return True

    seen.add(current_config_name)

    for child_config_name in children - seen:
        child_config = existing_configs.get(child_config_name)
        if child_config is None:
            #Link to a non-existing config, skip in the future.
            seen.add(child_config_name)
            continue
        if _follow_links(seen, new_config_name, child_config_name, child_config, existing_configs):
            return True

    return False

def process_store(identity, store):
    """Parses raw store data and returns contents.
    Called at startup to initialize the parsed version of the store."""
    results = {}
    sync_store = False
    for config_name, config_data in store.items():
        config_type = config_data["type"]
        config_string = config_data["data"]
        try:
            processed_config = process_raw_config(config_string, config_type)
            if check_for_recursion(config_name, processed_config, results):
                raise ValueError("Recursive configuration references")
            results[config_name] = processed_config
        except ValueError as e:
            _log.error("Error processing Agent {} config {}: {}".format(identity, config_name, str(e)))
            sync_store = True
            del store[config_name]

    if sync_store:
        _log.warning("Removing invalid configurations for Agent {}".format(identity))
        store.sync()

    return results

def process_raw_config(config_string, config_type="raw"):
    """Parses raw config string into python objects"""
    if config_type == "raw":
        return config_string
    elif config_type == "json":
        config = parse_json_config(config_string)
        if not isinstance(config, (list, dict)):
            raise ValueError("Configuration must be a list or object.")
        return config
    elif config_type == "csv":
        f = StringIO(config_string)
        return [x for x in DictReader(f)]

    raise ValueError("Unsupported configuration type.")

class ConfigStoreService(Agent):
    def __init__(self, *args, **kwargs):
        super(ConfigStoreService, self).__init__(*args, **kwargs)

        self.store = {}
        self.store_path = os.path.join(os.environ['VOLTTRON_HOME'], 'configuration_store')

    @Core.receiver('onsetup')
    def _setup(self, sender, **kwargs):
        _log.info("Initializing configuration store")
        try:
            os.makedirs(self.store_path)
        except OSError as e:
            if e.errno != errno.EEXIST:
                _log.critical("Failed to create configuration store directory: " + str(e))
                raise
            else:
                _log.debug("Configuration directory already exists.")

        config_store_iter = glob.iglob(os.path.join(self.store_path, "*" + store_ext))

        for store_path in config_store_iter:
            root, ext = os.path.splittext(store_path)
            agent_identity = os.path.basename(root)
            store = PersistentDict(filename=store_path, flag='c', format='json')
            parsed_configs = process_store(agent_identity, store)
            self.store[agent_identity] = {"configs": parsed_configs, "store": store, "lock": Semaphore()}

    @RPC.export
    def manage_store(self, identity, config_name, raw_contents, config_type="raw"):
        contents = process_raw_config(raw_contents, config_type)
        self._add_config_to_store(identity, config_name, raw_contents, contents, config_type,
                                  trigger_callback=True)

    @RPC.export
    def manage_delete_config(self, identity, config_name):
        self.delete(identity, config_name, trigger_callback=True)

    @RPC.export
    def manage_delete_store(self, identity):
        agent_store = self.store.get(identity)
        if agent_store is None:
            return

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        agent_configs.clear()
        agent_disk_store.clear()

        # Sync will delete the file if the store is empty.
        agent_disk_store.async_sync()

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", None, "DELETE_ALL", trigger_callback=trigger_callback).get(
                    timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))

        # If the store is still empty (nothing jumped in and added to it while we were informing the agent)
        # then remove it from the global store.
        if not agent_disk_store:
            self.store.pop(identity)

    @RPC.export
    def manage_list_configs(self, identity):
        return self.store.get(identity, {}).get("store", {}).keys()

    @RPC.export
    def manage_list_stores(self, identity):
        return self.store.keys()

    @RPC.export
    def manage_get(self, identity, config_name, raw=True):
        agent_store = self.store.get(identity)
        if agent_store is None:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]

        if config_name not in agent_disk_store:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        if raw:
            return agent_disk_store[config_name]["data"]

        return agent_configs[config_name]

    @RPC.export
    def set_config(self, config_name, contents, trigger_callback=False):
        identity = bytes(self.rpc().context.vip_message.peer)
        self.store(identity, config_name, contents, trigger_callback=trigger_callback)

    @RPC.export
    def get_configs(self):
        """Called by an Agent at startup to get the initial configuration state."""
        identity = bytes(self.rpc().context.vip_message.peer)
        return self.store.get(identity, {}).get("configs", {})

    @RPC.export
    def delete_config(self, config_name, trigger_callback=False):
        """Called by an Agent to delete a configuration."""
        identity = bytes(self.rpc().context.vip_message.peer)
        self.delete(identity, config_name, trigger_callback=trigger_callback)

    #Helper method to allow the local services to delete configs before message bus in online.
    def delete(self, identity, config_name, trigger_callback=False):
        agent_store = self.store.get(identity)
        if agent_store is None:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        if config_name not in agent_disk_store:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs.pop(config_name)
        agent_disk_store.pop(config_name)

        #Sync will delete the file if the store is empty.
        agent_disk_store.async_sync()

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", config_name, "DELETE", trigger_callback=trigger_callback).get(timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))

        #If the store is empty (and nothing jumped in and added to it while we were informing the agent)
        # then remove it from the global store.
        if not agent_disk_store:
            self.store.pop(identity)

    # Helper method to allow the local services to store configs before message bus is online.
    def store(self, identity, config_name, contents, trigger_callback=False):
        config_type = None
        raw_data = None
        if isinstance(contents, (dict, list)):
            config_type = 'json'
            raw_data = jsonapi.dumps(contents)
        elif isinstance(contents, str):
            config_type = 'raw'
            raw_data = contents
        else:
            raise ValueError("Unsupported configuration content type: {}".format(str(type(contents))))

        self._add_config_to_store(identity, config_name, raw_data,contents, config_type, trigger_callback=trigger_callback)

    def _add_config_to_store(self, identity, config_name, raw, parsed, config_type, trigger_callback=False):
        """Adds a processed configuration to the store."""
        agent_store = self.store.get(identity)

        action = "UPDATE"

        if agent_store is None:
            #Initialize a new store.
            store_path = os.path.join(self.store_path, identity+ store_ext)
            store = PersistentDict(filename=store_path, flag='c', format='json')
            agent_store = {"configs": {}, "store": store, "lock": Semaphore()}

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        config_name = strip_config_name(config_name)

        if config_name not in agent_configs:
            action = "NEW"

        if check_for_recursion(config_name, parsed, agent_configs):
            raise ValueError("Recursive configuration references")

        agent_configs[config_name] = parsed
        agent_disk_store[config_name] = {"type": config_type, "data": raw}

        agent_disk_store.async_sync()

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", action, contents=parsed, trigger_callback=trigger_callback).get(timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))
<commit_msg>Fixed various and sundry problems with the config store.<commit_after># -*- coding: utf-8 -*- {{{
# vim: set fenc=utf-8 ft=python sw=4 ts=4 sts=4 et:

# Copyright (c) 2015, Battelle Memorial Institute
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation
# are those of the authors and should not be interpreted as representing
# official policies, either expressed or implied, of the FreeBSD
# Project.
#
# This material was prepared as an account of work sponsored by an
# agency of the United States Government.  Neither the United States
# Government nor the United States Department of Energy, nor Battelle,
# nor any of their employees, nor any jurisdiction or organization that
# has cooperated in the development of these materials, makes any
# warranty, express or implied, or assumes any legal liability or
# responsibility for the accuracy, completeness, or usefulness or any
# information, apparatus, product, software, or process disclosed, or
# represents that its use would not infringe privately owned rights.
#
# Reference herein to any specific commercial product, process, or
# service by trade name, trademark, manufacturer, or otherwise does not
# necessarily constitute or imply its endorsement, recommendation, or
# favoring by the United States Government or any agency thereof, or
# Battelle Memorial Institute. The views and opinions of authors
# expressed herein do not necessarily state or reflect those of the
# United States Government or any agency thereof.
#
# PACIFIC NORTHWEST NATIONAL LABORATORY
# operated by BATTELLE for the UNITED STATES DEPARTMENT OF ENERGY
# under Contract DE-AC05-76RL01830

# }}}

import logging
import glob
import os
import os.path
import errno
from string import whitespace
from csv import DictReader
from StringIO import StringIO

from zmq.utils import jsonapi
from gevent.lock import Semaphore

from volttron.utils.persistance import PersistentDict
from volttron.platform.agent.utils import parse_json_config
from volttron.platform.vip.agent import errors
from volttron.platform.jsonrpc import RemoteError, MethodNotFound

from .vip.agent import Agent, Core, RPC

_log = logging.getLogger(__name__)

store_ext = ".store"
link_prefix = "config://"

def strip_config_name(config_name):
    return config_name.strip(whitespace + r'\/')

def _list_unique_links(config):
    """Returns a set of config files referenced in this configuration"""
    results = set()
    if isinstance(config, dict):
        values = config.values()
    elif isinstance(config, list):
        values = config
    else:
        #Raw config has no links
        return results

    for value in values:
        if isinstance(value, (dict,list)):
            results.update(_list_unique_links(value))
        elif isinstance(value, str):
            if value.startswith(link_prefix):
                config_name = value.replace(link_prefix, '', 1)
                config_name = strip_config_name(config_name)
                results.add(config_name)

    return results

def check_for_recursion(new_config_name, new_config, existing_configs):
    return _follow_links(set(), new_config_name, new_config_name, new_config, existing_configs)

def _follow_links(seen, new_config_name, current_config_name, current_config, existing_configs):
    children = _list_unique_links(current_config)

    if new_config_name in children:
        return True

    seen.add(current_config_name)

    for child_config_name in children - seen:
        child_config = existing_configs.get(child_config_name)
        if child_config is None:
            #Link to a non-existing config, skip in the future.
            seen.add(child_config_name)
            continue
        if _follow_links(seen, new_config_name, child_config_name, child_config, existing_configs):
            return True

    return False

def process_store(identity, store):
    """Parses raw store data and returns contents.
    Called at startup to initialize the parsed version of the store."""
    results = {}
    sync_store = False
    for config_name, config_data in store.items():
        config_type = config_data["type"]
        config_string = config_data["data"]
        try:
            processed_config = process_raw_config(config_string, config_type)
            if check_for_recursion(config_name, processed_config, results):
                raise ValueError("Recursive configuration references")
            results[config_name] = processed_config
        except ValueError as e:
            _log.error("Error processing Agent {} config {}: {}".format(identity, config_name, str(e)))
            sync_store = True
            del store[config_name]

    if sync_store:
        _log.warning("Removing invalid configurations for Agent {}".format(identity))
        store.sync()

    return results

def process_raw_config(config_string, config_type="raw"):
    """Parses raw config string into python objects"""
    if config_type == "raw":
        return config_string
    elif config_type == "json":
        config = parse_json_config(config_string)
        if not isinstance(config, (list, dict)):
            raise ValueError("Configuration must be a list or object.")
        return config
    elif config_type == "csv":
        f = StringIO(config_string)
        return [x for x in DictReader(f)]

    raise ValueError("Unsupported configuration type.")

class ConfigStoreService(Agent):
    def __init__(self, *args, **kwargs):
        super(ConfigStoreService, self).__init__(*args, **kwargs)

        # This agent is started before the router so we need
        # to keep it from blocking.
        self.core.delay_running_event_set = False

        self.store = {}
        self.store_path = os.path.join(os.environ['VOLTTRON_HOME'], 'configuration_store')

    @Core.receiver('onsetup')
    def _setup(self, sender, **kwargs):
        _log.info("Initializing configuration store")
        try:
            os.makedirs(self.store_path)
        except OSError as e:
            if e.errno != errno.EEXIST:
                _log.critical("Failed to create configuration store directory: " + str(e))
                raise
            else:
                _log.debug("Configuration directory already exists.")

        config_store_iter = glob.iglob(os.path.join(self.store_path, "*" + store_ext))

        for store_path in config_store_iter:
            root, ext = os.path.splitext(store_path)
            agent_identity = os.path.basename(root)
            _log.info("Processing store for agent {}".format(agent_identity))
            store = PersistentDict(filename=store_path, flag='c', format='json')
            parsed_configs = process_store(agent_identity, store)
            self.store[agent_identity] = {"configs": parsed_configs, "store": store, "lock": Semaphore()}

    @RPC.export
    def manage_store(self, identity, config_name, raw_contents, config_type="raw"):
        contents = process_raw_config(raw_contents, config_type)
        self._add_config_to_store(identity, config_name, raw_contents, contents, config_type,
                                  trigger_callback=True)

    @RPC.export
    def manage_delete_config(self, identity, config_name):
        self.delete(identity, config_name, trigger_callback=True)

    @RPC.export
    def manage_delete_store(self, identity):
        agent_store = self.store.get(identity)
        if agent_store is None:
            return

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        agent_configs.clear()
        agent_disk_store.clear()

        # Sync will delete the file if the store is empty.
        agent_disk_store.async_sync()

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", None, "DELETE_ALL", trigger_callback=True).get(
                    timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))
            except RemoteError as e:
                _log.error("Agent {} failure when all configurations: {}".format(identity, e))
            except MethodNotFound as e:
                _log.error(
                    "Agent {} failure when adding/updating configuration {}: {}".format(identity, config_name, e))

        # If the store is still empty (nothing jumped in and added to it while we were informing the agent)
        # then remove it from the global store.
        if not agent_disk_store:
            self.store.pop(identity)

    @RPC.export
    def manage_list_configs(self, identity):
        return self.store.get(identity, {}).get("store", {}).keys()

    @RPC.export
    def manage_list_stores(self):
        return self.store.keys()

    @RPC.export
    def manage_get(self, identity, config_name, raw=True):
        agent_store = self.store.get(identity)
        if agent_store is None:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]

        if config_name not in agent_disk_store:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        if raw:
            return agent_disk_store[config_name]["data"]

        return agent_configs[config_name]

    @RPC.export
    def set_config(self, config_name, contents, trigger_callback=False):
        identity = bytes(self.vip.rpc.context.vip_message.peer)
        self.store_config(identity, config_name, contents, trigger_callback=trigger_callback)

    @RPC.export
    def get_configs(self):
        """Called by an Agent at startup to get the initial configuration state."""
        identity = bytes(self.vip.rpc.context.vip_message.peer)
        return self.store.get(identity, {}).get("configs", {})

    @RPC.export
    def delete_config(self, config_name, trigger_callback=False):
        """Called by an Agent to delete a configuration."""
        identity = bytes(self.vip.rpc.context.vip_message.peer)
        self.delete(identity, config_name, trigger_callback=trigger_callback)

    #Helper method to allow the local services to delete configs before message bus in online.
    def delete(self, identity, config_name, trigger_callback=False):
        agent_store = self.store.get(identity)
        if agent_store is None:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        if config_name not in agent_disk_store:
            raise KeyError('No configuration file "{}" for VIP IDENTIY {}'.format(config_name, identity))

        agent_configs.pop(config_name)
        agent_disk_store.pop(config_name)

        #Sync will delete the file if the store is empty.
        agent_disk_store.async_sync()

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", config_name, "DELETE", trigger_callback=trigger_callback).get(timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))
            except RemoteError as e:
                _log.error("Agent {} failure when deleting configuration {}: {}".format(identity, config_name, e))
            except MethodNotFound as e:
                _log.error(
                    "Agent {} failure when adding/updating configuration {}: {}".format(identity, config_name, e))

        #If the store is empty (and nothing jumped in and added to it while we were informing the agent)
        # then remove it from the global store.
        if not agent_disk_store:
            self.store.pop(identity)

    # Helper method to allow the local services to store configs before message bus is online.
    def store_config(self, identity, config_name, contents, trigger_callback=False):
        config_type = None
        raw_data = None
        if isinstance(contents, (dict, list)):
            config_type = 'json'
            raw_data = jsonapi.dumps(contents)
        elif isinstance(contents, str):
            config_type = 'raw'
            raw_data = contents
        else:
            raise ValueError("Unsupported configuration content type: {}".format(str(type(contents))))

        self._add_config_to_store(identity, config_name, raw_data,contents, config_type, trigger_callback=trigger_callback)

    def _add_config_to_store(self, identity, config_name, raw, parsed, config_type, trigger_callback=False):
        """Adds a processed configuration to the store."""
        agent_store = self.store.get(identity)

        action = "UPDATE"

        if agent_store is None:
            #Initialize a new store.
            store_path = os.path.join(self.store_path, identity+ store_ext)
            store = PersistentDict(filename=store_path, flag='c', format='json')
            agent_store = {"configs": {}, "store": store, "lock": Semaphore()}
            self.store[identity] = agent_store

        agent_configs = agent_store["configs"]
        agent_disk_store = agent_store["store"]
        agent_store_lock = agent_store["lock"]

        config_name = strip_config_name(config_name)

        if config_name not in agent_configs:
            action = "NEW"

        if check_for_recursion(config_name, parsed, agent_configs):
            raise ValueError("Recursive configuration references detected.")

        agent_configs[config_name] = parsed
        agent_disk_store[config_name] = {"type": config_type, "data": raw}

        agent_disk_store.async_sync()

        _log.info("Agent {} config {} stored.".format(identity, config_name))

        with agent_store_lock:
            try:
                self.vip.rpc.call(identity, "update_config", action, contents=parsed, trigger_callback=trigger_callback).get(timeout=10.0)
            except errors.Unreachable:
                _log.debug("Agent {} not currently running. Configuration update not sent.".format(identity))
            except RemoteError as e:
                _log.error("Agent {} failure when adding/updating configuration {}: {}".format(identity, config_name, e))
            except MethodNotFound as e:
                _log.error(
                    "Agent {} failure when adding/updating configuration {}: {}".format(identity, config_name, e))
<|endoftext|>