<commit_before>"""This is the state that handles battles against
monsters"""

import pygame as pg
from .. import tools, setup
from .. components import person
from .. import constants as c

SELECT_ACTION = 'select action'
SELECT_ENEMY = 'select enemy'
ENEMY_ATTACK = 'enemy attack'
PLAYER_ATTACK = 'player attack'
SELECT_ITEM = 'select item'
SELECT_MAGIC = 'select magic'
RUN_AWAY = 'run away'

class Battle(tools._State):
    def __init__(self):
        super(Battle, self).__init__()

    def startup(self, current_time, game_data):
        """Initialize state attributes"""
        self.current_time = current_time
        self.game_data = game_data
        self.background = self.make_background()
        self.enemy_group = self.make_enemies()
        self.player_group = self.make_player()
        self.battle_info = BattleInfo()
        self.select_box = SelectBox()
        self.state_dict = self.make_state_dict()
        self.state = SELECT_ACTION
        self.select_action_state_dict = self.make_selection_state_dict()
        self.name = 'battle'
        self.next = game_data['last state']

    def make_background(self):
        """Make the blue/black background"""
        background = pg.sprite.Sprite()
        surface = pg.Surface(c.SCREEN_SIZE).convert()
        surface.fill(c.BLACK_BLUE)
        background.image = surface
        background.rect = background.image.get_rect()
        background_group = pg.sprite.Group(background)

        return background_group

    def make_enemies(self):
        """Make the enemies for the battle. Return sprite group"""
        enemy = person.Person('devil', 100, 220, 'down', 'battle resting')
        enemy.image = pg.transform.scale2x(enemy.image)
        group = pg.sprite.Group(enemy)

        return group

    def make_player(self):
        """Make the sprite for the player's character"""
        player = person.Player('left', 630, 220, 'battle resting', 1)
        player.image = pg.transform.scale2x(player.image)
        player_group = pg.sprite.Group(player)

        return player_group

    def make_state_dict(self):
        """
        Make the dictionary of states the battle can be in.
        """
        state_dict = {SELECT_ACTION: self.select_action,
                      SELECT_ENEMY: self.select_enemy,
                      ENEMY_ATTACK: self.enemy_attack,
                      PLAYER_ATTACK: self.player_attack,
                      RUN_AWAY: self.run_away}

        return state_dict

    def make_selection_state_dict(self):
        """
        Make a dictionary of states with arrow coordinates as keys.
        """
        pos_list = self.select_box.arrow.make_select_action_pos_list()
        state_list = [SELECT_ENEMY, SELECT_ITEM, SELECT_MAGIC, RUN_AWAY]
        return dict(zip(pos_list, state_list))

    def select_action(self):
        """
        Select player action, of either attack, item, magic, run away.
        """
        pass

    def select_enemy(self):
        """
        Select enemy you wish to attack.
        """
        pass

    def enemy_attack(self):
        """
        Enemies, each in turn, attack the player.
        """
        pass

    def player_attack(self):
        """
        Player attacks enemy
        """
        pass

    def run_away(self):
        """
        Player attempts to run away.
        """
        pass

    def update(self, surface, keys, current_time):
        """Update the battle state"""
        self.check_input(keys)
        self.enemy_group.update(current_time)
        self.player_group.update(keys, current_time)
        self.select_box.update(keys)
        self.battle_info.update(self.state)
        self.draw_battle(surface)

    def check_input(self, keys):
        """
        Check user input to navigate GUI.
        """
        arrow = self.select_box.arrow

        if keys[pg.K_RETURN]:
            self.game_data['last state'] = self.name
            self.done = True

        elif keys[pg.K_SPACE]:
            self.state = self.select_action_state_dict[arrow.rect.topleft]

    def draw_battle(self, surface):
        """Draw all elements of battle state"""
        self.background.draw(surface)
        self.enemy_group.draw(surface)
        self.player_group.draw(surface)
        surface.blit(self.battle_info.image, self.battle_info.rect)
        surface.blit(self.select_box.image, self.select_box.rect)

class BattleInfo(object):
    """
    Info box that describes attack damage and other battle
    related information.
    """
    def __init__(self):
        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
        self.message_dict = self.make_message_dict()
        self.image = self.make_image('select action')
        self.rect = self.image.get_rect(bottom=608)

    def make_message_dict(self):
        """
        Make dictionary of states Battle info can be in.
        """
        message_dict = {SELECT_ACTION: 'Select an action.',
                        SELECT_MAGIC: 'Select a magic spell.',
                        SELECT_ITEM: 'Select an item.',
                        SELECT_ENEMY: 'Select an enemy.',
                        ENEMY_ATTACK: 'The enemy attacks player!',
                        PLAYER_ATTACK: 'Player attacks enemy.',
                        RUN_AWAY: 'Run away'}

        return message_dict

    def make_image(self, state):
        """
        Make image out of box and message.
        """
        image = setup.GFX['shopbox']
        rect = image.get_rect(bottom=608)
        surface = pg.Surface(rect.size)
        surface.set_colorkey(c.BLACK)
        surface.blit(image, (0, 0))

        text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK)
        text_rect = text_surface.get_rect(x=50, y=50)
        surface.blit(text_surface, text_rect)

        return surface

    def update(self, state):
        self.image = self.make_image(state)

class SelectBox(object):
    """
    Box to select whether to attack, use item, use magic or run away.
    """
    def __init__(self):
        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
        self.slots = self.make_slots()
        self.arrow = SelectArrow()
        self.image = self.make_image()
        self.rect = self.image.get_rect(bottom=608,
                                        right=800)

    def make_image(self):
        """
        Make the box image for
        """
        image = setup.GFX['goldbox']
        rect = image.get_rect(bottom=608)
        surface = pg.Surface(rect.size)
        surface.set_colorkey(c.BLACK)
        surface.blit(image, (0, 0))

        for text in self.slots:
            text_surface = self.font.render(text, True, c.NEAR_BLACK)
            text_rect = text_surface.get_rect(x=self.slots[text]['x'],
                                              y=self.slots[text]['y'])
            surface.blit(text_surface, text_rect)

        surface.blit(self.arrow.image, self.arrow.rect)

        return surface

    def make_slots(self):
        """
        Make the slots that hold the text selections, and locations.
        """
        slot_dict = {}
        selections = ['Attack', 'Items', 'Magic', 'Run']

        for i, text in enumerate(selections):
            slot_dict[text] = {'x': 150,
                               'y': (i*34)+10}

        return slot_dict

    def update(self, keys):
        """Update components.
        """
        self.arrow.update(keys)
        self.image = self.make_image()

class SelectArrow(object):
    """Small arrow for menu"""
    def __init__(self):
        self.image = setup.GFX['smallarrow']
        self.rect = self.image.get_rect()
        self.state = 'select action'
        self.state_dict = self.make_state_dict()
        self.pos_list = self.make_select_action_pos_list()
        self.index = 0
        self.rect.topleft = self.pos_list[self.index]
        self.allow_input = False

    def make_state_dict(self):
        """Make state dictionary"""
        state_dict = {'select action': self.select_action,
                      'select enemy': self.select_enemy}

        return state_dict

    def select_action(self, keys):
        """
        Select what action the player should take.
        """
        self.pos_list = self.make_select_action_pos_list()
        self.rect.topleft = self.pos_list[self.index]

        if self.allow_input:
            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
                self.index += 1
                self.allow_input = False
            elif keys[pg.K_UP] and self.index > 0:
                self.index -= 1
                self.allow_input = False

        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False:
            self.allow_input = True

    def make_select_action_pos_list(self):
        """
        Make the list of positions the arrow can be in.
        """
        pos_list = []

        for i in range(4):
            x = 50
            y = (i * 34) + 12
            pos_list.append((x, y))

        return pos_list

    def select_enemy(self):
        """
        Select what enemy you want to take action on.
        """
        pass

    def enter_select_action(self):
        """
        Assign values for the select action state.
        """
        pass

    def enter_select_enemy(self):
        """
        Assign values for the select enemy state.
        """
        pass

    def update(self, keys):
        """
        Update arrow position.
        """
        state_function = self.state_dict[self.state]
        state_function(keys)

    def draw(self, surface):
        """
        Draw to surface.
        """
        surface.blit(self.image, self.rect)

<commit_msg>Made an observer, began implementing the select enemy state for the battle.<commit_after>"""This is the state that handles battles against
monsters"""

import random
import pygame as pg
from .. import tools, setup
from .. components import person
from .. import constants as c

#STATES

SELECT_ACTION = 'select action'
SELECT_ENEMY = 'select enemy'
ENEMY_ATTACK = 'enemy attack'
PLAYER_ATTACK = 'player attack'
SELECT_ITEM = 'select item'
SELECT_MAGIC = 'select magic'
RUN_AWAY = 'run away'

#EVENTS

END_BATTLE = 'end battle'

class Battle(tools._State):
    def __init__(self):
        super(Battle, self).__init__()

    def startup(self, current_time, game_data):
        """Initialize state attributes"""
        self.current_time = current_time
        self.game_data = game_data
        self.background = self.make_background()
        self.enemy_pos_list = []
        self.enemy_group = self.make_enemies()
        self.player_group = self.make_player()
        self.info_box = InfoBox()
        self.arrow = SelectArrow(self.enemy_group)
        self.select_box = SelectBox()
        self.state = SELECT_ACTION
        self.select_action_state_dict = self.make_selection_state_dict()
        self.name = 'battle'
        self.next = game_data['last state']
        self.observer = Observer(self)

    def make_background(self):
        """Make the blue/black background"""
        background = pg.sprite.Sprite()
        surface = pg.Surface(c.SCREEN_SIZE).convert()
        surface.fill(c.BLACK_BLUE)
        background.image = surface
        background.rect = background.image.get_rect()
        background_group = pg.sprite.Group(background)

        return background_group

    def make_enemies(self):
        """Make the enemies for the battle. Return sprite group"""
        group = pg.sprite.Group()
        columns =  random.randint(1,3)
        rows = random.randint(1,3)

        for column in range(columns):
            for row in range(rows):
                x = (column * 100) + 100
                y = (row * 100) + 100
                enemy = person.Person('devil', x, y, 'down', 'battle resting')
                enemy.image = pg.transform.scale2x(enemy.image)
                group.add(enemy)
                self.enemy_pos_list.append(enemy.rect.topleft)

        return group

    def make_player(self):
        """Make the sprite for the player's character"""
        player = person.Player('left', 630, 220, 'battle resting', 1)
        player.image = pg.transform.scale2x(player.image)
        player_group = pg.sprite.Group(player)

        return player_group

    def make_selection_state_dict(self):
        """
        Make a dictionary of states with arrow coordinates as keys.
        """
        pos_list = self.arrow.make_select_action_pos_list()
        state_list = [SELECT_ENEMY, SELECT_ITEM, SELECT_MAGIC, RUN_AWAY]
        return dict(zip(pos_list, state_list))

    def update(self, surface, keys, current_time):
        """Update the battle state"""
        self.check_input(keys)
        self.enemy_group.update(current_time)
        self.player_group.update(keys, current_time)
        self.select_box.update()
        self.arrow.update(keys)
        self.info_box.update(self.state)
        self.draw_battle(surface)

    def check_input(self, keys):
        """
        Check user input to navigate GUI.
        """
        if keys[pg.K_RETURN]:
            self.notify(END_BATTLE)

        elif keys[pg.K_SPACE] and self.state == SELECT_ACTION:
            self.state = self.select_action_state_dict[self.arrow.rect.topleft]
            self.notify(self.state)

    def notify(self, event):
        """
        Notify observer of event.
        """
        self.observer.on_notify(event)

    def end_battle(self):
        """
        End battle and flip back to previous state.
        """
        self.game_data['last state'] = self.name
        self.done = True

    def draw_battle(self, surface):
        """Draw all elements of battle state"""
        self.background.draw(surface)
        self.enemy_group.draw(surface)
        self.player_group.draw(surface)
        surface.blit(self.info_box.image, self.info_box.rect)
        surface.blit(self.select_box.image, self.select_box.rect)
        surface.blit(self.arrow.image, self.arrow.rect)

class Observer(object):
    """
    Observes events of battle and passes info to components.
    """
    def __init__(self, level):
        self.level = level
        self.info_box = level.info_box
        self.select_box = level.info_box
        self.arrow = level.arrow
        self.player = level.player_group
        self.enemies = level.enemy_group
        self.event_dict = self.make_event_dict()

    def make_event_dict(self):
        """
        Make a dictionary of events the Observer can
        receive.
        """
        event_dict = {END_BATTLE: self.end_battle,
                      SELECT_ACTION: self.select_action,
                      SELECT_ENEMY: self.select_enemy,
                      ENEMY_ATTACK: self.enemy_attack,
                      PLAYER_ATTACK: self.player_attack,
                      RUN_AWAY: self.run_away}

        return event_dict

    def on_notify(self, event):
        """
        Notify Observer of event.
        """
        if event in self.event_dict:
            self.event_dict[event]()

    def end_battle(self):
        """
        End Battle and flip to previous state.
        """
        self.level.end_battle()

    def select_action(self):
        """
        Set components to select action.
        """
        self.level.state = SELECT_ACTION
        self.arrow.index = 0

    def select_enemy(self):
        self.level.state = SELECT_ENEMY
        self.arrow.state = SELECT_ENEMY

    def enemy_attack(self):
        pass

    def player_attack(self):
        pass

    def run_away(self):
        pass

class InfoBox(object):
    """
    Info box that describes attack damage and other battle
    related information.
    """
    def __init__(self):
        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
        self.message_dict = self.make_message_dict()
        self.image = self.make_image('select action')
        self.rect = self.image.get_rect(bottom=608)

    def make_message_dict(self):
        """
        Make dictionary of states Battle info can be in.
        """
        message_dict = {SELECT_ACTION: 'Select an action.',
                        SELECT_MAGIC: 'Select a magic spell.',
                        SELECT_ITEM: 'Select an item.',
                        SELECT_ENEMY: 'Select an enemy.',
                        ENEMY_ATTACK: 'The enemy attacks player!',
                        PLAYER_ATTACK: 'Player attacks enemy.',
                        RUN_AWAY: 'Run away'}

        return message_dict

    def make_image(self, state):
        """
        Make image out of box and message.
        """
        image = setup.GFX['shopbox']
        rect = image.get_rect(bottom=608)
        surface = pg.Surface(rect.size)
        surface.set_colorkey(c.BLACK)
        surface.blit(image, (0, 0))

        text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK)
        text_rect = text_surface.get_rect(x=50, y=50)
        surface.blit(text_surface, text_rect)

        return surface

    def update(self, state):
        self.image = self.make_image(state)

class SelectBox(object):
    """
    Box to select whether to attack, use item, use magic or run away.
    """
    def __init__(self):
        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
        self.slots = self.make_slots()
        self.image = self.make_image()
        self.rect = self.image.get_rect(bottom=608,
                                        right=800)

    def make_image(self):
        """
        Make the box image for
        """
        image = setup.GFX['goldbox']
        rect = image.get_rect(bottom=608)
        surface = pg.Surface(rect.size)
        surface.set_colorkey(c.BLACK)
        surface.blit(image, (0, 0))

        for text in self.slots:
            text_surface = self.font.render(text, True, c.NEAR_BLACK)
            text_rect = text_surface.get_rect(x=self.slots[text]['x'],
                                              y=self.slots[text]['y'])
            surface.blit(text_surface, text_rect)

        return surface

    def make_slots(self):
        """
        Make the slots that hold the text selections, and locations.
        """
        slot_dict = {}
        selections = ['Attack', 'Items', 'Magic', 'Run']

        for i, text in enumerate(selections):
            slot_dict[text] = {'x': 150,
                               'y': (i*34)+10}

        return slot_dict

    def update(self):
        """
        Update components.
        """
        self.image = self.make_image()

class SelectArrow(object):
    """Small arrow for menu"""
    def __init__(self, enemy_group):
        self.image = setup.GFX['smallarrow']
        self.rect = self.image.get_rect()
        self.state = 'select action'
        self.state_dict = self.make_state_dict()
        self.pos_list = self.make_select_action_pos_list()
        self.index = 0
        self.rect.topleft = self.pos_list[self.index]
        self.allow_input = False
        self.enemies = enemy_group

    def make_state_dict(self):
        """Make state dictionary"""
        state_dict = {'select action': self.select_action,
                      'select enemy': self.select_enemy}

        return state_dict

    def select_action(self, keys):
        """
        Select what action the player should take.
        """
        self.pos_list = self.make_select_action_pos_list()
        self.rect.topleft = self.pos_list[self.index]

        if self.allow_input:
            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
                self.index += 1
                self.allow_input = False
            elif keys[pg.K_UP] and self.index > 0:
                self.index -= 1
                self.allow_input = False

        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False:
            self.allow_input = True

    def make_select_action_pos_list(self):
        """
        Make the list of positions the arrow can be in.
        """
        pos_list = []

        for i in range(4):
            x = 590
            y = (i * 34) + 472
            pos_list.append((x, y))

        return pos_list

    def make_select_enemy_pos_list(self):
        """
        Make the list of positions the arrow can be when selecting
        enemy.
        """
        pos_list = []

        for enemy in self.enemies:
            x = enemy.rect.x - 10
            y = enemy.rect.y - 10
            pos_list.append((x, y))

        print pos_list

        return pos_list

    def select_enemy(self, keys):
        """
        Select what enemy you want to take action on.
        """
        self.pos_list = self.make_select_enemy_pos_list()
        self.rect.topleft = self.pos_list[self.index]

        if self.allow_input:
            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
                self.index += 1
                self.allow_input = False
            elif keys[pg.K_UP] and self.index > 0:
                self.index -= 1
                self.allow_input = False
            elif keys[pg.K_RIGHT] and self.index < (len(self.pos_list) - 4):
                self.index += 3
                self.allow_input = False
            elif keys[pg.K_RIGHT] and self.index >= 3:
                self.index -= 3
                self.allow_input = False

        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \
                and keys[pg.K_RIGHT] and keys[pg.K_LEFT]:
            self.allow_input = True

    def enter_select_action(self):
        """
        Assign values for the select action state.
        """
        pass

    def enter_select_enemy(self):
        """
        Assign values for the select enemy state.
        """
        pass

    def update(self, keys):
        """
        Update arrow position.
        """
        state_function = self.state_dict[self.state]
        state_function(keys)

    def draw(self, surface):
        """
        Draw to surface.
        """
        surface.blit(self.image, self.rect)

<|endoftext|>


<commit_before>#!/usr/bin/env python
# F5 Networks - External Monitor: Azure HA
# https://github.com/ArtiomL/f5networks
# Artiom Lichtenstein
# v0.9.4, 19/08/2016

from argparse import ArgumentParser
from datetime import timedelta
import json
import os
import requests
from signal import SIGKILL
import socket
from subprocess import call
import sys
from time import time

__author__ = 'Artiom Lichtenstein'
__license__ = 'MIT'
__version__ = '0.9.4'

# Log level to /var/log/ltm
intLogLevel = 0
strLogID = '[-v%s-160818-] emon_AZURE_HA.py - ' % __version__

# Azure RM REST API
class clsAREA:
	def __init__(self):
		self.strCFile = '/shared/tmp/scripts/azure/azure_ha.json'
		self.strMgmtURI = 'https://management.azure.com/'
		self.strAPIVer = '?api-version=2016-03-30'

	def funAbsURL(self):
		return self.strMgmtURI, self.strSubID, self.strRGName, self.strAPIVer

	def funURI(self, strMidURI):
		return self.strMgmtURI + strMidURI + self.strAPIVer

	def funBear(self):
		return { 'Authorization': 'Bearer %s' % self.strBearer }

objAREA = clsAREA()

# Logger command
strLogger = 'logger -p local0.'

# Exit codes
class clsExCodes:
	def __init__(self):
		self.args = 8
		self.armAuth = 4

objExCodes = clsExCodes()

def funLog(intMesLevel, strMessage, strSeverity='info ', strMethod='log'):
	if intLogLevel >= intMesLevel:
		if strMethod == 'stdout':
			print strMessage
		else:
			lstCmd = (strLogger + strSeverity).split(' ')
			lstCmd.append(strLogID + strMessage)
			call(lstCmd)

def funARMAuth():
	# Azure RM OAuth2
	global objAREA
	# Read external config file
	if not os.path.isfile(objAREA.strCFile):
		funLog(1, 'Credentials file: %s is missing!' % objAREA.strCFile, 'err')
		return 3

	try:
		# Open the credentials file
		with open(objAREA.strCFile, 'r') as f:
			diCreds = json.load(f)
		# Read subscription and resource group
		objAREA.strSubID = diCreds['subID']
		objAREA.strRGName = diCreds['rgName']
		# Current epoch time
		intEpNow = int(time())
		# Check if Bearer token exists (in credentials file) and whether it can be reused (expiration with 1 minute time skew)
		if (set(('bearer', 'expiresOn')) <= set(diCreds) and int(diCreds['expiresOn']) - 60 > intEpNow):
			objAREA.strBearer = diCreds['bearer'].decode('base64')
			funLog(2, 'Reusing existing Bearer, it expires in %s' % str(timedelta(seconds=int(diCreds['expiresOn']) - intEpNow)))
			return 0

		# Read additional config parameters
		strTenantID = diCreds['tenantID']
		strAppID = diCreds['appID']
		strPass = diCreds['pass'].decode('base64')
		strEndPt = 'https://login.microsoftonline.com/%s/oauth2/token' % strTenantID
	except Exception as e:
		funLog(1, 'Invalid credentials file: %s' % objAREA.strCFile, 'err')
		return 2

	# Generate new Bearer token
	diPayload = { 'grant_type': 'client_credentials', 'client_id': strAppID, 'client_secret': strPass, 'resource': objAREA.strMgmtURI }
	try:
		objHResp = requests.post(url=strEndPt, data=diPayload)
		diAuth = json.loads(objHResp.content)
		if 'access_token' in diAuth.keys():
			# Successfully received new token
			objAREA.strBearer = diAuth['access_token']
			# Write the new token and its expiration epoch into the credentials file
			diCreds['bearer'] = objAREA.strBearer.encode('base64')
			diCreds['expiresOn'] = diAuth['expires_on']
			with open(objAREA.strCFile, 'w') as f:
				f.write(json.dumps(diCreds, sort_keys=True, indent=4, separators=(',', ': ')))
			return 0

	except requests.exceptions.RequestException as e:
		funLog(2, str(e), 'err')
	return 1

def funLocIP(strRemIP):
	# Get local private IP
	objUDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
	# The .connect method doesn't generate any real network traffic for UDP (socket.SOCK_DGRAM)
	objUDP.connect((strRemIP, 0))
	return objUDP.getsockname()[0]

def funCurState(strLocIP, strPeerIP):
	# Get current ARM state for the local machine
	global objAREA
	funLog(2, 'Current local private IP: %s, Resource Group: %s' % (strLocIP, objAREA.strRGName))
	# Construct loadBalancers URL
	strLBURL = '%ssubscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers%s' % objAREA.funAbsURL()
	diHeaders = objAREA.funBear()
	try:
		# Get LBAZ JSON
		objHResp = requests.get(strLBURL, headers = diHeaders)
		# Store the backend pool JSON (for funFailover)
		objAREA.diBEPool = json.loads(objHResp.content)['value'][0]['properties']['backendAddressPools']
		# Extract backend IP ID ([1:] at the end removes the first "/" char)
		strBEIPURI = objAREA.diBEPool[0]['properties']['backendIPConfigurations'][0]['id'][1:]
		# Store the URI for NIC currently in the backend pool (for funFailover)
		objAREA.strCurNICURI = strBEIPURI.split('ipConfiguration')[0]
		# Get backend IP JSON
		objHResp = requests.get(objAREA.funURI(strBEIPURI), headers = diHeaders)
		# Extract private IP address
		strARMIP = json.loads(objHResp.content)['properties']['privateIPAddress']
		funLog(2, 'Current private IP in Azure RM: %s' % strARMIP)
		if strARMIP == strLocIP:
			# This machine is already Active
			funLog(1, 'Current state: Active')
			return 'Active'

		elif strARMIP == strPeerIP:
			# The dead peer is listed as Active - failover required
			return 'Standby'

	except Exception as e:
		funLog(2, str(e), 'err')
	funLog(1, 'Current state: Unknown', 'warning')
	return 'Unknown'

def funOpStatus(objHResp):
	strStatus = 'InProgress'
	strOpURL = objHResp.headers['Azure-AsyncOperation']
	funLog(2, 'ARM Async Operation, x-ms-request-id: %s' % objHResp.headers['x-ms-request-id'])
	diHeaders = objAREA.funBear()
	while strStatus == 'InProgress':
		try:
			strStatus = json.loads(requests.get(strOpURL, headers = diHeaders).content)['status']
			funLog(3, 'ARM Async Operation Status: %s' % strStatus)
		except Exception as e:
			funLog(2, str(e), 'err')
			break
	funLog(2, strStatus)
	return strStatus

def funFailover():
	diHeaders = objAREA.funBear()
	try:
		strOldNICURL = objAREA.funURI(objAREA.strCurNICURI)
	except AttributeError as e:
		funLog(2, 'No NICs in the Backend Pool!', 'warning')
		return 3

	strChar = 'B/'
	if objAREA.strCurNICURI.endswith('B/'):
		strChar = 'A/'
	strNewNICURL = objAREA.funURI(objAREA.strCurNICURI[:-2] + strChar)
	try:
		# Get the JSON of the NIC currently in the backend pool
		objHResp = requests.get(strOldNICURL, headers = diHeaders)
		diOldNIC = json.loads(objHResp.content)
		# Remove the LB backend pool from that JSON
		diOldNIC['properties']['ipConfigurations'][0]['properties']['loadBalancerBackendAddressPools'] = []
		# Get the JSON of the new NIC to be added to the backend pool
		objHResp = requests.get(strNewNICURL, headers = diHeaders)
		diNewNIC = json.loads(objHResp.content)
		# Remove the existing backend IP ID from the LB backend pool JSON (stored in funCurState)
		objAREA.diBEPool[0]['properties']['backendIPConfigurations'] = []
		# Add the LB backend pool to the new NIC JSON
		diNewNIC['properties']['ipConfigurations'][0]['properties']['loadBalancerBackendAddressPools'] = objAREA.diBEPool
		# Add Content-Type to HTTP headers
		diHeaders['Content-Type'] = 'application/json'
		# Update the new NIC (add it to the backend pool)
		objHResp = requests.put(strNewNICURL, headers = diHeaders, data = json.dumps(diNewNIC))
		funLog(1, 'Adding the new NIC to LBAZ BE Pool...')
		if funOpStatus(objHResp) != 'Succeeded':
			return 2

		# Update the old NIC (remove it from the backend pool)
		objHResp = requests.put(strOldNICURL, headers = diHeaders, data = json.dumps(diOldNIC))
		funLog(1, 'Removing the old NIC from LBAZ BE Pool... ')
		if funOpStatus(objHResp) == 'Succeeded':
			return 0

	except Exception as e:
		funLog(2, str(e), 'err')
	return 1

def funArgParse():
	objArgParse = ArgumentParser()
	objArgParse.add_argument('-l', help='log level (0-3)', action='store', type=int, dest='level')
	objArgParse.add_argument('-a', help='test Azure RM authentication and exit', action='store_true', dest='auth')
	objArgParse.add_argument('-v', action='version', version='%(prog)s ' + __version__)
	return objArgParse.parse_args()

def main():
	print len(sys.argv)
	intArgNum = 3
	objArgs = funArgParse()
	if objArgs.level is not None:
		global intLogLevel
		intLogLevel = objArgs.level
		intArgNum += 2
	print intLogLevel
	return
	funLog(1, '=' * 62)
	if len(sys.argv) < intArgNum:
		funLog(1, 'Not enough arguments!', 'err')
		sys.exit(objExCodes.args)

	# Remove IPv6/IPv4 compatibility prefix (LTM passes addresses in IPv6 format)
	strRIP = sys.argv[1].strip(':f')
	strRPort = sys.argv[2]
	# PID file
	strPFile = '_'.join(['/var/run/', os.path.basename(sys.argv[0]), strRIP, strRPort + '.pid'])
	# PID
	strPID = str(os.getpid())

	funLog(2, 'PIDFile: %s, PID: %s' % (strPFile, strPID))

	# Kill the last instance of this monitor if hung
	if os.path.isfile(strPFile):
		try:
			os.kill(int(file(strPFile, 'r').read()), SIGKILL)
			funLog(1, 'Killed the last hung instance of this monitor.', 'warning')
		except OSError:
			pass

	# Record current PID
	file(strPFile, 'w').write(str(os.getpid()))

	# Health monitor
	try:
		objHResp = requests.head(''.join(['https://', strRIP, ':', strRPort]), verify = False)
		if objHResp.status_code == 200:
			os.unlink(strPFile)
			# Any standard output stops the script from running. Clean up any temporary files before the standard output operation
			funLog(2, 'Peer: %s is up.' % strRIP)
			print 'UP'
			sys.exit()

	except requests.exceptions.RequestException as e:
		funLog(2, str(e), 'err')

	# Peer down, ARM action required
	funLog(1, 'Peer down, ARM action required.', 'warning')
	if funARMAuth() != 0:
		funLog(1, 'ARM Auth Error!', 'err')
		os.unlink(strPFile)
		sys.exit(objExCodes.armAuth)

	# ARM Auth OK
	funLog(3, 'ARM Bearer: %s' % objAREA.strBearer)

	if funCurState(funLocIP(strRIP), strRIP) == 'Standby':
		funLog(1, 'We\'re Standby in ARM, Active peer down. Trying to failover...', 'warning')
		funFailover()

	os.unlink(strPFile)
	sys.exit(1)

if __name__ == '__main__':
	main()
<commit_msg>Update emon_AZURE_HA.py<commit_after>#!/usr/bin/env python
# F5 Networks - External Monitor: Azure HA
# https://github.com/ArtiomL/f5networks
# Artiom Lichtenstein
# v0.9.4, 19/08/2016

from argparse import ArgumentParser
from datetime import timedelta
import json
import os
import requests
from signal import SIGKILL
import socket
from subprocess import call
import sys
from time import time

__author__ = 'Artiom Lichtenstein'
__license__ = 'MIT'
__version__ = '0.9.4'

# Log level to /var/log/ltm
intLogLevel = 0
strLogID = '[-v%s-160818-] emon_AZURE_HA.py - ' % __version__

# Azure RM REST API
class clsAREA:
	def __init__(self):
		self.strCFile = '/shared/tmp/scripts/azure/azure_ha.json'
		self.strMgmtURI = 'https://management.azure.com/'
		self.strAPIVer = '?api-version=2016-03-30'

	def funAbsURL(self):
		return self.strMgmtURI, self.strSubID, self.strRGName, self.strAPIVer

	def funURI(self, strMidURI):
		return self.strMgmtURI + strMidURI + self.strAPIVer

	def funBear(self):
		return { 'Authorization': 'Bearer %s' % self.strBearer }

objAREA = clsAREA()

# Logger command
strLogger = 'logger -p local0.'

# Exit codes
class clsExCodes:
	def __init__(self):
		self.args = 8
		self.armAuth = 4

objExCodes = clsExCodes()

def funLog(intMesLevel, strMessage, strSeverity='info ', strMethod='log'):
	if intLogLevel >= intMesLevel:
		if strMethod == 'stdout':
			print strMessage
		else:
			lstCmd = (strLogger + strSeverity).split(' ')
			lstCmd.append(strLogID + strMessage)
			call(lstCmd)

def funARMAuth():
	# Azure RM OAuth2
	global objAREA
	# Read external config file
	if not os.path.isfile(objAREA.strCFile):
		funLog(1, 'Credentials file: %s is missing!' % objAREA.strCFile, 'err')
		return 3

	try:
		# Open the credentials file
		with open(objAREA.strCFile, 'r') as f:
			diCreds = json.load(f)
		# Read subscription and resource group
		objAREA.strSubID = diCreds['subID']
		objAREA.strRGName = diCreds['rgName']
		# Current epoch time
		intEpNow = int(time())
		# Check if Bearer token exists (in credentials file) and whether it can be reused (expiration with 1 minute time skew)
		if (set(('bearer', 'expiresOn')) <= set(diCreds) and int(diCreds['expiresOn']) - 60 > intEpNow):
			objAREA.strBearer = diCreds['bearer'].decode('base64')
			funLog(2, 'Reusing existing Bearer, it expires in %s' % str(timedelta(seconds=int(diCreds['expiresOn']) - intEpNow)))
			return 0

		# Read additional config parameters
		strTenantID = diCreds['tenantID']
		strAppID = diCreds['appID']
		strPass = diCreds['pass'].decode('base64')
		strEndPt = 'https://login.microsoftonline.com/%s/oauth2/token' % strTenantID
	except Exception as e:
		funLog(1, 'Invalid credentials file: %s' % objAREA.strCFile, 'err')
		return 2

	# Generate new Bearer token
	diPayload = { 'grant_type': 'client_credentials', 'client_id': strAppID, 'client_secret': strPass, 'resource': objAREA.strMgmtURI }
	try:
		objHResp = requests.post(url=strEndPt, data=diPayload)
		diAuth = json.loads(objHResp.content)
		if 'access_token' in diAuth.keys():
			# Successfully received new token
			objAREA.strBearer = diAuth['access_token']
			# Write the new token and its expiration epoch into the credentials file
			diCreds['bearer'] = objAREA.strBearer.encode('base64')
			diCreds['expiresOn'] = diAuth['expires_on']
			with open(objAREA.strCFile, 'w') as f:
				f.write(json.dumps(diCreds, sort_keys=True, indent=4, separators=(',', ': ')))
			return 0

	except requests.exceptions.RequestException as e:
		funLog(2, str(e), 'err')
	return 1

def funLocIP(strRemIP):
	# Get local private IP
	objUDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
	# The .connect method doesn't generate any real network traffic for UDP (socket.SOCK_DGRAM)
	objUDP.connect((strRemIP, 0))
	return objUDP.getsockname()[0]

def funCurState(strLocIP, strPeerIP):
	# Get current ARM state for the local machine
	global objAREA
	funLog(2, 'Current local private IP: %s, Resource Group: %s' % (strLocIP, objAREA.strRGName))
	# Construct loadBalancers URL
	strLBURL = '%ssubscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers%s' % objAREA.funAbsURL()
	diHeaders = objAREA.funBear()
	try:
		# Get LBAZ JSON
		objHResp = requests.get(strLBURL, headers = diHeaders)
		# Store the backend pool JSON (for funFailover)
		objAREA.diBEPool = json.loads(objHResp.content)['value'][0]['properties']['backendAddressPools']
		# Extract backend IP ID ([1:] at the end removes the first "/" char)
		strBEIPURI = objAREA.diBEPool[0]['properties']['backendIPConfigurations'][0]['id'][1:]
		# Store the URI for NIC currently in the backend pool (for funFailover)
		objAREA.strCurNICURI = strBEIPURI.split('ipConfiguration')[0]
		# Get backend IP JSON
		objHResp = requests.get(objAREA.funURI(strBEIPURI), headers = diHeaders)
		# Extract private IP address
		strARMIP = json.loads(objHResp.content)['properties']['privateIPAddress']
		funLog(2, 'Current private IP in Azure RM: %s' % strARMIP)
		if strARMIP == strLocIP:
			# This machine is already Active
			funLog(1, 'Current state: Active')
			return 'Active'

		elif strARMIP == strPeerIP:
			# The dead peer is listed as Active - failover required
			return 'Standby'

	except Exception as e:
		funLog(2, str(e), 'err')
	funLog(1, 'Current state: Unknown', 'warning')
	return 'Unknown'

def funOpStatus(objHResp):
	strStatus = 'InProgress'
	strOpURL = objHResp.headers['Azure-AsyncOperation']
	funLog(2, 'ARM Async Operation, x-ms-request-id: %s' % objHResp.headers['x-ms-request-id'])
	diHeaders = objAREA.funBear()
	while strStatus == 'InProgress':
		try:
			strStatus = json.loads(requests.get(strOpURL, headers = diHeaders).content)['status']
			funLog(3, 'ARM Async Operation Status: %s' % strStatus)
		except Exception as e:
			funLog(2, str(e), 'err')
			break
	funLog(2, strStatus)
	return strStatus

def funFailover():
	diHeaders = objAREA.funBear()
	try:
		strOldNICURL = objAREA.funURI(objAREA.strCurNICURI)
	except AttributeError as e:
		funLog(2, 'No NICs in the Backend Pool!', 'warning')
		return 3

	strChar = 'B/'
	if objAREA.strCurNICURI.endswith('B/'):
		strChar = 'A/'
	strNewNICURL = objAREA.funURI(objAREA.strCurNICURI[:-2] + strChar)
	try:
		# Get the JSON of the NIC currently in the backend pool
		objHResp = requests.get(strOldNICURL, headers = diHeaders)
		diOldNIC = json.loads(objHResp.content)
		# Remove the LB backend pool from that JSON
		diOldNIC['properties']['ipConfigurations'][0]['properties']['loadBalancerBackendAddressPools'] = []
		# Get the JSON of the new NIC to be added to the backend pool
		objHResp = requests.get(strNewNICURL, headers = diHeaders)
		diNewNIC = json.loads(objHResp.content)
		# Remove the existing backend IP ID from the LB backend pool JSON (stored in funCurState)
		objAREA.diBEPool[0]['properties']['backendIPConfigurations'] = []
		# Add the LB backend pool to the new NIC JSON
		diNewNIC['properties']['ipConfigurations'][0]['properties']['loadBalancerBackendAddressPools'] = objAREA.diBEPool
		# Add Content-Type to HTTP headers
		diHeaders['Content-Type'] = 'application/json'
		# Update the new NIC (add it to the backend pool)
		objHResp = requests.put(strNewNICURL, headers = diHeaders, data = json.dumps(diNewNIC))
		funLog(1, 'Adding the new NIC to LBAZ BE Pool...')
		if funOpStatus(objHResp) != 'Succeeded':
			return 2

		# Update the old NIC (remove it from the backend pool)
		objHResp = requests.put(strOldNICURL, headers = diHeaders, data = json.dumps(diOldNIC))
		funLog(1, 'Removing the old NIC from LBAZ BE Pool... ')
		if funOpStatus(objHResp) == 'Succeeded':
			return 0

	except Exception as e:
		funLog(2, str(e), 'err')
	return 1

def funArgParse():
	objArgParse = ArgumentParser()
	objArgParse.add_argument('-l', help='log level (0-3)', action='store', type=int, dest='level')
	objArgParse.add_argument('-a', help='test Azure RM authentication and exit', action='store_true', dest='auth')
	objArgParse.add_argument('-v', action='version', version='%(prog)s ' + __version__)
	objArgs, unknown = objArgParse.parse_known_args()
	return objArgs

def main():
	intArgNum = 3
	objArgs = funArgParse()
	if objArgs.level is not None:
		global intLogLevel
		intLogLevel = objArgs.level
		intArgNum += 2
	funLog(1, '=' * 62)
	if len(sys.argv) < intArgNum:
		funLog(1, 'Not enough arguments!', 'err')
		sys.exit(objExCodes.args)

	# Remove IPv6/IPv4 compatibility prefix (LTM passes addresses in IPv6 format)
	strRIP = sys.argv[1].strip(':f')
	strRPort = sys.argv[2]
	# PID file
	strPFile = '_'.join(['/var/run/', os.path.basename(sys.argv[0]), strRIP, strRPort + '.pid'])
	# PID
	strPID = str(os.getpid())

	funLog(2, 'PIDFile: %s, PID: %s' % (strPFile, strPID))

	# Kill the last instance of this monitor if hung
	if os.path.isfile(strPFile):
		try:
			os.kill(int(file(strPFile, 'r').read()), SIGKILL)
			funLog(1, 'Killed the last hung instance of this monitor.', 'warning')
		except OSError:
			pass

	# Record current PID
	file(strPFile, 'w').write(str(os.getpid()))

	# Health monitor
	try:
		objHResp = requests.head(''.join(['https://', strRIP, ':', strRPort]), verify = False)
		if objHResp.status_code == 200:
			os.unlink(strPFile)
			# Any standard output stops the script from running. Clean up any temporary files before the standard output operation
			funLog(2, 'Peer: %s is up.' % strRIP)
			print 'UP'
			sys.exit()

	except requests.exceptions.RequestException as e:
		funLog(2, str(e), 'err')

	# Peer down, ARM action required
	funLog(1, 'Peer down, ARM action required.', 'warning')
	if funARMAuth() != 0:
		funLog(1, 'ARM Auth Error!', 'err')
		os.unlink(strPFile)
		sys.exit(objExCodes.armAuth)

	# ARM Auth OK
	funLog(3, 'ARM Bearer: %s' % objAREA.strBearer)

	if funCurState(funLocIP(strRIP), strRIP) == 'Standby':
		funLog(1, 'We\'re Standby in ARM, Active peer down. Trying to failover...', 'warning')
		funFailover()

	os.unlink(strPFile)
	sys.exit(1)

if __name__ == '__main__':
	main()
<|endoftext|>


<commit_before>from corehq.apps.app_manager.const import APP_V2
from corehq.apps.app_manager.exceptions import CaseError
from corehq.apps.app_manager.models import (
    Application,
    Module,
    UpdateCaseAction,
    OpenSubCaseAction,
    AdvancedOpenCaseAction,
    FormActionCondition,
    CaseIndex,
    AdvancedModule,
    LoadUpdateAction,
    PreloadAction,
)
from corehq.apps.app_manager.tests import TestFileMixin
from corehq.apps.app_manager.xform import CaseBlock, XForm, _make_elem
from corehq.apps.app_manager.xpath import session_var
from couchdbkit import BadValueError
from django.test import SimpleTestCase
from mock import patch

class ExtCasePropertiesTests(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()

        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.app.version = 3
        self.fish_module = self.app.add_module(Module.new_module('Fish Module', lang='en'))
        self.fish_module.case_type = 'fish'
        self.fish_form = self.app.new_form(0, 'New Form', lang='en')
        self.fish_form.source = self.get_xml('original')

        self.freshwater_module = self.app.add_module(Module.new_module('Freshwater Module', lang='en'))
        self.freshwater_module.case_type = 'freshwater'
        self.freshwater_form = self.app.new_form(0, 'New Form', lang='en')
        self.freshwater_form.source = self.get_xml('original')

        self.aquarium_module = self.app.add_module(Module.new_module('Aquarium Module', lang='en'))
        self.aquarium_module.case_type = 'aquarium'

    def tearDown(self):
        self.is_usercase_in_use_patch.stop()

    def test_ext_case_preload_host_case(self):
        """
        Properties of a host case should be available in a extension case
        """
        self.freshwater_form.requires = 'case'
        self.freshwater_form.actions.case_preload = PreloadAction(preload={
            '/data/question1': 'question1',
            '/data/question2': 'host/question2',
        })
        self.freshwater_form.actions.update_case.condition.type = 'always'
        self.assertXmlEqual(self.get_xml('preload_host_case'), self.freshwater_form.render_xform())

    def test_ext_case_update_host_case(self):
        """
        A extension case should be able to save host case properties
        """
        self.freshwater_form.requires = 'case'
        self.freshwater_form.actions.update_case = UpdateCaseAction(update={
            'question1': '/data/question1',
            'host/question1': '/data/question1',
        })
        self.freshwater_form.actions.update_case.condition.type = 'always'
        self.assertXmlEqual(self.get_xml('update_host_case'), self.freshwater_form.render_xform())

    def test_host_case_preload_ext_cases(self):
        """
        Properties of one or more extension cases should be available in a host case
        """
        self.skipTest('TODO: Write this test')

    def test_host_case_update_ext_cases(self):
        """
        A host case should be able to save properties of one or more extension cases
        """
        self.skipTest('TODO: Write this test')

class ExtCasePropertiesAdvancedTests(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.app.version = 3
        self.module = self.app.add_module(AdvancedModule.new_module('New Module', lang='en'))
        self.module.case_type = 'test_case_type'
        self.form = self.module.new_form("Untitled Form", "en", self.get_xml('original'))

        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()

    def tearDown(self):
        self.is_usercase_in_use_patch.stop()

    def test_ext_case_preload_host_case(self):
        """
        A extension case should be able to preload host case properties in an advanced module
        """
        self.form.actions.load_update_cases.append(LoadUpdateAction(
            case_type=self.module.case_type,
            case_tag='load_1',
            preload={
                '/data/question1': 'question1',
                '/data/question2': 'host/question2'
            },
        ))
        self.assertXmlEqual(self.get_xml('preload_host_case_adv'), self.form.render_xform())

    def test_ext_case_update_host_case(self):
        """
        A extension case should be able to save host case properties in an advanced module
        """
        self.form.actions.load_update_cases.append(LoadUpdateAction(
            case_type=self.module.case_type,
            case_tag='load_1',
            case_properties={
                'question1': '/data/question1',
                'host/question1': '/data/question1'
            },
        ))
        self.assertXmlEqual(self.get_xml('update_host_case_adv'), self.form.render_xform())

    def test_host_case_preload_ext_case(self):
        self.skipTest('TODO: Write this test')

    def test_host_case_update_ext_case(self):
        self.skipTest('TODO: Write this test')

class CaseBlockIndexRelationshipTest(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()
        self.is_usercase_in_use_mock.return_value = True

        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.module = self.app.add_module(Module.new_module('Fish Module', None))
        self.module.case_type = 'fish'
        self.form = self.module.new_form('New Form', None)
        self.subcase = OpenSubCaseAction(
            case_type='freshwater',
            case_name='Wanda',
            condition=FormActionCondition(type='always'),
            case_properties={'name': '/data/question1'},
            relationship='extension',
        )
        self.form.actions.subcases.append(self.subcase)
        self.xform = XForm(self.get_xml('original'))
        self.add_subcase_block()

    def add_subcase_block(self):
        ### messy messy messy
        parent_node = self.xform.data_node
        case_id = session_var(self.form.session_var_for_action('subcases', 0))
        #name = 'subcase_0'
        subcase_node = _make_elem('{x}subcase_0')
        parent_node.append(subcase_node)
        path = 'subcase_0/'
        self.subcase_block = CaseBlock(self.xform, path)
        subcase_node.insert(0, self.subcase_block.elem)
        self.subcase_block.add_create_block(
            relevance=self.xform.action_relevance(self.subcase.condition),
            case_name=self.subcase.case_name,
            case_type=self.subcase.case_type,
            delay_case_id=bool(self.subcase.repeat_context),
            autoset_owner_id=True,
            has_case_sharing=self.form.get_app().case_sharing,
            case_id=case_id
        )
        self.subcase_block.add_update_block(self.subcase.case_properties)

    def test_xform_case_block_index_supports_relationship(self):
        """
        CaseBlock index should allow the relationship to be set
        """
        self.subcase_block.add_index_ref(
            'host',
            self.form.get_case_type(),
            self.xform.resolve_path("case/@case_id"),
            self.subcase.relationship,
        )
        self.assertXmlEqual(self.get_xml('open_subcase'), str(self.xform))

    def test_xform_case_block_index_default_relationship(self):
        """
        CaseBlock index relationship should default to "child"
        """
        self.subcase_block.add_index_ref(
            'host',
            self.form.get_case_type(),
            self.xform.resolve_path("case/@case_id")
        )
        self.assertXmlEqual(self.get_xml('open_subcase_child'), str(self.xform))

    def test_xform_case_block_index_valid_relationship(self):
        """
        CaseBlock index relationship should only allow valid values
        """
        with self.assertRaisesRegexp(CaseError,
                                     'Valid values for an index relationship are "child" and "extension"'):
            self.subcase_block.add_index_ref(
                'host',
                self.form.get_case_type(),
                self.xform.resolve_path("case/@case_id"),
                'cousin',
            )

class OpenSubCaseActionTests(SimpleTestCase):

    def test_open_subcase_action_supports_relationship(self):
        """
        OpenSubCaseAction should allow relationship to be set
        """
        action = OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='extension')
        self.assertEqual(action.relationship, 'extension')

    def test_open_subcase_action_default_relationship(self):
        """
        OpenSubCaseAction relationship should default to "child"
        """
        action = OpenSubCaseAction(case_type='mother', case_name='Eva')
        self.assertEqual(action.relationship, 'child')

    def test_open_subcase_action_valid_relationship(self):
        """
        OpenSubCaseAction relationship should only allow valid values
        """
        OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='child')
        OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='extension')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='parent')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='host')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='master')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='slave')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='cousin')

class CaseIndexTests(SimpleTestCase):

    def test_case_index_support_relationship(self):
        """
        CaseIndex should allow relationship to be set
        """
        case_index = CaseIndex(tag='mother', relationship='extension')
        self.assertEqual(case_index.relationship, 'extension')

    def test_case_index_default_relationship(self):
        """
        CaseIndex relationship should default to "child"
        """
        case_index = CaseIndex(tag='mother')
        self.assertEqual(case_index.relationship, 'child')

    def test_case_index_valid_relationship(self):
        """
        CaseIndex relationship should only allow valid values
        """
        CaseIndex(tag='mother', relationship='child')
        CaseIndex(tag='mother', relationship='extension')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='parent')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='host')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='master')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='slave')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='cousin')

class AdvancedOpenCaseActionMigrationTests(SimpleTestCase):

    def test_parent_tag(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex if a parent tag is given
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_indices[0].tag, 'eggs')

    def test_defaults(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex with property defaults
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_indices[0].reference_id, 'parent')
        self.assertEqual(action.case_indices[0].relationship, 'child')

    def test_properties(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex with given properties
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(action.case_indices[0].tag, 'eggs')
        self.assertEqual(action.case_indices[0].reference_id, 'spam')
        self.assertEqual(action.case_indices[0].relationship, 'extension')

    def test_advanced_action_no_parent_tag(self):
        """
        AdvancedOpenCaseAction migration should not create a CaseIndex without parent_tag
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(len(action.case_indices), 0)

class LoadUpdateActionMigrationTests(SimpleTestCase):

    def test_parent_tag(self):
        """
        LoadUpdateAction migration should create a CaseIndex if a parent tag is given
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_index.tag, 'eggs')

    def test_defaults(self):
        """
        LoadUpdateAction migration should create a CaseIndex with property defaults
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_index.reference_id, 'parent')
        self.assertEqual(action.case_index.relationship, 'child')

    def test_properties(self):
        """
        LoadUpdateAction migration should create a CaseIndex with given properties
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(action.case_index.tag, 'eggs')
        self.assertEqual(action.case_index.reference_id, 'spam')
        self.assertEqual(action.case_index.relationship, 'extension')

    def test_advanced_action_no_parent_tag(self):
        """
        LoadUpdateAction migration should not create a CaseIndex without parent_tag
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertFalse(bool(action.case_index.tag))
<commit_msg>form.session_var_for_action only takes one param<commit_after>from corehq.apps.app_manager.const import APP_V2
from corehq.apps.app_manager.exceptions import CaseError
from corehq.apps.app_manager.models import (
    Application,
    Module,
    UpdateCaseAction,
    OpenSubCaseAction,
    AdvancedOpenCaseAction,
    FormActionCondition,
    CaseIndex,
    AdvancedModule,
    LoadUpdateAction,
    PreloadAction,
)
from corehq.apps.app_manager.tests import TestFileMixin
from corehq.apps.app_manager.xform import CaseBlock, XForm, _make_elem
from corehq.apps.app_manager.xpath import session_var
from couchdbkit import BadValueError
from django.test import SimpleTestCase
from mock import patch

class ExtCasePropertiesTests(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()

        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.app.version = 3
        self.fish_module = self.app.add_module(Module.new_module('Fish Module', lang='en'))
        self.fish_module.case_type = 'fish'
        self.fish_form = self.app.new_form(0, 'New Form', lang='en')
        self.fish_form.source = self.get_xml('original')

        self.freshwater_module = self.app.add_module(Module.new_module('Freshwater Module', lang='en'))
        self.freshwater_module.case_type = 'freshwater'
        self.freshwater_form = self.app.new_form(0, 'New Form', lang='en')
        self.freshwater_form.source = self.get_xml('original')

        self.aquarium_module = self.app.add_module(Module.new_module('Aquarium Module', lang='en'))
        self.aquarium_module.case_type = 'aquarium'

    def tearDown(self):
        self.is_usercase_in_use_patch.stop()

    def test_ext_case_preload_host_case(self):
        """
        Properties of a host case should be available in a extension case
        """
        self.freshwater_form.requires = 'case'
        self.freshwater_form.actions.case_preload = PreloadAction(preload={
            '/data/question1': 'question1',
            '/data/question2': 'host/question2',
        })
        self.freshwater_form.actions.update_case.condition.type = 'always'
        self.assertXmlEqual(self.get_xml('preload_host_case'), self.freshwater_form.render_xform())

    def test_ext_case_update_host_case(self):
        """
        A extension case should be able to save host case properties
        """
        self.freshwater_form.requires = 'case'
        self.freshwater_form.actions.update_case = UpdateCaseAction(update={
            'question1': '/data/question1',
            'host/question1': '/data/question1',
        })
        self.freshwater_form.actions.update_case.condition.type = 'always'
        self.assertXmlEqual(self.get_xml('update_host_case'), self.freshwater_form.render_xform())

    def test_host_case_preload_ext_cases(self):
        """
        Properties of one or more extension cases should be available in a host case
        """
        self.skipTest('TODO: Write this test')

    def test_host_case_update_ext_cases(self):
        """
        A host case should be able to save properties of one or more extension cases
        """
        self.skipTest('TODO: Write this test')

class ExtCasePropertiesAdvancedTests(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.app.version = 3
        self.module = self.app.add_module(AdvancedModule.new_module('New Module', lang='en'))
        self.module.case_type = 'test_case_type'
        self.form = self.module.new_form("Untitled Form", "en", self.get_xml('original'))

        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()

    def tearDown(self):
        self.is_usercase_in_use_patch.stop()

    def test_ext_case_preload_host_case(self):
        """
        A extension case should be able to preload host case properties in an advanced module
        """
        self.form.actions.load_update_cases.append(LoadUpdateAction(
            case_type=self.module.case_type,
            case_tag='load_1',
            preload={
                '/data/question1': 'question1',
                '/data/question2': 'host/question2'
            },
        ))
        self.assertXmlEqual(self.get_xml('preload_host_case_adv'), self.form.render_xform())

    def test_ext_case_update_host_case(self):
        """
        A extension case should be able to save host case properties in an advanced module
        """
        self.form.actions.load_update_cases.append(LoadUpdateAction(
            case_type=self.module.case_type,
            case_tag='load_1',
            case_properties={
                'question1': '/data/question1',
                'host/question1': '/data/question1'
            },
        ))
        self.assertXmlEqual(self.get_xml('update_host_case_adv'), self.form.render_xform())

    def test_host_case_preload_ext_case(self):
        self.skipTest('TODO: Write this test')

    def test_host_case_update_ext_case(self):
        self.skipTest('TODO: Write this test')

class CaseBlockIndexRelationshipTest(SimpleTestCase, TestFileMixin):
    file_path = 'data', 'extension_case'

    def setUp(self):
        self.is_usercase_in_use_patch = patch('corehq.apps.app_manager.models.is_usercase_in_use')
        self.is_usercase_in_use_mock = self.is_usercase_in_use_patch.start()
        self.is_usercase_in_use_mock.return_value = True

        self.app = Application.new_app('domain', 'New App', APP_V2)
        self.module = self.app.add_module(Module.new_module('Fish Module', None))
        self.module.case_type = 'fish'
        self.form = self.module.new_form('New Form', None)
        self.subcase = OpenSubCaseAction(
            case_type='freshwater',
            case_name='Wanda',
            condition=FormActionCondition(type='always'),
            case_properties={'name': '/data/question1'},
            relationship='extension',
        )
        self.form.actions.subcases.append(self.subcase)
        self.xform = XForm(self.get_xml('original'))
        self.add_subcase_block()

    def add_subcase_block(self):
        ### messy messy messy
        parent_node = self.xform.data_node
        case_id = session_var(self.form.session_var_for_action('subcases'))
        #name = 'subcase_0'
        subcase_node = _make_elem('{x}subcase_0')
        parent_node.append(subcase_node)
        path = 'subcase_0/'
        self.subcase_block = CaseBlock(self.xform, path)
        subcase_node.insert(0, self.subcase_block.elem)
        self.subcase_block.add_create_block(
            relevance=self.xform.action_relevance(self.subcase.condition),
            case_name=self.subcase.case_name,
            case_type=self.subcase.case_type,
            delay_case_id=bool(self.subcase.repeat_context),
            autoset_owner_id=True,
            has_case_sharing=self.form.get_app().case_sharing,
            case_id=case_id
        )
        self.subcase_block.add_update_block(self.subcase.case_properties)

    def test_xform_case_block_index_supports_relationship(self):
        """
        CaseBlock index should allow the relationship to be set
        """
        self.subcase_block.add_index_ref(
            'host',
            self.form.get_case_type(),
            self.xform.resolve_path("case/@case_id"),
            self.subcase.relationship,
        )
        self.assertXmlEqual(self.get_xml('open_subcase'), str(self.xform))

    def test_xform_case_block_index_default_relationship(self):
        """
        CaseBlock index relationship should default to "child"
        """
        self.subcase_block.add_index_ref(
            'host',
            self.form.get_case_type(),
            self.xform.resolve_path("case/@case_id")
        )
        self.assertXmlEqual(self.get_xml('open_subcase_child'), str(self.xform))

    def test_xform_case_block_index_valid_relationship(self):
        """
        CaseBlock index relationship should only allow valid values
        """
        with self.assertRaisesRegexp(CaseError,
                                     'Valid values for an index relationship are "child" and "extension"'):
            self.subcase_block.add_index_ref(
                'host',
                self.form.get_case_type(),
                self.xform.resolve_path("case/@case_id"),
                'cousin',
            )

class OpenSubCaseActionTests(SimpleTestCase):

    def test_open_subcase_action_supports_relationship(self):
        """
        OpenSubCaseAction should allow relationship to be set
        """
        action = OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='extension')
        self.assertEqual(action.relationship, 'extension')

    def test_open_subcase_action_default_relationship(self):
        """
        OpenSubCaseAction relationship should default to "child"
        """
        action = OpenSubCaseAction(case_type='mother', case_name='Eva')
        self.assertEqual(action.relationship, 'child')

    def test_open_subcase_action_valid_relationship(self):
        """
        OpenSubCaseAction relationship should only allow valid values
        """
        OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='child')
        OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='extension')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='parent')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='host')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='master')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='slave')
        with self.assertRaises(BadValueError):
            OpenSubCaseAction(case_type='mother', case_name='Eva', relationship='cousin')

class CaseIndexTests(SimpleTestCase):

    def test_case_index_support_relationship(self):
        """
        CaseIndex should allow relationship to be set
        """
        case_index = CaseIndex(tag='mother', relationship='extension')
        self.assertEqual(case_index.relationship, 'extension')

    def test_case_index_default_relationship(self):
        """
        CaseIndex relationship should default to "child"
        """
        case_index = CaseIndex(tag='mother')
        self.assertEqual(case_index.relationship, 'child')

    def test_case_index_valid_relationship(self):
        """
        CaseIndex relationship should only allow valid values
        """
        CaseIndex(tag='mother', relationship='child')
        CaseIndex(tag='mother', relationship='extension')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='parent')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='host')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='master')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='slave')
        with self.assertRaises(BadValueError):
            CaseIndex(tag='mother', relationship='cousin')

class AdvancedOpenCaseActionMigrationTests(SimpleTestCase):

    def test_parent_tag(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex if a parent tag is given
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_indices[0].tag, 'eggs')

    def test_defaults(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex with property defaults
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_indices[0].reference_id, 'parent')
        self.assertEqual(action.case_indices[0].relationship, 'child')

    def test_properties(self):
        """
        AdvancedOpenCaseAction migration should create a CaseIndex with given properties
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(action.case_indices[0].tag, 'eggs')
        self.assertEqual(action.case_indices[0].reference_id, 'spam')
        self.assertEqual(action.case_indices[0].relationship, 'extension')

    def test_advanced_action_no_parent_tag(self):
        """
        AdvancedOpenCaseAction migration should not create a CaseIndex without parent_tag
        """
        action = AdvancedOpenCaseAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(len(action.case_indices), 0)

class LoadUpdateActionMigrationTests(SimpleTestCase):

    def test_parent_tag(self):
        """
        LoadUpdateAction migration should create a CaseIndex if a parent tag is given
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_index.tag, 'eggs')

    def test_defaults(self):
        """
        LoadUpdateAction migration should create a CaseIndex with property defaults
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
        })
        self.assertEqual(action.case_index.reference_id, 'parent')
        self.assertEqual(action.case_index.relationship, 'child')

    def test_properties(self):
        """
        LoadUpdateAction migration should create a CaseIndex with given properties
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_tag': 'eggs',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertEqual(action.case_index.tag, 'eggs')
        self.assertEqual(action.case_index.reference_id, 'spam')
        self.assertEqual(action.case_index.relationship, 'extension')

    def test_advanced_action_no_parent_tag(self):
        """
        LoadUpdateAction migration should not create a CaseIndex without parent_tag
        """
        action = LoadUpdateAction.wrap({
            'case_type': 'spam',
            'case_tag': 'ham',
            'parent_reference_id': 'spam',
            'relationship': 'extension',
        })
        self.assertFalse(bool(action.case_index.tag))
<|endoftext|>


<commit_before># Copyright (c) 2012 Cloudera, Inc. All rights reserved.

try:
  import json
except ImportError:
  import simplejson as json
import logging

from cm_api.endpoints.types import config_to_json, json_to_config, \
    config_to_api_list, ApiCommand, ApiHostRef, ApiList, BaseApiObject
from cm_api.endpoints import roles, roletypes

__docformat__ = "epytext"

SERVICES_PATH = "/clusters/%s/services"
SERVICE_PATH = "/clusters/%s/services/%s"
ROLETYPES_PATH = "/clusters/%s/services/%s/roletypes"

LOG = logging.getLogger(__name__)

def create_service(resource_root, name, service_type, version,
                   cluster_name="default"):
  """
  Create a service
  @param resource_root: The root Resource object.
  @param name: Service name
  @param service_type: Service type
  @param version: Service version
  @param cluster_name: Cluster name
  @return: An ApiService object
  """
  apiservice = ApiService(resource_root, name, service_type, version)
  apiservice_list = ApiList([apiservice])
  body = json.dumps(apiservice_list.to_json_dict())
  resp = resource_root.post(SERVICES_PATH % (cluster_name,), data=body)
  # The server returns a list of created services (with size 1)
  return ApiList.from_json_dict(ApiService, resp, resource_root)[0]

def get_service(resource_root, name, cluster_name="default"):
  """
  Lookup a service by name
  @param resource_root: The root Resource object.
  @param name: Service name
  @param cluster_name: Cluster name
  @return: An ApiService object
  """
  dic = resource_root.get("%s/%s" % (SERVICES_PATH % (cluster_name,), name))
  return ApiService.from_json_dict(dic, resource_root)

def get_all_services(resource_root, cluster_name="default", view=None):
  """
  Get all services
  @param resource_root: The root Resource object.
  @param cluster_name: Cluster name
  @return: A list of ApiService objects.
  """
  dic = resource_root.get(SERVICES_PATH % (cluster_name,),
          params=view and dict(view=view) or None)
  return ApiList.from_json_dict(ApiService, dic, resource_root)

def delete_service(resource_root, name, cluster_name="default"):
  """
  Delete a service by name
  @param resource_root: The root Resource object.
  @param name: Service name
  @param cluster_name: Cluster name
  @return: The deleted ApiService object
  """
  resp = resource_root.delete("%s/%s" % (SERVICES_PATH % (cluster_name,), name))
  return ApiService.from_json_dict(resp, resource_root)

class ApiService(BaseApiObject):
  RO_ATTR = ('serviceState', 'healthSummary', 'healthChecks', 'clusterRef', 'configStale')
  RW_ATTR = ('name', 'type', 'version')

  def __init__(self, resource_root, name, type, version):
    # Unfortunately, the json key is called "type". So our input arg
    # needs to be called "type" as well, despite it being a python keyword.
    BaseApiObject.ctor_helper(**locals())

  def _get_cluster_name(self):
    if self.clusterRef:
      return self.clusterRef.clusterName
    return None

  def _path(self):
    """
    Return the API path for this service.

    This method assumes that lack of a cluster reference means that the
    object refers to the Cloudera Management Services instance.
    """
    if self._get_cluster_name():
      return SERVICE_PATH % (self._get_cluster_name(), self.name)
    else:
      return '/cm/service'

  def _cmd(self, cmd, data=None):
    path = self._path() + '/commands/' + cmd
    resp = self._get_resource_root().post(path, data=data)
    return ApiCommand.from_json_dict(resp, self._get_resource_root())

  def _role_cmd(self, cmd, roles):
    path = self._path() + '/roleCommands/' + cmd
    data = json.dumps({ ApiList.LIST_KEY : roles })
    resp = self._get_resource_root().post(path, data = data)
    return ApiList.from_json_dict(ApiCommand, resp, self._get_resource_root())

  def get_config(self, view = None):
    """
    Retrieve the service's configuration.

    The 'summary' view contains strings as the dictionary values. The full
    view contains ApiConfig instances as the values.

    @param view: View to materialize ('full' or 'summary')
    @return Dictionary with configuration data.
    """
    path = self._path() + '/config'
    resp = self._get_resource_root().get(path,
        params = view and dict(view=view) or None)
    return json_to_config(resp, view == 'full')

  def update_config(self, config):
    """
    Update the service's configuration.

    @param config Dictionary with configuration to update.
    @return Dictionary with updated configuration.
    """
    path = self._path() + '/config'
    resp = self._get_resource_root().put(path, data = config_to_json(config))
    return json_to_config(resp)

  def create_role(self, role_name, role_type, host_id):
    """
    Create a role.

    @param role_name: Role name
    @param role_type: Role type
    @return: An ApiRole object
    """
    return roles.create_role(self._get_resource_root(), self.name, role_type,
        role_name, host_id, self._get_cluster_name())

  def delete_role(self, name):
    """
    Delete a role by name.

    @param name Role name
    @return The deleted ApiRole object
    """
    return roles.delete_role(self._get_resource_root(), self.name, name,
        self._get_cluster_name())

  def get_role(self, name):
    """
    Lookup a role by name.

    @param name: Role name
    @return: An ApiRole object
    """
    return roles.get_role(self._get_resource_root(), self.name, name,
        self._get_cluster_name())

  def get_all_roles(self, view = None):
    """
    Get all roles in the service.

    @param view: View to materialize ('full' or 'summary')
    @return: A list of ApiRole objects.
    """
    return roles.get_all_roles(self._get_resource_root(), self.name,
        self._get_cluster_name(), view)

  def get_roles_by_type(self, role_type, view = None):
    """
    Get all roles of a certain type in a service.

    @param role_type: Role type
    @param view: View to materialize ('full' or 'summary')
    @return: A list of ApiRole objects.
    """
    return roles.get_roles_by_type(self._get_resource_root(), self.name,
        role_type, self._get_cluster_name(), view)

  def get_role_types(self):
    """
    Get a list of role types in a service.

    @return: A list of role types (strings)
    """
    if self.type == 'MGMT':
      LOG.error("Management service does not support /roleTypes")
      return None
    resp = self._get_resource_root().get(self._path() + '/roleTypes')
    return resp[ApiList.LIST_KEY]

  def get_role_type(self, roletype):
    """
    Get a role type resource by name.

    @param roletype: Role type
    @return: An ApiRoleType object
    """
    if self.type == 'MGMT':
      LOG.error("Management service does not support /roleTypes")
      return None
    return roletypes.ApiRoleType(self._get_resource_root(), self, roletype)

  def start(self):
    """
    Start a service.

    @return Reference to the submitted command.
    """
    return self._cmd('start')

  def stop(self):
    """
    Stop a service.

    @return Reference to the submitted command.
    """
    return self._cmd('stop')

  def restart(self):
    """
    Restart a service.

    @return Reference to the submitted command.
    """
    return self._cmd('restart')

  def create_beeswax_warehouse(self):
    """
    Create the Beeswax role's warehouse for a Hue service.
    @return: Reference to the submitted command.
    """
    return self._cmd('hueCreateHiveWarehouse')

  def create_hbase_root(self):
    """
    Create the root directory of an HBase service.

    @return Reference to the submitted command.
    """
    return self._cmd('hbaseCreateRoot')

  def refresh(self, *role_names):
    """
    Execute the "refresh" command on a set of roles.

    @param: role_names Names of the roles to decommission.
    @return: Reference to the submitted command.
    """
    return self._role_cmd('refresh', role_names)

  def decommission(self, *role_names):
    """
    Decommission roles in a service.

    @param role_names Names of the roles to decommission.
    @return Reference to the submitted command.
    """
    data = json.dumps({ ApiList.LIST_KEY : role_names })
    return self._cmd('decommission', data)

  def deploy_client_config(self, *role_names):
    """
    Deploys client configuration to the hosts where roles are running.

    @param: role_names Names of the roles to decommission.
    @return: Reference to the submitted command.
    """
    data = json.dumps({ ApiList.LIST_KEY : role_names })
    return self._cmd('deployClientConfig', data)

  def enable_hdfs_ha(self, active_name, active_shared_path, standby_name,
      standby_shared_path, nameservice):
    """
    Enable high availability for an HDFS NameNode.

    @param active_name: name of active NameNode.
    @param active_shared_path: shared edits path for active NameNode.
    @param standby_name: name of stand-by NameNode.
    @param standby_shared_path: shared edits path for stand-by NameNode.
    @param nameservice: name service for the HA pair.
    @return: Reference to the submitted command.
    """
    args = dict(
      activeName = active_name,
      activeSharedEditsPath = active_shared_path,
      standByName = standby_name,
      standBySharedEditsPath = standby_shared_path,
      nameservice = nameservice,
    )
    return self._cmd('hdfsEnableHa', data = json.dumps(args))

  def format_hdfs(self, *namenodes):
    """
    Format NameNode instances of an HDFS service.

    @param namenodes Name of NameNode instances to format.
    @return List of submitted commands.
    """
    return self._role_cmd('hdfsFormat', namenodes)

  def sync_hue_db(self, *servers):
    """
    Synchronize the Hue server's database.

    @param: servers Name of Hue Server roles to synchronize.
    @return: List of submitted commands.
    """
    return self._role_cmd('hueSyncDb', servers)

class ApiServiceSetupInfo(ApiService):
  RO_ATTR = ( )
  RW_ATTR = ('name', 'type', 'version', 'config', 'roleTypes', 'roles')

  def __init__(self, name=None, type=None, version=None,
               config=None, roleTypes=None, roles=None):
    # The BaseApiObject expects a resource_root, which we don't care about
    resource_root = None
    # Unfortunately, the json key is called "type". So our input arg
    # needs to be called "type" as well, despite it being a python keyword.
    BaseApiObject.ctor_helper(**locals())

  def set_config(self, config):
    """
    Set the service configuration.

    @param config: A dictionary of config key/value
    """
    self.config = config_to_api_list(config)

  def add_role_type_info(self, role_type, config):
    """
    Add a role type setup info.

    @param role_type: Role type
    @param config: A dictionary of role type configuration
    """
    if self.roleTypes is None:
      self.roleTypes = [ ]
    self.roleTypes.append({
        'roleType' : role_type,
        'config' : config_to_api_list(config) })

  def add_role_info(self, role_name, role_type, host_id, config=None):
    """
    Add a role info. The role will be created along with the service setup.

    @param role_name: Role name
    @param role_type: Role type
    @param host_id: The host where the role should run
    @param config: (Optional) A dictionary of role config values
    """
    if self.roles is None:
      self.roles = [ ]
    api_config_list = config is not None and config_to_api_list(config) or None
    self.roles.append({
        'name' : role_name,
        'type' : role_type,
        'hostRef' : { 'hostId' : host_id },
        'config' : api_config_list })

<commit_msg>[API] Add HDFS failover command.<commit_after># Copyright (c) 2012 Cloudera, Inc. All rights reserved.

try:
  import json
except ImportError:
  import simplejson as json
import logging

from cm_api.endpoints.types import config_to_json, json_to_config, \
    config_to_api_list, ApiCommand, ApiHostRef, ApiList, BaseApiObject
from cm_api.endpoints import roles, roletypes

__docformat__ = "epytext"

SERVICES_PATH = "/clusters/%s/services"
SERVICE_PATH = "/clusters/%s/services/%s"
ROLETYPES_PATH = "/clusters/%s/services/%s/roletypes"

LOG = logging.getLogger(__name__)

def create_service(resource_root, name, service_type, version,
                   cluster_name="default"):
  """
  Create a service
  @param resource_root: The root Resource object.
  @param name: Service name
  @param service_type: Service type
  @param version: Service version
  @param cluster_name: Cluster name
  @return: An ApiService object
  """
  apiservice = ApiService(resource_root, name, service_type, version)
  apiservice_list = ApiList([apiservice])
  body = json.dumps(apiservice_list.to_json_dict())
  resp = resource_root.post(SERVICES_PATH % (cluster_name,), data=body)
  # The server returns a list of created services (with size 1)
  return ApiList.from_json_dict(ApiService, resp, resource_root)[0]

def get_service(resource_root, name, cluster_name="default"):
  """
  Lookup a service by name
  @param resource_root: The root Resource object.
  @param name: Service name
  @param cluster_name: Cluster name
  @return: An ApiService object
  """
  dic = resource_root.get("%s/%s" % (SERVICES_PATH % (cluster_name,), name))
  return ApiService.from_json_dict(dic, resource_root)

def get_all_services(resource_root, cluster_name="default", view=None):
  """
  Get all services
  @param resource_root: The root Resource object.
  @param cluster_name: Cluster name
  @return: A list of ApiService objects.
  """
  dic = resource_root.get(SERVICES_PATH % (cluster_name,),
          params=view and dict(view=view) or None)
  return ApiList.from_json_dict(ApiService, dic, resource_root)

def delete_service(resource_root, name, cluster_name="default"):
  """
  Delete a service by name
  @param resource_root: The root Resource object.
  @param name: Service name
  @param cluster_name: Cluster name
  @return: The deleted ApiService object
  """
  resp = resource_root.delete("%s/%s" % (SERVICES_PATH % (cluster_name,), name))
  return ApiService.from_json_dict(resp, resource_root)

class ApiService(BaseApiObject):
  RO_ATTR = ('serviceState', 'healthSummary', 'healthChecks', 'clusterRef', 'configStale')
  RW_ATTR = ('name', 'type', 'version')

  def __init__(self, resource_root, name, type, version):
    # Unfortunately, the json key is called "type". So our input arg
    # needs to be called "type" as well, despite it being a python keyword.
    BaseApiObject.ctor_helper(**locals())

  def _get_cluster_name(self):
    if self.clusterRef:
      return self.clusterRef.clusterName
    return None

  def _path(self):
    """
    Return the API path for this service.

    This method assumes that lack of a cluster reference means that the
    object refers to the Cloudera Management Services instance.
    """
    if self._get_cluster_name():
      return SERVICE_PATH % (self._get_cluster_name(), self.name)
    else:
      return '/cm/service'

  def _cmd(self, cmd, data=None):
    path = self._path() + '/commands/' + cmd
    resp = self._get_resource_root().post(path, data=data)
    return ApiCommand.from_json_dict(resp, self._get_resource_root())

  def _role_cmd(self, cmd, roles):
    path = self._path() + '/roleCommands/' + cmd
    data = json.dumps({ ApiList.LIST_KEY : roles })
    resp = self._get_resource_root().post(path, data = data)
    return ApiList.from_json_dict(ApiCommand, resp, self._get_resource_root())

  def get_config(self, view = None):
    """
    Retrieve the service's configuration.

    The 'summary' view contains strings as the dictionary values. The full
    view contains ApiConfig instances as the values.

    @param view: View to materialize ('full' or 'summary')
    @return Dictionary with configuration data.
    """
    path = self._path() + '/config'
    resp = self._get_resource_root().get(path,
        params = view and dict(view=view) or None)
    return json_to_config(resp, view == 'full')

  def update_config(self, config):
    """
    Update the service's configuration.

    @param config Dictionary with configuration to update.
    @return Dictionary with updated configuration.
    """
    path = self._path() + '/config'
    resp = self._get_resource_root().put(path, data = config_to_json(config))
    return json_to_config(resp)

  def create_role(self, role_name, role_type, host_id):
    """
    Create a role.

    @param role_name: Role name
    @param role_type: Role type
    @return: An ApiRole object
    """
    return roles.create_role(self._get_resource_root(), self.name, role_type,
        role_name, host_id, self._get_cluster_name())

  def delete_role(self, name):
    """
    Delete a role by name.

    @param name Role name
    @return The deleted ApiRole object
    """
    return roles.delete_role(self._get_resource_root(), self.name, name,
        self._get_cluster_name())

  def get_role(self, name):
    """
    Lookup a role by name.

    @param name: Role name
    @return: An ApiRole object
    """
    return roles.get_role(self._get_resource_root(), self.name, name,
        self._get_cluster_name())

  def get_all_roles(self, view = None):
    """
    Get all roles in the service.

    @param view: View to materialize ('full' or 'summary')
    @return: A list of ApiRole objects.
    """
    return roles.get_all_roles(self._get_resource_root(), self.name,
        self._get_cluster_name(), view)

  def get_roles_by_type(self, role_type, view = None):
    """
    Get all roles of a certain type in a service.

    @param role_type: Role type
    @param view: View to materialize ('full' or 'summary')
    @return: A list of ApiRole objects.
    """
    return roles.get_roles_by_type(self._get_resource_root(), self.name,
        role_type, self._get_cluster_name(), view)

  def get_role_types(self):
    """
    Get a list of role types in a service.

    @return: A list of role types (strings)
    """
    if self.type == 'MGMT':
      LOG.error("Management service does not support /roleTypes")
      return None
    resp = self._get_resource_root().get(self._path() + '/roleTypes')
    return resp[ApiList.LIST_KEY]

  def get_role_type(self, roletype):
    """
    Get a role type resource by name.

    @param roletype: Role type
    @return: An ApiRoleType object
    """
    if self.type == 'MGMT':
      LOG.error("Management service does not support /roleTypes")
      return None
    return roletypes.ApiRoleType(self._get_resource_root(), self, roletype)

  def start(self):
    """
    Start a service.

    @return Reference to the submitted command.
    """
    return self._cmd('start')

  def stop(self):
    """
    Stop a service.

    @return Reference to the submitted command.
    """
    return self._cmd('stop')

  def restart(self):
    """
    Restart a service.

    @return Reference to the submitted command.
    """
    return self._cmd('restart')

  def create_beeswax_warehouse(self):
    """
    Create the Beeswax role's warehouse for a Hue service.
    @return: Reference to the submitted command.
    """
    return self._cmd('hueCreateHiveWarehouse')

  def create_hbase_root(self):
    """
    Create the root directory of an HBase service.

    @return Reference to the submitted command.
    """
    return self._cmd('hbaseCreateRoot')

  def refresh(self, *role_names):
    """
    Execute the "refresh" command on a set of roles.

    @param: role_names Names of the roles to decommission.
    @return: Reference to the submitted command.
    """
    return self._role_cmd('refresh', role_names)

  def decommission(self, *role_names):
    """
    Decommission roles in a service.

    @param role_names Names of the roles to decommission.
    @return Reference to the submitted command.
    """
    data = json.dumps({ ApiList.LIST_KEY : role_names })
    return self._cmd('decommission', data)

  def deploy_client_config(self, *role_names):
    """
    Deploys client configuration to the hosts where roles are running.

    @param: role_names Names of the roles to decommission.
    @return: Reference to the submitted command.
    """
    data = json.dumps({ ApiList.LIST_KEY : role_names })
    return self._cmd('deployClientConfig', data)

  def enable_hdfs_ha(self, active_name, active_shared_path, standby_name,
      standby_shared_path, nameservice):
    """
    Enable high availability for an HDFS NameNode.

    @param active_name: name of active NameNode.
    @param active_shared_path: shared edits path for active NameNode.
    @param standby_name: name of stand-by NameNode.
    @param standby_shared_path: shared edits path for stand-by NameNode.
    @param nameservice: name service for the HA pair.
    @return: Reference to the submitted command.
    """
    args = dict(
      activeName = active_name,
      activeSharedEditsPath = active_shared_path,
      standByName = standby_name,
      standBySharedEditsPath = standby_shared_path,
      nameservice = nameservice,
    )
    return self._cmd('hdfsEnableHa', data = json.dumps(args))

  def failover_hdfs(self, active_name, standby_name):
    """
    Initiate a failover of an HDFS NameNode HA pair.

    @param active_name: name of active NameNode.
    @param standby_name: name of stand-by NameNode.
    @return: Reference to the submitted command.
    """
    args = { ApiList.LIST_KEY : [ active_name, standby_name ] }
    return self._cmd('hdfsFailover', data = json.dumps(args))

  def format_hdfs(self, *namenodes):
    """
    Format NameNode instances of an HDFS service.

    @param namenodes Name of NameNode instances to format.
    @return List of submitted commands.
    """
    return self._role_cmd('hdfsFormat', namenodes)

  def sync_hue_db(self, *servers):
    """
    Synchronize the Hue server's database.

    @param: servers Name of Hue Server roles to synchronize.
    @return: List of submitted commands.
    """
    return self._role_cmd('hueSyncDb', servers)

class ApiServiceSetupInfo(ApiService):
  RO_ATTR = ( )
  RW_ATTR = ('name', 'type', 'version', 'config', 'roleTypes', 'roles')

  def __init__(self, name=None, type=None, version=None,
               config=None, roleTypes=None, roles=None):
    # The BaseApiObject expects a resource_root, which we don't care about
    resource_root = None
    # Unfortunately, the json key is called "type". So our input arg
    # needs to be called "type" as well, despite it being a python keyword.
    BaseApiObject.ctor_helper(**locals())

  def set_config(self, config):
    """
    Set the service configuration.

    @param config: A dictionary of config key/value
    """
    self.config = config_to_api_list(config)

  def add_role_type_info(self, role_type, config):
    """
    Add a role type setup info.

    @param role_type: Role type
    @param config: A dictionary of role type configuration
    """
    if self.roleTypes is None:
      self.roleTypes = [ ]
    self.roleTypes.append({
        'roleType' : role_type,
        'config' : config_to_api_list(config) })

  def add_role_info(self, role_name, role_type, host_id, config=None):
    """
    Add a role info. The role will be created along with the service setup.

    @param role_name: Role name
    @param role_type: Role type
    @param host_id: The host where the role should run
    @param config: (Optional) A dictionary of role config values
    """
    if self.roles is None:
      self.roles = [ ]
    api_config_list = config is not None and config_to_api_list(config) or None
    self.roles.append({
        'name' : role_name,
        'type' : role_type,
        'hostRef' : { 'hostId' : host_id },
        'config' : api_config_list })

<|endoftext|>


<commit_before>def main():
    import os
    import sys

    sys.path[0] = os.getcwd()
    os.chdir(sys.argv[1])
    secret = sys.argv[2]
    port = int(sys.argv[3])
    testFx = sys.argv[4]
    args = sys.argv[5:]
    from ptvsd.visualstudio_py_debugger import DONT_DEBUG, DEBUG_ENTRYPOINTS, get_code
    from ptvsd.attach_server import DEFAULT_PORT, enable_attach, wait_for_attach

    DONT_DEBUG.append(os.path.normcase(__file__))
    DEBUG_ENTRYPOINTS.add(get_code(main))

    enable_attach(secret, ('127.0.0.1', port), redirect_output = False)
    sys.stdout.flush()
    print('READY')
    sys.stdout.flush()
    wait_for_attach()

    try:
        if testFx == 'pytest':
            import pytest
            pytest.main(args)
        else:
            import nose
            nose.run(argv=args)
        sys.exit()
    finally:
        pass

if __name__ == '__main__':
    main()<commit_msg>fix #543<commit_after>def main():
    import os
    import sys
    from ptvsd.visualstudio_py_debugger import DONT_DEBUG, DEBUG_ENTRYPOINTS, get_code
    from ptvsd.attach_server import DEFAULT_PORT, enable_attach, wait_for_attach

    sys.path[0] = os.getcwd()
    os.chdir(sys.argv[1])
    secret = sys.argv[2]
    port = int(sys.argv[3])
    testFx = sys.argv[4]
    args = sys.argv[5:]

    DONT_DEBUG.append(os.path.normcase(__file__))
    DEBUG_ENTRYPOINTS.add(get_code(main))

    enable_attach(secret, ('127.0.0.1', port), redirect_output = False)
    sys.stdout.flush()
    print('READY')
    sys.stdout.flush()
    wait_for_attach()

    try:
        if testFx == 'pytest':
            import pytest
            pytest.main(args)
        else:
            import nose
            nose.run(argv=args)
        sys.exit()
    finally:
        pass

if __name__ == '__main__':
    main()<|endoftext|>