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

"""Module containing the AccessChecker class that contains helper functions
for checking access.
"""

__authors__ = [
    '"Daniel Hans" <daniel.m.hans@gmail.com>',
  ]

from django.utils.translation import ugettext

from google.appengine.api import users

from soc.logic.helper import timeline as timeline_helper
from soc.logic.models.user import logic as user_logic

from soc.logic.exceptions import LoginRequest
from soc.logic.exceptions import RedirectRequest
from soc.logic.exceptions import AccessViolation

from soc.modules.gsoc.models.organization import GSoCOrganization
from soc.modules.gsoc.models.proposal import GSoCProposal

DEF_AGREE_TO_TOS_MSG_FMT = ugettext(
    'You must agree to the site-wide Terms of'
    ' Service in your User Profile'
    ' in order to view this page.')

DEF_NOT_PARTICIPATING_MSG = ugettext(
    'You are not participating in this program and have no access.')

DEF_ALREADY_PARTICIPATING_MSG = ugettext(
    'You cannot become a Student because you are already participating '
    'in this program.')

DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext(
    'Please sign out'
    ' and sign in'
    ' again as %(role)s to view this page.')

DEF_NEED_ROLE_MSG = ugettext(
    'You do not have the required role.')

DEF_NO_ACTIVE_ENTITY_MSG = ugettext(
    'There is no such active entity.')

DEF_NO_USER_LOGIN_MSG = ugettext(
    'Please create User Profile'
    ' in order to view this page.')

DEF_PAGE_INACTIVE_MSG = ugettext(
    'This page is inactive at this time.')

DEF_SCOPE_INACTIVE_MSG = ugettext(
    'The scope for this request is not active.')

DEF_PROGRAM_INACTIVE_MSG_FMT = ugettext(
    'This page is inaccessible because %s is not active at this time.')

DEF_PAGE_INACTIVE_OUTSIDE_MSG_FMT = ugettext(
    'This page is inactive before %s and after %s.')

DEF_NO_SUCH_PROGRAM_MSG = ugettext(
    'The url is wrong (no program was found).')

DEF_ROLE_INACTIVE_MSG = ugettext(
    'This page is inaccessible because you do not have an active role '
    'in the program at this time.')

DEF_IS_NOT_STUDENT_MSG = ugettext(
    'This page is inaccessiible baucause you do not have a student role '
    'in the program.')

DEF_ORG_DOES_NOT_EXISTS_MSG_FMT = ugettext(
    'Organization, whose link_id is %(link_id)s, does not exist in '
    '%(program)s.')

DEF_ORG_NOT_ACTIVE_MSG_FMT = ugettext(
    'Organization %(name)s is not active in %(program)s.')

DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT = ugettext(
    '%(model)s entity, whose id is %(id)s, does not exist.')

DEF_ID_BASED_ENTITY_INVALID_MSG_FMT = ugettext(
    '%(model)s entity, whose id is %(id)s, is invalid at this time.')

DEF_ENTITY_DOES_NOT_BELONG_TO_YOU = ugettext(
    'This %(model)s entity does not belong to you.')

DEF_NOT_HOST_MSG = ugettext(
    'You need to be a program adminstrator to access this page.')

DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT_MSG = ugettext(
    'You cannot register as a student since you are already a '
    'mentor or organization administrator in %s.')

DEF_ALREADY_PARTICIPATING_AS_STUDENT_MSG = ugettext(
    'You cannot register as a %s since you are already a '
    'student in %s.')

DEF_NOT_VALID_INVITATION_MSG = ugettext(
    'This is not a valid invitation.')

DEF_HAS_ALREADY_ROLE_FOR_ORG_MSG = ugettext(
    'You already have %(role)s role for %(org)s.')

class AccessChecker(object):
  """Helper classes for access checking.
  """

  def __init__(self, data=None):
    """Initializes the access checker object.
    """

    self.data = data
    self.gae_user = users.get_current_user()

  def fail(self, message):
    """Raises an AccessViolation with the specified message.
    """
    raise AccessViolation(message)

  def isLoggedIn(self):
    """Raises an alternate HTTP response if Google Account is not logged in.
    """

    if self.gae_user:
      return

    raise LoginRequest()

  def isUser(self):
    """Raises an alternate HTTP response if Google Account has no User entity.

    Raises:
      AccessViolationResponse:
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
      * if User has not agreed to the site-wide ToS, if one exists
    """

    self.isLoggedIn()

    if self.data.user:
      return

    raise AccessViolation(DEF_NO_USER_LOGIN_MSG)

  def isHost(self):
    """Checks whether the current user has a host role.
    """

    if self.data.is_host:
      return

    raise AccessViolation(DEF_NOT_HOST_MSG)

  def hasUserEntity(self):
    """Raises an alternate HTTP response if Google Account has no User entity.

    Raises:
      AccessViolationResponse:
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
    """

    self.isLoggedIn()

    if self.data.user:
      return

    raise AccessViolation(DEF_NO_USER_LOGIN_MSG)

  def isDeveloper(self):
    """Raises an alternate HTTP response if Google Account is not a Developer.

    Raises:
      AccessViolationResponse:
      * if User is not a Developer, or
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
    """

    self.isUser()

    if self.data.user.is_developer:
      return

    if users.is_current_user_admin():
      return

    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
        'role': 'a Site Developer ',
        }

    raise AccessViolation(login_message_fmt)

  def isProgramActive(self):
    """Checks that the program is active.
    """

    if not self.data.program:
      raise AccessViolation(DEF_NO_SUCH_PROGRAM_MSG)

    if self.data.program.status == 'visible':
      return

    raise AccessViolation(
        DEF_PROGRAM_INACTIVE_MSG_FMT % self.data.program.name)

  def isActivePeriod(self, period_name):
    """Checks if the given period is active for the given program.

    Args:
      period_name: the name of the period which is checked
    """
    self.isProgramActive()

    if timeline_helper.isActivePeriod(self.data.program_timeline, period_name):
      return

    active_period = timeline_helper.activePeriod(self.data.program_timeline, period_name)

    raise AccessViolation(DEF_PAGE_INACTIVE_OUTSIDE_MSG_FMT % active_period)

  def canApplyStudent(self, edit_url):
    """Checks if the user can apply as a student.
    """

    if self.data.profile and self.data.profile.student_info:
      raise RedirectRequest(edit_url)

    self.isActivePeriod('student_signup')

    if not self.data.profile:
      return

    raise AccessViolation(
        DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT_MSG % self.data.program.name)

  def canApplyNonStudent(self, role, edit_url):
    """Checks if the user can apply as a mentor or org admin.
    """

    if not self.data.profile.student_info:
      raise RedirectRequest(edit_url)

    if not self.data.profile:
      return

    raise AccessViolation(DEF_ALREADY_PARTICIPATING_AS_STUDENT_MSG % (
        role, self.data.program.name))

  def isActive(self, entity):
    """Checks if the specified entity is active.
    """

    if entity.status == 'active':
      return

    raise AccessViolation(DEF_NO_ACTIVE_ENTITY_MSG)

  def isRoleActive(self):
    """Checks if the role of the current user is active.
    """

    if self.data.profile and self.data.profile.status == 'active':
      return

    raise AccessViolation(DEF_ROLE_INACTIVE_MSG)

  def isActiveStudent(self):
    """Checks if the user is an active student.
    """

    self.isRoleActive()

    if self.data.student_info:
      return

    raise AccessViolation(DEF_IS_NOT_STUDENT_MSG)

  def notHaveRoleForOrganization(self, org, role):
    """Checks if the user have not the specified role for the organization.
    """

    if not self.data.profile:
      return

    if role == 'org_admin':
      roles = self.data.profile.org_admin_for
    else:
      roles = self.data.profile.mentor_for

    key = org.key()
    if key in roles:
      error_msg = DEF_HAS_ALREADY_ROLE_FOR_ORG_MSG % {
          'role': 'Mentor' if role == 'mentor' else 'Org Admin',
          'org': org.name
          }
      raise AccessViolation(error_msg)

  def haveRoleForOrganization(self, org, role):
    """Checks if the user has the specified role for the organization.
    """

    self.isRoleActive()

    if role == 'org_admin':
      roles = self.data.profile.org_admin_for
    else:
      roles = self.data.profile.mentor_for

    key = org.key()
    if key in roles:
      return

    AccessViolation(DEF_NEED_ROLE_MSG)

  def isOrganizationInURLActive(self):
    """Checks if the organization in URL exists and if its status is active.

    Side effects (RequestData):
      - if the organization exists and is active, it is saved as 'organization'
    """

    # kwargs which defines an organization
    fields = ['sponsor', 'program', 'organization']

    key_name = '/'.join(self.data.kwargs[field] for field in fields)
    self.data.organization = GSoCOrganization.get_by_key_name(key_name)

    if not self.data.organization:
      error_msg = DEF_ORG_DOES_NOT_EXISTS_MSG_FMT % {
          'link_id': self.data.kwargs['organization'],
          'program': self.data.program.name
          }
      raise AccessViolation(error_msg)

    if self.data.organization.status != 'active':
      error_msg = DEF_ORG_NOT_ACTIVE_MSG_FMT % {
          'name': self.data.organization.name,
          'program': self.data.program.name
          }
      raise AccessViolation(error_msg)

  def isProposalInURLValid(self):
    """Checks if the proposal in URL exists.

    Side effects (RequestData):
      - if the proposal exists and is active, it is saved as 'proposal'
    """

    id = int(self.data.kwargs['id'])
    self.data.proposal = GSoCProposal.get_by_id(id, parent=self.data.profile)

    if not self.data.proposal:
      error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id
          }
      raise AccessViolation(error_msg)

    if self.data.proposal.status == 'invalid':
      error_msg = DEF_ID_BASED_ENTITY_INVALID_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id,
          }
      raise AccessViolation(error_msg)

  def canStudentUpdateProposal(self):
    """Checks if the student is eligible to submit a proposal.
    """

    # check if the timeline allows updating proposals
    self.isActivePeriod('student_signup')

    # TODO: it should be changed - we should not assume that the proposal
    # is already in RequestData
    if self.data.proposal.status == 'invalid':
      error_msg = DEF_ID_BASED_ENTITY_INVALID_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id,
          }
      raise AccessViolation(error_msg)

    # check if the proposal belongs to the current user
    expected_profile = self.data.proposal.parent()
    if expected_profile.key().name() != self.data.profile.key().name():
      error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
          'model': 'GSoCProposal'
          }
      raise AccessViolation(error_msg)

  def isIdBasedEntityPresent(self, entity, id, model_name):
    """Checks if the entity is not None.
    """

    if entity is not None:
      return

    error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT % {
        'model': model_name,
        'id': id,
        }
    raise AccessViolation(error_msg)

  def isRequestPresent(self, entity, id):
    """Checks if the specified Request entity is not None.
    """

    self.isIdBasedEntityPresent(entity, id, 'Request')

  def canRespondToInvite(self):
    """Checks if the current user can accept the invitation.
    """

    assert self.data.invite
    assert self.data.org
    assert self.data.invited_user

    # check if the entity represents an invitation
    if self.data.invite.type != 'Invitation':
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity can be responded
    if self.data.invite.status not in ['new']:
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity is addressed to the current user
    if self.data.invited_user.key() != self.data.user.key():
      error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
          'model': 'Request'
          }
      raise AccessViolation(error_msg)

    # check if the user does not have this role
    self.notHaveRoleForOrganization(self.data.org, self.data.invite.role)

  def canViewInvite(self):
    """Checks if the current user can see the invitation.
    """

    assert self.data.invite
    assert self.data.org
    assert self.data.invited_user

    # check if the entity represents an invitation
    if self.data.invite.type != 'Invitation':
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity is addressed to the current user
    if self.data.invited_user.key() != self.data.user.key():
      # check if the current user is an org admin for the organization
      self.haveRoleForOrganization(self.data.org, 'org_admin')
      self.data.canRespond = False

    self.data.canRespond = True
<commit_msg>Add missing raise statement to access checker<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module containing the AccessChecker class that contains helper functions
for checking access.
"""

__authors__ = [
    '"Daniel Hans" <daniel.m.hans@gmail.com>',
  ]

from django.utils.translation import ugettext

from google.appengine.api import users

from soc.logic.helper import timeline as timeline_helper
from soc.logic.models.user import logic as user_logic

from soc.logic.exceptions import LoginRequest
from soc.logic.exceptions import RedirectRequest
from soc.logic.exceptions import AccessViolation

from soc.modules.gsoc.models.organization import GSoCOrganization
from soc.modules.gsoc.models.proposal import GSoCProposal

DEF_AGREE_TO_TOS_MSG_FMT = ugettext(
    'You must agree to the site-wide Terms of'
    ' Service in your User Profile'
    ' in order to view this page.')

DEF_NOT_PARTICIPATING_MSG = ugettext(
    'You are not participating in this program and have no access.')

DEF_ALREADY_PARTICIPATING_MSG = ugettext(
    'You cannot become a Student because you are already participating '
    'in this program.')

DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext(
    'Please sign out'
    ' and sign in'
    ' again as %(role)s to view this page.')

DEF_NEED_ROLE_MSG = ugettext(
    'You do not have the required role.')

DEF_NO_ACTIVE_ENTITY_MSG = ugettext(
    'There is no such active entity.')

DEF_NO_USER_LOGIN_MSG = ugettext(
    'Please create User Profile'
    ' in order to view this page.')

DEF_PAGE_INACTIVE_MSG = ugettext(
    'This page is inactive at this time.')

DEF_SCOPE_INACTIVE_MSG = ugettext(
    'The scope for this request is not active.')

DEF_PROGRAM_INACTIVE_MSG_FMT = ugettext(
    'This page is inaccessible because %s is not active at this time.')

DEF_PAGE_INACTIVE_OUTSIDE_MSG_FMT = ugettext(
    'This page is inactive before %s and after %s.')

DEF_NO_SUCH_PROGRAM_MSG = ugettext(
    'The url is wrong (no program was found).')

DEF_ROLE_INACTIVE_MSG = ugettext(
    'This page is inaccessible because you do not have a profile '
    'in the program at this time.')

DEF_IS_NOT_STUDENT_MSG = ugettext(
    'This page is inaccessiible baucause you do not have a student role '
    'in the program.')

DEF_ORG_DOES_NOT_EXISTS_MSG_FMT = ugettext(
    'Organization, whose link_id is %(link_id)s, does not exist in '
    '%(program)s.')

DEF_ORG_NOT_ACTIVE_MSG_FMT = ugettext(
    'Organization %(name)s is not active in %(program)s.')

DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT = ugettext(
    '%(model)s entity, whose id is %(id)s, does not exist.')

DEF_ID_BASED_ENTITY_INVALID_MSG_FMT = ugettext(
    '%(model)s entity, whose id is %(id)s, is invalid at this time.')

DEF_ENTITY_DOES_NOT_BELONG_TO_YOU = ugettext(
    'This %(model)s entity does not belong to you.')

DEF_NOT_HOST_MSG = ugettext(
    'You need to be a program adminstrator to access this page.')

DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT_MSG = ugettext(
    'You cannot register as a student since you are already a '
    'mentor or organization administrator in %s.')

DEF_ALREADY_PARTICIPATING_AS_STUDENT_MSG = ugettext(
    'You cannot register as a %s since you are already a '
    'student in %s.')

DEF_NOT_VALID_INVITATION_MSG = ugettext(
    'This is not a valid invitation.')

DEF_HAS_ALREADY_ROLE_FOR_ORG_MSG = ugettext(
    'You already have %(role)s role for %(org)s.')

class AccessChecker(object):
  """Helper classes for access checking.
  """

  def __init__(self, data=None):
    """Initializes the access checker object.
    """

    self.data = data
    self.gae_user = users.get_current_user()

  def fail(self, message):
    """Raises an AccessViolation with the specified message.
    """
    raise AccessViolation(message)

  def isLoggedIn(self):
    """Raises an alternate HTTP response if Google Account is not logged in.
    """

    if self.gae_user:
      return

    raise LoginRequest()

  def isUser(self):
    """Raises an alternate HTTP response if Google Account has no User entity.

    Raises:
      AccessViolationResponse:
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
      * if User has not agreed to the site-wide ToS, if one exists
    """

    self.isLoggedIn()

    if self.data.user:
      return

    raise AccessViolation(DEF_NO_USER_LOGIN_MSG)

  def isHost(self):
    """Checks whether the current user has a host role.
    """

    if self.data.is_host:
      return

    raise AccessViolation(DEF_NOT_HOST_MSG)

  def hasUserEntity(self):
    """Raises an alternate HTTP response if Google Account has no User entity.

    Raises:
      AccessViolationResponse:
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
    """

    self.isLoggedIn()

    if self.data.user:
      return

    raise AccessViolation(DEF_NO_USER_LOGIN_MSG)

  def isDeveloper(self):
    """Raises an alternate HTTP response if Google Account is not a Developer.

    Raises:
      AccessViolationResponse:
      * if User is not a Developer, or
      * if no User exists for the logged-in Google Account, or
      * if no Google Account is logged in at all
    """

    self.isUser()

    if self.data.user.is_developer:
      return

    if users.is_current_user_admin():
      return

    login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
        'role': 'a Site Developer ',
        }

    raise AccessViolation(login_message_fmt)

  def isProgramActive(self):
    """Checks that the program is active.
    """

    if not self.data.program:
      raise AccessViolation(DEF_NO_SUCH_PROGRAM_MSG)

    if self.data.program.status == 'visible':
      return

    raise AccessViolation(
        DEF_PROGRAM_INACTIVE_MSG_FMT % self.data.program.name)

  def isActivePeriod(self, period_name):
    """Checks if the given period is active for the given program.

    Args:
      period_name: the name of the period which is checked
    """
    self.isProgramActive()

    if timeline_helper.isActivePeriod(self.data.program_timeline, period_name):
      return

    active_period = timeline_helper.activePeriod(self.data.program_timeline, period_name)

    raise AccessViolation(DEF_PAGE_INACTIVE_OUTSIDE_MSG_FMT % active_period)

  def canApplyStudent(self, edit_url):
    """Checks if the user can apply as a student.
    """

    if self.data.profile and self.data.profile.student_info:
      raise RedirectRequest(edit_url)

    self.isActivePeriod('student_signup')

    if not self.data.profile:
      return

    raise AccessViolation(
        DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT_MSG % self.data.program.name)

  def canApplyNonStudent(self, role, edit_url):
    """Checks if the user can apply as a mentor or org admin.
    """

    if not self.data.profile.student_info:
      raise RedirectRequest(edit_url)

    if not self.data.profile:
      return

    raise AccessViolation(DEF_ALREADY_PARTICIPATING_AS_STUDENT_MSG % (
        role, self.data.program.name))

  def isActive(self, entity):
    """Checks if the specified entity is active.
    """

    if entity.status == 'active':
      return

    raise AccessViolation(DEF_NO_ACTIVE_ENTITY_MSG)

  def isRoleActive(self):
    """Checks if the role of the current user is active.
    """

    if self.data.profile and self.data.profile.status == 'active':
      return

    raise AccessViolation(DEF_ROLE_INACTIVE_MSG)

  def isActiveStudent(self):
    """Checks if the user is an active student.
    """

    self.isRoleActive()

    if self.data.student_info:
      return

    raise AccessViolation(DEF_IS_NOT_STUDENT_MSG)

  def notHaveRoleForOrganization(self, org, role):
    """Checks if the user have not the specified role for the organization.
    """

    if not self.data.profile:
      return

    if role == 'org_admin':
      roles = self.data.profile.org_admin_for
    else:
      roles = self.data.profile.mentor_for

    key = org.key()
    if key in roles:
      error_msg = DEF_HAS_ALREADY_ROLE_FOR_ORG_MSG % {
          'role': 'Mentor' if role == 'mentor' else 'Org Admin',
          'org': org.name
          }
      raise AccessViolation(error_msg)

  def haveRoleForOrganization(self, org, role):
    """Checks if the user has the specified role for the organization.
    """

    self.isRoleActive()

    if role == 'org_admin':
      roles = self.data.profile.org_admin_for
    else:
      roles = self.data.profile.mentor_for

    key = org.key()
    if key in roles:
      return

    raise AccessViolation(DEF_NEED_ROLE_MSG)

  def isOrganizationInURLActive(self):
    """Checks if the organization in URL exists and if its status is active.

    Side effects (RequestData):
      - if the organization exists and is active, it is saved as 'organization'
    """

    # kwargs which defines an organization
    fields = ['sponsor', 'program', 'organization']

    key_name = '/'.join(self.data.kwargs[field] for field in fields)
    self.data.organization = GSoCOrganization.get_by_key_name(key_name)

    if not self.data.organization:
      error_msg = DEF_ORG_DOES_NOT_EXISTS_MSG_FMT % {
          'link_id': self.data.kwargs['organization'],
          'program': self.data.program.name
          }
      raise AccessViolation(error_msg)

    if self.data.organization.status != 'active':
      error_msg = DEF_ORG_NOT_ACTIVE_MSG_FMT % {
          'name': self.data.organization.name,
          'program': self.data.program.name
          }
      raise AccessViolation(error_msg)

  def isProposalInURLValid(self):
    """Checks if the proposal in URL exists.

    Side effects (RequestData):
      - if the proposal exists and is active, it is saved as 'proposal'
    """

    id = int(self.data.kwargs['id'])
    self.data.proposal = GSoCProposal.get_by_id(id, parent=self.data.profile)

    if not self.data.proposal:
      error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id
          }
      raise AccessViolation(error_msg)

    if self.data.proposal.status == 'invalid':
      error_msg = DEF_ID_BASED_ENTITY_INVALID_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id,
          }
      raise AccessViolation(error_msg)

  def canStudentUpdateProposal(self):
    """Checks if the student is eligible to submit a proposal.
    """

    # check if the timeline allows updating proposals
    self.isActivePeriod('student_signup')

    # TODO: it should be changed - we should not assume that the proposal
    # is already in RequestData
    if self.data.proposal.status == 'invalid':
      error_msg = DEF_ID_BASED_ENTITY_INVALID_MSG_FMT % {
          'model': 'GSoCProposal',
          'id': id,
          }
      raise AccessViolation(error_msg)

    # check if the proposal belongs to the current user
    expected_profile = self.data.proposal.parent()
    if expected_profile.key().name() != self.data.profile.key().name():
      error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
          'model': 'GSoCProposal'
          }
      raise AccessViolation(error_msg)

  def isIdBasedEntityPresent(self, entity, id, model_name):
    """Checks if the entity is not None.
    """

    if entity is not None:
      return

    error_msg = DEF_ID_BASED_ENTITY_NOT_EXISTS_MSG_FMT % {
        'model': model_name,
        'id': id,
        }
    raise AccessViolation(error_msg)

  def isRequestPresent(self, entity, id):
    """Checks if the specified Request entity is not None.
    """

    self.isIdBasedEntityPresent(entity, id, 'Request')

  def canRespondToInvite(self):
    """Checks if the current user can accept the invitation.
    """

    assert self.data.invite
    assert self.data.org
    assert self.data.invited_user

    # check if the entity represents an invitation
    if self.data.invite.type != 'Invitation':
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity can be responded
    if self.data.invite.status not in ['new']:
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity is addressed to the current user
    if self.data.invited_user.key() != self.data.user.key():
      error_msg = DEF_ENTITY_DOES_NOT_BELONG_TO_YOU % {
          'model': 'Request'
          }
      raise AccessViolation(error_msg)

    # check if the user does not have this role
    self.notHaveRoleForOrganization(self.data.org, self.data.invite.role)

  def canViewInvite(self):
    """Checks if the current user can see the invitation.
    """

    assert self.data.invite
    assert self.data.org
    assert self.data.invited_user

    # check if the entity represents an invitation
    if self.data.invite.type != 'Invitation':
      raise AccessViolation(DEF_NOT_VALID_INVITATION_MSG)

    # check if the entity is addressed to the current user
    if self.data.invited_user.key() != self.data.user.key():
      # check if the current user is an org admin for the organization
      self.haveRoleForOrganization(self.data.org, 'org_admin')
      self.data.canRespond = False

    self.data.canRespond = True
<|endoftext|>


<commit_before>import imp
import os
import sys

module_name = 'donlib'
here_dir = os.path.dirname(os.path.abspath(__file__))
module_path = os.path.join(here_dir, '../../')
sys.path.append(module_path)
fp, pathname, description = imp.find_module(module_name)
donlib = imp.load_module(module_name, fp, pathname, description)

api_key_id = "APIKEYSAMPLE000"
api_secret_key = "APISECRETKEYSAMPLE000"
api_hostname = "api.cloudpassage.com"
api_port = "443"
slack_token = "hello-i-am-a-slack-token"

class TestUnitHalo:
    def instantiate_config_helper(self, monkeypatch):
        monkeypatch.setenv('HALO_API_KEY', api_key_id)
        monkeypatch.setenv('HALO_API_SECRET_KEY', api_secret_key)
        monkeypatch.setenv('HALO_API_HOSTNAME', api_hostname)
        monkeypatch.setenv('HALO_API_PORT', api_port)
        monkeypatch.setenv('SLACK_API_TOKEN', slack_token)
        config_obj = donlib.ConfigHelper()
        return config_obj

    def test_unit_halo_init(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        assert donlib.Halo(cfg)

    def test_unit_help_text(self):
        assert donlib.Halo.help_text()

    def test_unit_selfie(self):
        assert donlib.Halo.take_selfie()

    def test_unit_interrogate_help_text(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg)
        assert h_obj.interrogate("help", "help")

    def test_unit_interrogate_selfie(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg)
        assert h_obj.interrogate("selfie", "selfie")

    def test_unit_credentials_work(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg)
        assert h_obj.credentials_work() is False
<commit_msg>fix: test: Corrected unit tests for Halo module !minor<commit_after>import imp
import os
import sys

module_name = 'donlib'
here_dir = os.path.dirname(os.path.abspath(__file__))
module_path = os.path.join(here_dir, '../../')
sys.path.append(module_path)
fp, pathname, description = imp.find_module(module_name)
donlib = imp.load_module(module_name, fp, pathname, description)

api_key_id = "APIKEYSAMPLE000"
api_secret_key = "APISECRETKEYSAMPLE000"
api_hostname = "api.cloudpassage.com"
api_port = "443"
slack_token = "hello-i-am-a-slack-token"

class TestUnitHalo:
    def instantiate_config_helper(self, monkeypatch):
        monkeypatch.setenv('HALO_API_KEY', api_key_id)
        monkeypatch.setenv('HALO_API_SECRET_KEY', api_secret_key)
        monkeypatch.setenv('HALO_API_HOSTNAME', api_hostname)
        monkeypatch.setenv('HALO_API_PORT', api_port)
        monkeypatch.setenv('SLACK_API_TOKEN', slack_token)
        config_obj = donlib.ConfigHelper()
        return config_obj

    def test_unit_halo_init(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        assert donlib.Halo(cfg, "")

    def test_unit_help_text(self):
        assert donlib.Halo.help_text()

    def test_unit_selfie(self):
        assert donlib.Halo.take_selfie()

    def test_unit_interrogate_help_text(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg, "")
        assert h_obj.interrogate("help", "help")

    def test_unit_interrogate_selfie(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg, "")
        assert h_obj.interrogate("selfie", "selfie")

    def test_unit_credentials_work(self, monkeypatch):
        cfg = self.instantiate_config_helper(monkeypatch)
        h_obj = donlib.Halo(cfg, "")
        assert h_obj.credentials_work() is False
<|endoftext|>


<commit_before>import httpretty

from paystackapi.tests.base_test_case import BaseTestCase
from paystackapi.tcontrol import TransferControl

class TestTransfer(BaseTestCase):

    @httpretty.activate
    def test_check_balance(self):
        """Method defined to test check_balance."""
        httpretty.register_uri(
            httpretty.GET,
            self.endpoint_url("/balance"),
            content_type='text/json',
            body='{"status": true, "message": "Balances retrieved"}',
            status=201,
        )

        response = TransferControl.check_balance()
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_resend_otp(self):
        """Method defined to test resend_otp."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/resend_otp"),
            content_type='text/json',
            body='{"status": true, "message": "OTP has been resent"}',
            status=201,
        )

        response = TransferControl.resend_otp(
            transfer_code="TRF_vsyqdmlzble3uii",
            reason="Just do it."
        )
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_disable_otp(self):
        """Method defined to test disable_otp."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/disable_otp"),
            content_type='text/json',
            body='{"status": true,\
                "message": "OTP has been sent to mobile number ending with 4321"}',
            status=201,
        )

        response = TransferControl.disable_otp()
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_disable_otp_finalize(self):
        """Method defined to test disable_otp_finalize."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/disable_otp_finalize"),
            content_type='text/json',
            body='{"status": true,\
                "message": "OTP requirement for transfers has been disabled"}',
            status=201,
        )

        response = TransferControl.disable_otp_finalize(
            otp="928783",
        )
        self.assertTrue(response['status'])
<commit_msg>Add test for transfer control enable_otp<commit_after>import httpretty

from paystackapi.tests.base_test_case import BaseTestCase
from paystackapi.tcontrol import TransferControl

class TestTransfer(BaseTestCase):

    @httpretty.activate
    def test_check_balance(self):
        """Method defined to test check_balance."""
        httpretty.register_uri(
            httpretty.GET,
            self.endpoint_url("/balance"),
            content_type='text/json',
            body='{"status": true, "message": "Balances retrieved"}',
            status=201,
        )

        response = TransferControl.check_balance()
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_resend_otp(self):
        """Method defined to test resend_otp."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/resend_otp"),
            content_type='text/json',
            body='{"status": true, "message": "OTP has been resent"}',
            status=201,
        )

        response = TransferControl.resend_otp(
            transfer_code="TRF_vsyqdmlzble3uii",
            reason="Just do it."
        )
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_disable_otp(self):
        """Method defined to test disable_otp."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/disable_otp"),
            content_type='text/json',
            body='{"status": true,\
                "message": "OTP has been sent to mobile number ending with 4321"}',
            status=201,
        )

        response = TransferControl.disable_otp()
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_disable_otp_finalize(self):
        """Method defined to test disable_otp_finalize."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/disable_otp_finalize"),
            content_type='text/json',
            body='{"status": true,\
                "message": "OTP requirement for transfers has been disabled"}',
            status=201,
        )

        response = TransferControl.disable_otp_finalize(
            otp="928783",
        )
        self.assertTrue(response['status'])

    @httpretty.activate
    def test_enable_otp(self):
        """Method defined to test enable_otp."""
        httpretty.register_uri(
            httpretty.POST,
            self.endpoint_url("/transfer/enable_otp"),
            content_type='text/json',
            body='{"status": true,\
                "message": "OTP requirement for transfers has been enabled"}',
            status=201,
        )

        response = TransferControl.enable_otp()
        self.assertTrue(response['status'])
<|endoftext|>


<commit_before>import unittest

from robot.errors import DataError, ExecutionFailed
from robot.parsing.keywords import SetKeyword as SetKeywordData
from robot.running.timeouts import KeywordTimeout
from robot.running.keywords import Keyword, SetKeyword
from robot.utils.asserts import *
from test_testlibrary import _FakeNamespace

class OutputStub:

    def __getattr__(self, name):
        if name == 'syslog':
            return self
        return lambda *args: None

class MockHandler:

    type = 'mock'

    def __init__(self, name='Mock Handler', doc='Mock Doc', error=False):
        self.name = self.longname = name
        self.doc = self.shortdoc = doc
        self.error = error
        self.timeout = KeywordTimeout()

    def run(self, output, namespace, args):
        """Sets given args to self.ags and optionally returns something.

        Returning works so that if two args are given and the first one is
        string 'return' (case insensitive) the second argument is returned.
        """
        if self.error:
            raise DataError
        self.args = args
        if len(args) == 2 and args[0].lower() == 'return':
            return args[1]

class FakeNamespace(_FakeNamespace):

    def __init__(self, error=False):
        _FakeNamespace.__init__(self)
        self.error = error

    def get_handler(self, kwname):
        return MockHandler('Mocked.'+kwname, error=self.error)

class TestKeyword(unittest.TestCase):

    def test_run(self):
        for args in [ [], ['arg',], ['a1','a2'] ]:
            self._verify_run(args)

    def test_run_with_variables(self):
        for args in [ ['${str}',], ['a1','--${str}--'], ['@{list}',],
                           ['@{list}','${str}-${str}','@{list}','v3'] ]:
            self._verify_run(args)

    def test_run_with_escape(self):
        for args in [ ['\\ arg \\',], ['\\${str}',], ['\\\\${str}',], ]:
            self._verify_run(args)

    def test_run_error(self):
        kw = Keyword('handler_name', ())
        assert_raises(ExecutionFailed, kw.run, OutputStub(), FakeNamespace(error=True))

    def _verify_run(self, args):
        kw = Keyword('handler_name', args)
        assert_equals(kw.name, 'handler_name')
        assert_equals(kw.args, args)
        kw.run(OutputStub(), FakeNamespace())
        assert_equals(kw.name, 'Mocked.handler_name')
        assert_equals(kw.doc, 'Mock Doc')
        assert_equals(kw.handler_name, 'handler_name')

class TestSetKeyword(unittest.TestCase):

    def test_init_one_scalar_var(self):
        skw = SetKeyword(SetKeywordData(['${var}','Set','x']))
        assert_equal(skw.name, 'Set')
        assert_equal(skw.scalar_vars, ['${var}'])
        assert_none(skw.list_var)
        assert_equal(skw.args, ['x'])

    def test_init_three_scalar_vars(self):
        skw = SetKeyword(SetKeywordData('${v1} ${v2} ${v3} Set x y z'.split()))
        assert_equal(skw.scalar_vars, ['${v1}','${v2}','${v3}'])
        assert_none(skw.list_var)
        assert_equal(skw.args, ['x','y','z'])

    def test_init_list_var(self):
        skw = SetKeyword(SetKeywordData(['@{list}','Set','x','y','z']))
        assert_equal(skw.scalar_vars, [])
        assert_equal(skw.list_var, '@{list}')
        assert_equal(skw.args, ['x','y','z'])

    def test_init_two_scalar_and_one_list_vars(self):
        skw = SetKeyword(SetKeywordData('${v1} ${v2} @{list} Set x y z'.split()))
        assert_equal(skw.scalar_vars, ['${v1}','${v2}'])
        assert_equal(skw.list_var, '@{list}')
        assert_equal(skw.args, ['x','y','z'])

    def test_init_no_vars_raises(self):
        assert_raises(TypeError, SetKeywordData, ['Set','a'])

    def test_init_list_in_wrong_place_raises(self):
        assert_raises(DataError, SetKeywordData, ['@{list}','${str}','Set','a'])

    def test_init_no_keyword_raises(self):
        assert_raises(DataError, SetKeywordData, ['${var}'])

    def test_set_string_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN','value']))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], 'value')

    def test_set_object_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',self]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], self)

    def test_set_empty_list_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',[]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], [])

    def test_set_list_with_one_element_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',['hi']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], ['hi'])

    def test_set_strings_to_three_scalars(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','${v3}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'x')
        assert_equal(namespace.variables['${v2}'], 'y')
        assert_equal(namespace.variables['${v3}'], 'z')

    def test_set_objects_to_three_scalars(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','${v3}','KW','RETURN',[['x','y'],{},None]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], ['x','y'])
        assert_equal(namespace.variables['${v2}'], {})
        assert_equal(namespace.variables['${v3}'], None)

    def test_set_list_of_strings_to_list(self):
        skw = SetKeyword(SetKeywordData(['@{var}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['@{var}'], ['x','y','z'])

    def test_set_empty_list_to_list(self):
        skw = SetKeyword(SetKeywordData(['@{var}','KW','RETURN',[]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['@{var}'], [])

    def test_set_objects_to_two_scalars_and_list(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','@{v3}','KW','RETURN',['a',None,'x','y',{}]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'a')
        assert_equal(namespace.variables['${v2}'], None)
        assert_equal(namespace.variables['@{v3}'], ['x','y',{}])

    def test_set_scalars_and_list_so_that_list_is_empty(self):
        skw = SetKeyword(SetKeywordData(['${scal}','@{list}','KW','RETURN',['a']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${scal}'], 'a')
        assert_equal(namespace.variables['@{list}'], [])

    def test_set_more_values_than_variables(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'x')
        assert_equal(namespace.variables['${v2}'], ['y','z'])

    def test_set_too_few_scalars_raises(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','KW','RETURN',['x']]))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

    def test_set_list_but_no_list_raises(self):
        skw = SetKeyword(SetKeywordData(['@{list}','KW','RETURN','not a list']))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

    def test_set_too_few_scalars_with_list_raises(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','@{list}','KW','RETURN',['x']]))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

if __name__ == '__main__':
    unittest.main()
<commit_msg>fixed after api change<commit_after>import unittest

from robot.errors import DataError, ExecutionFailed
from robot.parsing.keywords import SetKeyword as SetKeywordData
from robot.running.timeouts import KeywordTimeout
from robot.running.keywords import Keyword, SetKeyword
from robot.utils.asserts import *
from test_testlibrary import _FakeNamespace

class OutputStub:

    def __getattr__(self, name):
        if name == 'syslog':
            return self
        return lambda *args: None

class MockHandler:

    type = 'mock'

    def __init__(self, name='Mock Handler', doc='Mock Doc', error=False):
        self.name = self.longname = name
        self.doc = self.shortdoc = doc
        self.error = error
        self.timeout = KeywordTimeout()

    def run(self, output, namespace, args):
        """Sets given args to self.ags and optionally returns something.

        Returning works so that if two args are given and the first one is
        string 'return' (case insensitive) the second argument is returned.
        """
        if self.error:
            raise DataError
        self.args = args
        if len(args) == 2 and args[0].lower() == 'return':
            return args[1]

class _FakeSuite(object):
    def __init__(self):
        self.status = 'RUNNING'

class FakeNamespace(_FakeNamespace):
    def __init__(self, error=False):
        _FakeNamespace.__init__(self)
        self.error = error
        self.suite = _FakeSuite()

    def get_handler(self, kwname):
        return MockHandler('Mocked.'+kwname, error=self.error)

class TestKeyword(unittest.TestCase):

    def test_run(self):
        for args in [ [], ['arg',], ['a1','a2'] ]:
            self._verify_run(args)

    def test_run_with_variables(self):
        for args in [ ['${str}',], ['a1','--${str}--'], ['@{list}',],
                           ['@{list}','${str}-${str}','@{list}','v3'] ]:
            self._verify_run(args)

    def test_run_with_escape(self):
        for args in [ ['\\ arg \\',], ['\\${str}',], ['\\\\${str}',], ]:
            self._verify_run(args)

    def test_run_error(self):
        kw = Keyword('handler_name', ())
        assert_raises(ExecutionFailed, kw.run, OutputStub(), FakeNamespace(error=True))

    def _verify_run(self, args):
        kw = Keyword('handler_name', args)
        assert_equals(kw.name, 'handler_name')
        assert_equals(kw.args, args)
        kw.run(OutputStub(), FakeNamespace())
        assert_equals(kw.name, 'Mocked.handler_name')
        assert_equals(kw.doc, 'Mock Doc')
        assert_equals(kw.handler_name, 'handler_name')

class TestSetKeyword(unittest.TestCase):

    def test_init_one_scalar_var(self):
        skw = SetKeyword(SetKeywordData(['${var}','Set','x']))
        assert_equal(skw.name, 'Set')
        assert_equal(skw.scalar_vars, ['${var}'])
        assert_none(skw.list_var)
        assert_equal(skw.args, ['x'])

    def test_init_three_scalar_vars(self):
        skw = SetKeyword(SetKeywordData('${v1} ${v2} ${v3} Set x y z'.split()))
        assert_equal(skw.scalar_vars, ['${v1}','${v2}','${v3}'])
        assert_none(skw.list_var)
        assert_equal(skw.args, ['x','y','z'])

    def test_init_list_var(self):
        skw = SetKeyword(SetKeywordData(['@{list}','Set','x','y','z']))
        assert_equal(skw.scalar_vars, [])
        assert_equal(skw.list_var, '@{list}')
        assert_equal(skw.args, ['x','y','z'])

    def test_init_two_scalar_and_one_list_vars(self):
        skw = SetKeyword(SetKeywordData('${v1} ${v2} @{list} Set x y z'.split()))
        assert_equal(skw.scalar_vars, ['${v1}','${v2}'])
        assert_equal(skw.list_var, '@{list}')
        assert_equal(skw.args, ['x','y','z'])

    def test_init_no_vars_raises(self):
        assert_raises(TypeError, SetKeywordData, ['Set','a'])

    def test_init_list_in_wrong_place_raises(self):
        assert_raises(DataError, SetKeywordData, ['@{list}','${str}','Set','a'])

    def test_init_no_keyword_raises(self):
        assert_raises(DataError, SetKeywordData, ['${var}'])

    def test_set_string_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN','value']))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], 'value')

    def test_set_object_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',self]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], self)

    def test_set_empty_list_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',[]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], [])

    def test_set_list_with_one_element_to_scalar(self):
        skw = SetKeyword(SetKeywordData(['${var}','KW','RETURN',['hi']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${var}'], ['hi'])

    def test_set_strings_to_three_scalars(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','${v3}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'x')
        assert_equal(namespace.variables['${v2}'], 'y')
        assert_equal(namespace.variables['${v3}'], 'z')

    def test_set_objects_to_three_scalars(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','${v3}','KW','RETURN',[['x','y'],{},None]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], ['x','y'])
        assert_equal(namespace.variables['${v2}'], {})
        assert_equal(namespace.variables['${v3}'], None)

    def test_set_list_of_strings_to_list(self):
        skw = SetKeyword(SetKeywordData(['@{var}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['@{var}'], ['x','y','z'])

    def test_set_empty_list_to_list(self):
        skw = SetKeyword(SetKeywordData(['@{var}','KW','RETURN',[]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['@{var}'], [])

    def test_set_objects_to_two_scalars_and_list(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','@{v3}','KW','RETURN',['a',None,'x','y',{}]]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'a')
        assert_equal(namespace.variables['${v2}'], None)
        assert_equal(namespace.variables['@{v3}'], ['x','y',{}])

    def test_set_scalars_and_list_so_that_list_is_empty(self):
        skw = SetKeyword(SetKeywordData(['${scal}','@{list}','KW','RETURN',['a']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${scal}'], 'a')
        assert_equal(namespace.variables['@{list}'], [])

    def test_set_more_values_than_variables(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','KW','RETURN',['x','y','z']]))
        namespace = FakeNamespace()
        skw.run(OutputStub(), namespace)
        assert_equal(namespace.variables['${v1}'], 'x')
        assert_equal(namespace.variables['${v2}'], ['y','z'])

    def test_set_too_few_scalars_raises(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','KW','RETURN',['x']]))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

    def test_set_list_but_no_list_raises(self):
        skw = SetKeyword(SetKeywordData(['@{list}','KW','RETURN','not a list']))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

    def test_set_too_few_scalars_with_list_raises(self):
        skw = SetKeyword(SetKeywordData(['${v1}','${v2}','@{list}','KW','RETURN',['x']]))
        assert_raises(ExecutionFailed, skw.run, OutputStub(), FakeNamespace())

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


<commit_before>from experiment_config_lib import ControllerConfig
from sts.topology import MeshTopology, BufferedPatchPanel
from sts.control_flow import Interactive, Fuzzer
from input_traces.input_logger import InputLogger

# Use POX as our controller
command_line = "./pox/pox.py --no-cli openflow.of_01 --address=__address__ --port=__port__ samples.topo forwarding.l2_learning messenger.messenger samples.nommessenger"
controllers = [ControllerConfig(command_line)]

# Use a FatTree with 4 pods (already the default)
# (specify the class, but don't instantiate the object)
topology_class = MeshTopology
# Comma-delimited list of arguments to pass into the FatTree constructor,
# specified just as you would type them within the parens.
topology_params = "num_switches=2"

# Use a BufferedPatchPanel (already the default)
# (specify the class, but don't instantiate the object)
patch_panel_class = BufferedPatchPanel

# Use a Fuzzer (already the default)
control_flow = Fuzzer(check_interval=80)

# Specify None as the dataplane trace (already the default)
# Otherwise, specify the path to the trace file
# (e.g. "dataplane_traces/ping_pong_same_subnet.trace")
dataplane_trace = "dataplane_traces/ping_pong_same_subnet.trace"
<commit_msg>config.cctest: BufferedPatchPanel -> PatchPanel<commit_after>from experiment_config_lib import ControllerConfig
from sts.topology import MeshTopology, BufferedPatchPanel
from sts.control_flow import Interactive, Fuzzer
from input_traces.input_logger import InputLogger

# Use POX as our controller
command_line = "./pox/pox.py --no-cli openflow.of_01 --address=__address__ --port=__port__ samples.topo forwarding.l2_learning messenger.messenger samples.nommessenger"
controllers = [ControllerConfig(command_line)]

# Use a FatTree with 4 pods (already the default)
# (specify the class, but don't instantiate the object)
topology_class = MeshTopology
# Comma-delimited list of arguments to pass into the FatTree constructor,
# specified just as you would type them within the parens.
topology_params = "num_switches=2"

# Use a BufferedPatchPanel (already the default)
# (specify the class, but don't instantiate the object)
patch_panel_class = PatchPanel

# Use a Fuzzer (already the default)
control_flow = Fuzzer(check_interval=80)

# Specify None as the dataplane trace (already the default)
# Otherwise, specify the path to the trace file
# (e.g. "dataplane_traces/ping_pong_same_subnet.trace")
dataplane_trace = "dataplane_traces/ping_pong_same_subnet.trace"
<|endoftext|>


<commit_before>import argparse
import boto
from os.path import basename
from time import sleep

FAILURE_STATES = [
    'CREATE_FAILED',
    'ROLLBACK_IN_PROGRESS',
    'ROLLBACK_FAILED',
    'ROLLBACK_COMPLETE',
    'DELETE_IN_PROGRESS',
    'DELETE_FAILED',
    'DELETE_COMPLETE',
    ]

def upload_file(file_path, bucket_name, key_name):
    """
    Upload a file to the given s3 bucket and return a template url.
    """
    conn = boto.connect_s3()
    try:
        bucket = conn.get_bucket(bucket_name)
    except boto.exception.S3ResponseError as e:
        conn.create_bucket(bucket_name)
        bucket = conn.get_bucket(bucket_name, validate=False)

    key = boto.s3.key.Key(bucket)
    key.key = key_name
    key.set_contents_from_filename(file_path)

    url = 'https://s3.amazonaws.com/{}/{}'.format(bucket_name, key_name)
    return url

def create_stack(stack_name, template, region='us-east-1', blocking=True, temp_bucket='edx-sandbox-devops'):
    cfn = boto.connect_cloudformation()

    # Upload the template to s3
    key_name = 'cloudformation/auto/{}_{}'.format(stack_name, basename(template))
    template_url = upload_file(template, temp_bucket, key_name)

    # Reference the stack.
    try:
        stack_id = cfn.create_stack(stack_name,
            template_url=template_url,
            capabilities=['CAPABILITY_IAM'],
            tags={'autostack':'true'},
            parameters=[('KeyName', 'continuous-integration')])
    except Exception as e:
        print(e.message)
        raise e

    status = None
    while blocking:
        sleep(5)
        stack_instance = cfn.describe_stacks(stack_id)[0]
        status = stack_instance.stack_status
        print(status)
        if 'COMPLETE' in status:
            break

    if status in FAILURE_STATES:
        raise Excoption('Creation Failed. Stack Status: {}, ID:{}'.format(
            status, stack_id))

    return stack_id

if __name__ == '__main__':
        description = 'Create a cloudformation stack from a template.'
        parser = argparse.ArgumentParser(description=description)

        msg = 'Name for the cloudformation stack.'
        parser.add_argument('-n', '--stackname', required=True, help=msg)

        msg = 'Name of the bucket to use for temporarily uploading the \
            template.'
        parser.add_argument('-b', '--bucketname', default="edx-sandbox-devops",
            help=msg)

        msg = 'The path to the cloudformation template.'
        parser.add_argument('-t', '--template', required=True, help=msg)

        msg = 'The AWS region to build this stack in.'
        parser.add_argument('-r', '--region', default='us-east-1', help=msg)

        args = parser.parse_args()
        stack_name = args.stackname
        template = args.template
        region = args.region
        bucket_name = args.bucketname

        create_stack(stack_name, template, region, bucket_name)
        print('Stack({}) created.'.format(stack_name))
<commit_msg>Fix spelling.<commit_after>import argparse
import boto
from os.path import basename
from time import sleep

FAILURE_STATES = [
    'CREATE_FAILED',
    'ROLLBACK_IN_PROGRESS',
    'ROLLBACK_FAILED',
    'ROLLBACK_COMPLETE',
    'DELETE_IN_PROGRESS',
    'DELETE_FAILED',
    'DELETE_COMPLETE',
    ]

def upload_file(file_path, bucket_name, key_name):
    """
    Upload a file to the given s3 bucket and return a template url.
    """
    conn = boto.connect_s3()
    try:
        bucket = conn.get_bucket(bucket_name)
    except boto.exception.S3ResponseError as e:
        conn.create_bucket(bucket_name)
        bucket = conn.get_bucket(bucket_name, validate=False)

    key = boto.s3.key.Key(bucket)
    key.key = key_name
    key.set_contents_from_filename(file_path)

    url = 'https://s3.amazonaws.com/{}/{}'.format(bucket_name, key_name)
    return url

def create_stack(stack_name, template, region='us-east-1', blocking=True, temp_bucket='edx-sandbox-devops'):
    cfn = boto.connect_cloudformation()

    # Upload the template to s3
    key_name = 'cloudformation/auto/{}_{}'.format(stack_name, basename(template))
    template_url = upload_file(template, temp_bucket, key_name)

    # Reference the stack.
    try:
        stack_id = cfn.create_stack(stack_name,
            template_url=template_url,
            capabilities=['CAPABILITY_IAM'],
            tags={'autostack':'true'},
            parameters=[('KeyName', 'continuous-integration')])
    except Exception as e:
        print(e.message)
        raise e

    status = None
    while blocking:
        sleep(5)
        stack_instance = cfn.describe_stacks(stack_id)[0]
        status = stack_instance.stack_status
        print(status)
        if 'COMPLETE' in status:
            break

    if status in FAILURE_STATES:
        raise Exception('Creation Failed. Stack Status: {}, ID:{}'.format(
            status, stack_id))

    return stack_id

if __name__ == '__main__':
        description = 'Create a cloudformation stack from a template.'
        parser = argparse.ArgumentParser(description=description)

        msg = 'Name for the cloudformation stack.'
        parser.add_argument('-n', '--stackname', required=True, help=msg)

        msg = 'Name of the bucket to use for temporarily uploading the \
            template.'
        parser.add_argument('-b', '--bucketname', default="edx-sandbox-devops",
            help=msg)

        msg = 'The path to the cloudformation template.'
        parser.add_argument('-t', '--template', required=True, help=msg)

        msg = 'The AWS region to build this stack in.'
        parser.add_argument('-r', '--region', default='us-east-1', help=msg)

        args = parser.parse_args()
        stack_name = args.stackname
        template = args.template
        region = args.region
        bucket_name = args.bucketname

        create_stack(stack_name, template, region, bucket_name)
        print('Stack({}) created.'.format(stack_name))
<|endoftext|>


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

"""Module containing fio installation, cleanup, parsing functions."""
import ConfigParser
import io

from perfkitbenchmarker import regex_util
from perfkitbenchmarker import sample
from perfkitbenchmarker import vm_util

FIO_DIR = '%s/fio' % vm_util.VM_TMP_DIR
GIT_REPO = 'http://git.kernel.dk/fio.git'
GIT_TAG = 'fio-2.1.14'
FIO_PATH = FIO_DIR + '/fio'
FIO_CMD_PREFIX = '%s --output-format=json' % FIO_PATH
SECTION_REGEX = r'\[(\w+)\]\n([\w\d\n=*$/]+)'
PARAMETER_REGEX = r'(\w+)=([/\w\d$*]+)\n'
GLOBAL = 'global'
CMD_SECTION_REGEX = r'--name=(\w+)\s+'
JOB_SECTION_REPL_REGEX = r'[\1]\n'
CMD_PARAMETER_REGEX = r'--(\w+=[/\w\d]+)\n'
CMD_PARAMETER_REPL_REGEX = r'\1\n'
CMD_STONEWALL_PARAMETER = '--stonewall'
JOB_STONEWALL_PARAMETER = 'stonewall'

def _Install(vm):
  """Installs the fio package on the VM."""
  vm.Install('build_tools')
  vm.RemoteCommand('git clone {0} {1}'.format(GIT_REPO, FIO_DIR))
  vm.RemoteCommand('cd {0} && git checkout {1}'.format(FIO_DIR, GIT_TAG))
  vm.RemoteCommand('cd {0} && ./configure && make'.format(FIO_DIR))

def YumInstall(vm):
  """Installs the fio package on the VM."""
  vm.InstallPackages('libaio-devel libaio bc')
  _Install(vm)

def AptInstall(vm):
  """Installs the fio package on the VM."""
  vm.InstallPackages('libaio-dev libaio1 bc')
  _Install(vm)

def ParseJobFile(job_file):
  """Parse fio job file as dictionaries of sample metadata.

  Args:
    job_file: The contents of fio job file.

  Returns:
    A dictionary of dictionaries of sample metadata, using test name as keys,
        dictionaries of sample metadata as value.
  """
  config = ConfigParser.RawConfigParser(allow_no_value=True)
  config.readfp(io.BytesIO(job_file))
  global_metadata = {}
  if GLOBAL in config.sections():
    global_metadata = dict(config.items(GLOBAL))
  section_metadata = {}
  for section in config.sections():
    if section != GLOBAL:
      metadata = {}
      metadata.update(dict(config.items(section)))
      metadata.update(global_metadata)
      if JOB_STONEWALL_PARAMETER in metadata:
        del metadata[JOB_STONEWALL_PARAMETER]
      section_metadata[section] = metadata
  return section_metadata

def FioParametersToJob(fio_parameters):
  """Translate fio parameters into a job config file.

  Sample fio parameters:
  --filesize=10g --directory=/scratch0
  --name=sequential_write --overwrite=0 --rw=write

  Output:
  [global]
  filesize=10g
  directory=/scratch0
  [sequential_write]
  overwrite=0
  rw=write

  Args:
    fio_parameter: string. Fio parameters in string format.

  Returns:
    A string representing a fio job config file.
  """
  fio_parameters = fio_parameters.replace(' ', '\n')
  fio_parameters = regex_util.Substitute(
      CMD_SECTION_REGEX, JOB_SECTION_REPL_REGEX, fio_parameters)
  fio_parameters = '[%s]\n%s' % (GLOBAL, fio_parameters)
  fio_parameters = regex_util.Substitute(
      CMD_PARAMETER_REGEX, CMD_PARAMETER_REPL_REGEX, fio_parameters)
  return fio_parameters.replace(CMD_STONEWALL_PARAMETER,
                                JOB_STONEWALL_PARAMETER)

def ParseResults(job_file, fio_json_result):
  """Parse fio json output into samples.

  Args:
    job_file: The contents of the fio job file.
    fio_json_result: Fio results in json format.

  Returns:
    A list of sample.Sample objects.
  """
  samples = []
  parameter_metadata = ParseJobFile(job_file)
  io_modes = ['read', 'write', 'trim']
  for job in fio_json_result['jobs']:
    job_name = job['jobname']
    for mode in io_modes:
      if job[mode]['io_bytes']:
        metric_name = '%s:%s' % (job_name, mode)
        parameters = parameter_metadata[job_name]
        bw_metadata = {
            'bw_min': job[mode]['bw_min'],
            'bw_max': job[mode]['bw_max'],
            'bw_dev': job[mode]['bw_dev'],
            'bw_agg': job[mode]['bw_agg'],
            'bw_mean': job[mode]['bw_mean']}
        bw_metadata.update(parameters)
        samples.append(
            sample.Sample('%s:bandwidth' % metric_name,
                          job[mode]['bw'],
                          'KB/s', bw_metadata))

        # There is one sample whose metric is '<metric_name>:latency'
        # with all of the latency percentiles in its metadata, and
        # then ten samples whose metrics are
        # '<metric_name>:latency:min' through
        # '<metric_name>:latency:p99.99' that hold the individual
        # latency numbers as values. This is for historical reasons.
        lat_metadata = {
            'min': job[mode]['clat']['min'],
            'max': job[mode]['clat']['max'],
            'stddev': job[mode]['clat']['stddev'],
            'p1': job[mode]['clat']['percentile']['1.000000'],
            'p5': job[mode]['clat']['percentile']['5.000000'],
            'p10': job[mode]['clat']['percentile']['10.000000'],
            'p20': job[mode]['clat']['percentile']['20.000000'],
            'p30': job[mode]['clat']['percentile']['30.000000'],
            'p40': job[mode]['clat']['percentile']['40.000000'],
            'p50': job[mode]['clat']['percentile']['50.000000'],
            'p60': job[mode]['clat']['percentile']['60.000000'],
            'p70': job[mode]['clat']['percentile']['70.000000'],
            'p80': job[mode]['clat']['percentile']['80.000000'],
            'p90': job[mode]['clat']['percentile']['90.000000'],
            'p95': job[mode]['clat']['percentile']['95.000000'],
            'p99': job[mode]['clat']['percentile']['99.000000'],
            'p99.5': job[mode]['clat']['percentile']['99.500000'],
            'p99.9': job[mode]['clat']['percentile']['99.900000'],
            'p99.95': job[mode]['clat']['percentile']['99.950000'],
            'p99.99': job[mode]['clat']['percentile']['99.990000']}
        lat_metadata.update(parameters)
        samples.append(
            sample.Sample('%s:latency' % metric_name,
                          job[mode]['clat']['mean'],
                          'usec', lat_metadata))

        samples.append(
            sample.Sample('%s:latency:min' % metric_name,
                          job[mode]['clat']['min'],
                          'usec', parameters))
        samples.append(
            sample.Sample('%s:latency:max' % metric_name,
                          job[mode]['clat']['max'],
                          'usec', parameters))
        samples.append(
            sample.Sample('%s:latency:mean' % metric_name,
                          job[mode]['clat']['mean'],
                          'usec', parameters))
        samples.append(
            sample.Sample('%s:latency:stddev' % metric_name,
                          job[mode]['clat']['stddev'],
                          'usec', parameters))
        # these tuples map the name we will record in our sample to
        # the name fio gave us in the JSON results
        latency_statistic_name_pairs = [
            ('p1', '1.000000'),
            ('p5', '5.000000'),
            ('p10', '10.000000'),
            ('p20', '20.000000'),
            ('p30', '30.000000'),
            ('p40', '40.000000'),
            ('p50', '50.000000'),
            ('p60', '60.000000'),
            ('p70', '70.000000'),
            ('p80', '80.000000'),
            ('p90', '90.000000'),
            ('p95', '95.000000'),
            ('p99', '99.000000'),
            ('p99.5', '99.500000'),
            ('p99.9', '99.900000'),
            ('p99.95', '99.950000'),
            ('p99.99', '99.990000')]
        for pkb_name, fio_name in latency_statistic_name_pairs:
          samples.append(
              sample.Sample('%s:latency:%s' % (metric_name, pkb_name),
                            job[mode]['clat']['percentile'][fio_name],
                            'usec', parameters))

        samples.append(
            sample.Sample('%s:iops' % metric_name,
                          job[mode]['iops'], '', parameters))
  return samples

def DeleteParameterFromJobFile(job_file, parameter):
  """Delete all occurance of parameter from job_file.

  Args:
    job_file: The contents of the fio job file.
    parameter: The parameter to be deleted in job file.

  Returns:
    A string representing a fio job file after removing parameter.
  """
  try:
    return regex_util.Substitute(r'%s=[\w\d_/]+\n' % parameter, '', job_file)
  except regex_util.NoMatchError:
    return job_file
<commit_msg>Refactor perfkitbenchmarker/packages/fio.py<commit_after># Copyright 2014 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module containing fio installation, cleanup, parsing functions."""
import ConfigParser
import io

from perfkitbenchmarker import regex_util
from perfkitbenchmarker import sample
from perfkitbenchmarker import vm_util

FIO_DIR = '%s/fio' % vm_util.VM_TMP_DIR
GIT_REPO = 'http://git.kernel.dk/fio.git'
GIT_TAG = 'fio-2.1.14'
FIO_PATH = FIO_DIR + '/fio'
FIO_CMD_PREFIX = '%s --output-format=json' % FIO_PATH
SECTION_REGEX = r'\[(\w+)\]\n([\w\d\n=*$/]+)'
PARAMETER_REGEX = r'(\w+)=([/\w\d$*]+)\n'
GLOBAL = 'global'
CMD_SECTION_REGEX = r'--name=(\w+)\s+'
JOB_SECTION_REPL_REGEX = r'[\1]\n'
CMD_PARAMETER_REGEX = r'--(\w+=[/\w\d]+)\n'
CMD_PARAMETER_REPL_REGEX = r'\1\n'
CMD_STONEWALL_PARAMETER = '--stonewall'
JOB_STONEWALL_PARAMETER = 'stonewall'

def _Install(vm):
  """Installs the fio package on the VM."""
  vm.Install('build_tools')
  vm.RemoteCommand('git clone {0} {1}'.format(GIT_REPO, FIO_DIR))
  vm.RemoteCommand('cd {0} && git checkout {1}'.format(FIO_DIR, GIT_TAG))
  vm.RemoteCommand('cd {0} && ./configure && make'.format(FIO_DIR))

def YumInstall(vm):
  """Installs the fio package on the VM."""
  vm.InstallPackages('libaio-devel libaio bc')
  _Install(vm)

def AptInstall(vm):
  """Installs the fio package on the VM."""
  vm.InstallPackages('libaio-dev libaio1 bc')
  _Install(vm)

def ParseJobFile(job_file):
  """Parse fio job file as dictionaries of sample metadata.

  Args:
    job_file: The contents of fio job file.

  Returns:
    A dictionary of dictionaries of sample metadata, using test name as keys,
        dictionaries of sample metadata as value.
  """
  config = ConfigParser.RawConfigParser(allow_no_value=True)
  config.readfp(io.BytesIO(job_file))
  global_metadata = {}
  if GLOBAL in config.sections():
    global_metadata = dict(config.items(GLOBAL))
  section_metadata = {}
  for section in config.sections():
    if section != GLOBAL:
      metadata = {}
      metadata.update(dict(config.items(section)))
      metadata.update(global_metadata)
      if JOB_STONEWALL_PARAMETER in metadata:
        del metadata[JOB_STONEWALL_PARAMETER]
      section_metadata[section] = metadata
  return section_metadata

def FioParametersToJob(fio_parameters):
  """Translate fio parameters into a job config file.

  Sample fio parameters:
  --filesize=10g --directory=/scratch0
  --name=sequential_write --overwrite=0 --rw=write

  Output:
  [global]
  filesize=10g
  directory=/scratch0
  [sequential_write]
  overwrite=0
  rw=write

  Args:
    fio_parameter: string. Fio parameters in string format.

  Returns:
    A string representing a fio job config file.
  """
  fio_parameters = fio_parameters.replace(' ', '\n')
  fio_parameters = regex_util.Substitute(
      CMD_SECTION_REGEX, JOB_SECTION_REPL_REGEX, fio_parameters)
  fio_parameters = '[%s]\n%s' % (GLOBAL, fio_parameters)
  fio_parameters = regex_util.Substitute(
      CMD_PARAMETER_REGEX, CMD_PARAMETER_REPL_REGEX, fio_parameters)
  return fio_parameters.replace(CMD_STONEWALL_PARAMETER,
                                JOB_STONEWALL_PARAMETER)

def ParseResults(job_file, fio_json_result):
  """Parse fio json output into samples.

  Args:
    job_file: The contents of the fio job file.
    fio_json_result: Fio results in json format.

  Returns:
    A list of sample.Sample objects.
  """
  samples = []
  parameter_metadata = ParseJobFile(job_file)
  io_modes = ['read', 'write', 'trim']
  for job in fio_json_result['jobs']:
    job_name = job['jobname']
    for mode in io_modes:
      if job[mode]['io_bytes']:
        metric_name = '%s:%s' % (job_name, mode)
        parameters = parameter_metadata[job_name]
        bw_metadata = {
            'bw_min': job[mode]['bw_min'],
            'bw_max': job[mode]['bw_max'],
            'bw_dev': job[mode]['bw_dev'],
            'bw_agg': job[mode]['bw_agg'],
            'bw_mean': job[mode]['bw_mean']}
        bw_metadata.update(parameters)
        samples.append(
            sample.Sample('%s:bandwidth' % metric_name,
                          job[mode]['bw'],
                          'KB/s', bw_metadata))

        # There is one sample whose metric is '<metric_name>:latency'
        # with all of the latency statistics in its metadata, and then
        # ten samples whose metrics are '<metric_name>:latency:min'
        # through '<metric_name>:latency:p99.99' that hold the
        # individual latency numbers as values. This is for historical
        # reasons.
        lat_statistics = [
            ('min', job[mode]['clat']['min']),
            ('max', job[mode]['clat']['max']),
            ('mean', job[mode]['clat']['mean']),
            ('stddev', job[mode]['clat']['stddev']),
            ('p1', job[mode]['clat']['percentile']['1.000000']),
            ('p5', job[mode]['clat']['percentile']['5.000000']),
            ('p10', job[mode]['clat']['percentile']['10.000000']),
            ('p20', job[mode]['clat']['percentile']['20.000000']),
            ('p30', job[mode]['clat']['percentile']['30.000000']),
            ('p40', job[mode]['clat']['percentile']['40.000000']),
            ('p50', job[mode]['clat']['percentile']['50.000000']),
            ('p60', job[mode]['clat']['percentile']['60.000000']),
            ('p70', job[mode]['clat']['percentile']['70.000000']),
            ('p80', job[mode]['clat']['percentile']['80.000000']),
            ('p90', job[mode]['clat']['percentile']['90.000000']),
            ('p95', job[mode]['clat']['percentile']['95.000000']),
            ('p99', job[mode]['clat']['percentile']['99.000000']),
            ('p99.5', job[mode]['clat']['percentile']['99.500000']),
            ('p99.9', job[mode]['clat']['percentile']['99.900000']),
            ('p99.95', job[mode]['clat']['percentile']['99.950000']),
            ('p99.99', job[mode]['clat']['percentile']['99.990000'])]

        lat_metadata = parameters.copy()
        for name, val in lat_statistics:
          lat_metadata[name] = val
        samples.append(
            sample.Sample('%s:latency' % metric_name,
                          job[mode]['clat']['mean'],
                          'usec', lat_metadata))

        for stat_name, stat_val in lat_statistics:
          samples.append(
              sample.Sample('%s:latency:%s' % (metric_name, stat_name),
                            stat_val, 'usec', parameters))

        samples.append(
            sample.Sample('%s:iops' % metric_name,
                          job[mode]['iops'], '', parameters))
  return samples

def DeleteParameterFromJobFile(job_file, parameter):
  """Delete all occurance of parameter from job_file.

  Args:
    job_file: The contents of the fio job file.
    parameter: The parameter to be deleted in job file.

  Returns:
    A string representing a fio job file after removing parameter.
  """
  try:
    return regex_util.Substitute(r'%s=[\w\d_/]+\n' % parameter, '', job_file)
  except regex_util.NoMatchError:
    return job_file
<|endoftext|>


<commit_before>#!/usr/bin/env python

# utils/profdata_merge_worker.py
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

# This script is used to prevent profile data filling up available disk space
# by listening for profile data and merging them into a universal profdata
# file while tests are executing.

import os
import shutil
import pipes
import sys
import socket
import SocketServer
from multiprocessing import Lock, Process, Queue, JoinableQueue
import thread
import subprocess
import tempfile
import argparse

SERVER_ADDRESS = ('localhost', 12400)
TESTS_FINISHED_SENTINEL = "PROFDATA_MERGE_WORKER_TESTS_FINISHED_SENTINEL"
FILE_QUEUE = JoinableQueue()
FILES_MERGED = set()
CONFIG = None

class Config():
    """A class to store configuration information specified by command-line arguments.
    Used to encapsulate what would normally be global variables."""
    def __init__(self, debug, out_dir, no_remove_files):
        self.debug = debug
        self.out_dir = out_dir
        self.tmp_dir = tempfile.mkdtemp()
        self.pid_file_path = os.path.join(self.out_dir, "profdata_merge_worker.pid")
        self.final_profdata_path = os.path.join(self.out_dir, "swift.profdata")
        self.remove_files = not no_remove_files

printlock = Lock()
def printsync(msg):
    if CONFIG and CONFIG.debug:
        with printlock:
            print >>sys.stderr, msg

class ProfdataTCPHandler(SocketServer.StreamRequestHandler):
    def report(self, msg):
        """Convenience method for reporting status from the workers."""
        printsync("\n===== ProfdataTCPHandler =====\n%s\n" % msg)

    def handle(self):
        """Receive a newline-separated list of filenames from a TCP connection
        and add them to the shared merge queue, where the workers will
        execute llvm-profdata merge commands."""
        data = self.rfile.read()
        self.report("received data (length %d): %s" % (len(data), data))

        # Stop once we receive the sentinel
        if data.startswith(TESTS_FINISHED_SENTINEL):
            self.report("received sentinel; killing server...")
            self.finish()
            self.connection.close()
            def kill_server(server):
                server.shutdown()
            # must be killed on another thread, or else deadlock
            thread.start_new_thread(kill_server, (self.server,))
        else:
            # Add all the files to the queue
            for f in data.splitlines():
                f = f.strip()
                if f in FILES_MERGED:
                    return
                FILES_MERGED.add(f)
                FILE_QUEUE.put(f)

class ProfdataMergerProcess(Process):
    def __init__(self):
        Process.__init__(self)
        self.filename_buffer = []
        self.profdata_path = os.path.join(CONFIG.tmp_dir, "%s.profdata" % self.name)
        self.profdata_tmp_path = self.profdata_path + ".copy"

    def report(self, msg):
        """Convenience method for reporting status from the workers."""
        printsync("\n===== %s =====\n%s\n" % (self.name, msg))

    def merge_file_buffer(self):
        """Merge all files in this worker's buffer and clear them.
        This method makes a copy of the working merge progress, then
        calls llvm-cov merge with up to 10 filenames, plus the current
        in-progress merge."""
        if not self.filename_buffer:
            self.report("no files to merge...")
            return
        if os.path.exists(self.profdata_path):
            os.rename(self.profdata_path, self.profdata_tmp_path)
            self.filename_buffer.append(self.profdata_tmp_path)
        cleaned_files = ' '.join(pipes.quote(f) for f in self.filename_buffer)
        # FIXME: This doesn't necessarily always line up with the version
        #        of clang++ used to build the binaries.
        llvm_cmd = ("xcrun llvm-profdata merge -o %s %s"
            % (self.profdata_path, cleaned_files))
        self.report(llvm_cmd)
        ret = subprocess.call(llvm_cmd, shell=True)
        if ret != 0:
            self.report("llvm profdata command failed -- Exited with code %d"
                % ret)
        if CONFIG.remove_files:
            for f in self.filename_buffer:
                if os.path.exists(f):
                    os.remove(f)
        self.filename_buffer = []

    def run(self):
        """Blocks and waits for the file queue, so it can fill its buffer
        and execute merges. If it finds None in the queue, then it knows to
        stop waiting for the queue, merge its current buffer, and kill itself"""
        while True:
            filename = FILE_QUEUE.get()
            self.report("received filename: %s" % filename)
            if filename is None:
                self.report("received sentinel; merging...")
                self.merge_file_buffer()
                FILE_QUEUE.task_done()
                break
            self.filename_buffer.append(filename)
            self.report("Adding %s to filename_buffer." % filename)
            if len(self.filename_buffer) >= 10:
                self.merge_file_buffer()
                FILE_QUEUE.task_done()

def run_server():
    pid = os.getpid()
    if os.path.exists(CONFIG.pid_file_path):
        with open(CONFIG.pid_file_path) as pidfile:
            pid = pidfile.read()
            printsync(("existing process found with pid %s." +
                       "Ensure there are no other test runners running," +
                       "and delete the file at %s")
                       % (pid, CONFIG.pid_file_path))
        return

    with open(CONFIG.pid_file_path, "w") as pidfile:
        pidfile.write(str(pid))

    processes = [ProfdataMergerProcess() for _ in range(10)]
    for p in processes:
        p.start()

    server = SocketServer.TCPServer(SERVER_ADDRESS, ProfdataTCPHandler)
    server.serve_forever()

    for p in processes:
        # force each merge worker to gracefully exit
        FILE_QUEUE.put(None)

    for p in processes:
        printsync("waiting for %s to finish..." % p.name)
        p.join()

    # now that all workers have completed, merge all their files
    merge_final = ProfdataMergerProcess()
    merge_final.profdata_path = CONFIG.final_profdata_path
    for p in processes:
        if os.path.exists(p.profdata_path):
            printsync("merging " + p.profdata_path + "...")
            merge_final.filename_buffer.append(p.profdata_path)
    merge_final.merge_file_buffer()

def stop_server(args):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(SERVER_ADDRESS)
    sock.send(TESTS_FINISHED_SENTINEL)
    sock.close()

def start_server(args):
    global CONFIG
    CONFIG = Config(args.debug, args.output_dir, args.no_remove)
    if not CONFIG.debug:
        pid = os.fork()
        if pid != 0:
            sys.exit(0) # kill the parent process we forked from.
    try:
        run_server()
    finally:
        if os.path.exists(CONFIG.pid_file_path):
            os.remove(CONFIG.pid_file_path)
        if os.path.exists(CONFIG.tmp_dir):
            shutil.rmtree(CONFIG.tmp_dir, ignore_errors=True)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers()

    start = subparsers.add_parser("start")
    start.add_argument("-d", "--debug",
        help="Run in foreground and report status.",
        action="store_true")
    start.add_argument("-o", "--output-dir",
        help="The directory to write the PID file and final profdata file.",
        default="/tmp")
    start.add_argument("--no-remove",
        action="store_true",
        help="Don't remove profraw files after merging them.")
    start.set_defaults(func=start_server)

    stop = subparsers.add_parser("stop")
    stop.set_defaults(func=stop_server)

    args = parser.parse_args()
    args.func(args)
<commit_msg>[coverage] Converted global non-constants to explicitly passed parameters<commit_after>#!/usr/bin/env python

# utils/profdata_merge_worker.py
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

# This script is used to prevent profile data filling up available disk space
# by listening for profile data and merging them into a universal profdata
# file while tests are executing.

import os
import shutil
import pipes
import sys
import socket
import SocketServer
from multiprocessing import Lock, Process, Queue, JoinableQueue
import thread
import subprocess
import tempfile
import argparse

SERVER_ADDRESS = ('localhost', 12400)
TESTS_FINISHED_SENTINEL = "PROFDATA_MERGE_WORKER_TESTS_FINISHED_SENTINEL"

class Config():
    """A class to store configuration information specified by command-line arguments.
    Used to encapsulate what would normally be global variables."""
    def __init__(self, debug, out_dir, no_remove_files):
        self.debug = debug
        self.out_dir = out_dir
        self.tmp_dir = tempfile.mkdtemp()
        self.pid_file_path = os.path.join(self.out_dir, "profdata_merge_worker.pid")
        self.final_profdata_path = os.path.join(self.out_dir, "swift.profdata")
        self.remove_files = not no_remove_files

printlock = Lock()
def printsync(msg, config=None):
    if not config.debug:
        return
    with printlock:
        print >>sys.stderr, msg

class ProfdataTCPHandler(SocketServer.StreamRequestHandler):
    def report(self, msg):
        """Convenience method for reporting status from the workers."""
        printsync("\n===== ProfdataTCPHandler =====\n%s\n" % msg, self.server.config)

    def handle(self):
        """Receive a newline-separated list of filenames from a TCP connection
        and add them to the shared merge queue, where the workers will
        execute llvm-profdata merge commands."""
        data = self.rfile.read()
        self.report("received data (length %d): %s" % (len(data), data))

        # Stop once we receive the sentinel
        if data.startswith(TESTS_FINISHED_SENTINEL):
            self.report("received sentinel; killing server...")
            self.finish()
            self.connection.close()
            def kill_server(server):
                server.shutdown()
            # must be killed on another thread, or else deadlock
            thread.start_new_thread(kill_server, (self.server,))
        else:
            # Add all the files to the queue
            for f in data.splitlines():
                f = f.strip()
                if f in self.server.files_merged:
                    return
                self.server.files_merged.add(f)
                self.server.file_queue.put(f)

class ProfdataServer(SocketServer.TCPServer, object):
    def __init__(self, config, file_queue):
        super(ProfdataServer, self).__init__(SERVER_ADDRESS, ProfdataTCPHandler)
        self.config = config
        self.file_queue = file_queue
        self.files_merged = set()

class ProfdataMergerProcess(Process):
    def __init__(self, config, file_queue):
        Process.__init__(self)
        self.config = config
        self.file_queue = file_queue
        self.filename_buffer = []
        self.profdata_path = os.path.join(config.tmp_dir, "%s.profdata" % self.name)
        self.profdata_tmp_path = self.profdata_path + ".copy"

    def report(self, msg):
        """Convenience method for reporting status from the workers."""
        printsync("\n===== %s =====\n%s\n" % (self.name, msg), self.config)

    def merge_file_buffer(self):
        """Merge all files in this worker's buffer and clear them.
        This method makes a copy of the working merge progress, then
        calls llvm-cov merge with up to 10 filenames, plus the current
        in-progress merge."""
        if not self.filename_buffer:
            self.report("no files to merge...")
            return
        if os.path.exists(self.profdata_path):
            os.rename(self.profdata_path, self.profdata_tmp_path)
            self.filename_buffer.append(self.profdata_tmp_path)
        cleaned_files = ' '.join(pipes.quote(f) for f in self.filename_buffer)
        # FIXME: This doesn't necessarily always line up with the version
        #        of clang++ used to build the binaries.
        llvm_cmd = ("xcrun llvm-profdata merge -o %s %s"
            % (self.profdata_path, cleaned_files))
        self.report(llvm_cmd)
        ret = subprocess.call(llvm_cmd, shell=True)
        if ret != 0:
            self.report("llvm profdata command failed -- Exited with code %d"
                % ret)
        if self.config.remove_files:
            for f in self.filename_buffer:
                if os.path.exists(f):
                    os.remove(f)
        self.filename_buffer = []

    def run(self):
        """Blocks and waits for the file queue, so it can fill its buffer
        and execute merges. If it finds None in the queue, then it knows to
        stop waiting for the queue, merge its current buffer, and kill itself"""
        while True:
            filename = self.file_queue.get()
            self.report("received filename: %s" % filename)
            if filename is None:
                self.report("received sentinel; merging...")
                self.merge_file_buffer()
                self.file_queue.task_done()
                break
            self.filename_buffer.append(filename)
            self.report("Adding %s to filename_buffer." % filename)
            if len(self.filename_buffer) >= 10:
                self.merge_file_buffer()
                self.file_queue.task_done()

def run_server(config):
    pid = os.getpid()
    if os.path.exists(config.pid_file_path):
        with open(config.pid_file_path) as pidfile:
            pid = pidfile.read()
            printsync(("existing process found with pid %s." +
                       "Ensure there are no other test runners running," +
                       "and delete the file at %s")
                       % (pid, config.pid_file_path), config)
        return

    with open(config.pid_file_path, "w") as pidfile:
        pidfile.write(str(pid))

    file_queue = JoinableQueue()

    processes = [ProfdataMergerProcess(config, file_queue) for _ in range(10)]
    for p in processes:
        p.start()

    server = ProfdataServer(config, file_queue)
    server.serve_forever()

    for p in processes:
        # force each merge worker to gracefully exit
        file_queue.put(None)

    for p in processes:
        printsync("waiting for %s to finish..." % p.name, config)
        p.join()

    # now that all workers have completed, merge all their files
    merge_final = ProfdataMergerProcess(config, file_queue)
    merge_final.profdata_path = config.final_profdata_path
    for p in processes:
        if os.path.exists(p.profdata_path):
            printsync("merging " + p.profdata_path + "...", config)
            merge_final.filename_buffer.append(p.profdata_path)
    merge_final.merge_file_buffer()

def stop_server(args):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(SERVER_ADDRESS)
    sock.send(TESTS_FINISHED_SENTINEL)
    sock.close()

def start_server(args):
    config = Config(args.debug, args.output_dir, args.no_remove)
    if not config.debug:
        pid = os.fork()
        if pid != 0:
            sys.exit(0) # kill the parent process we forked from.
    try:
        run_server(config)
    finally:
        if os.path.exists(config.pid_file_path):
            os.remove(config.pid_file_path)
        if os.path.exists(config.tmp_dir):
            shutil.rmtree(config.tmp_dir, ignore_errors=True)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers()

    start = subparsers.add_parser("start")
    start.add_argument("-d", "--debug",
        help="Run in foreground and report status.",
        action="store_true")
    start.add_argument("-o", "--output-dir",
        help="The directory to write the PID file and final profdata file.",
        default="/tmp")
    start.add_argument("--no-remove",
        action="store_true",
        help="Don't remove profraw files after merging them.")
    start.set_defaults(func=start_server)

    stop = subparsers.add_parser("stop")
    stop.set_defaults(func=stop_server)

    args = parser.parse_args()
    args.func(args)
<|endoftext|>