def ensure_protect(protect_lines): """Require that 'p4 protect' table includes grant of admin to git-fusion-user. """ with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protects', '-m', '-u', p4gf_const.P4GF_USER) if p4gf_p4msg.find_msgid(p4, p4gf_p4msgid.MsgDm_ProtectsEmpty): report(INFO, "Protect table empty. Setting....") report(DEBUG, 'p4 protects -mu git-fusion-user\n{}'.format(r)) perm = p4gf_util.first_value_for_key(r, KEY_PERM_MAX) if perm and perm in ['admin', 'super']: report(INFO, ("Protect table already grants 'admin'" + " to user {}. Not changing").format(p4gf_const.P4GF_USER)) return False l = protect_lines if p4gf_version.p4d_version_supports_admin_user(p4): perm = 'admin' else: perm = 'super' l.append('{perm} user {user} * //...'.format(perm=perm, user=p4gf_const.P4GF_USER)) p4gf_util.set_spec(p4, 'protect', values={KEY_PROTECTIONS: l}) report( INFO, "Protect table modified. {} granted admin permission.".format( p4gf_const.P4GF_USER)) return True
def _upgrade_p4gf(p4): """Perform upgrade from earlier versions of P4GF. This should be invoked using _maybe_perform_init() to avoid race conditions across hosts. """ # If updating from 12.2 to 13.1 we need to create global config file # (this does nothing if file already exists) c = p4gf_config.create_file_global(p4) if c: _info( _("Global config file '{}' created.").format( p4gf_config.depot_path_global())) else: _info( _("Global config file '{}' already exists.").format( p4gf_config.depot_path_global())) # Ensure the time zone name has been set, else default to something sensible. r = p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) tzname = p4gf_util.first_value_for_key(r, 'value') if tzname == "0" or tzname is None: msg = _("Counter '{}' not set, using UTC as default." \ " Change this to your Perforce server's time zone.") \ .format(p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) LOG.warn(msg) sys.stderr.write(NTR('Git Fusion: {}\n').format(msg)) tzname = None else: # Sanity check the time zone name. try: pytz.timezone(tzname) except pytz.exceptions.UnknownTimeZoneError: LOG.warn("Time zone name '{}' unrecognized, using UTC as default". format(tzname)) tzname = None if tzname is None: p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME, 'UTC')
def release(self): """Release a previously acquired hold on the shared lock. Does nothing if the lock is not currently held by this instance. """ if not self.__has: return counter_name = host_view_lock_name(self.__host_name, self.__view_name) shared_name = shared_host_view_name(self.__host_name, self.__view_name) with CounterLock(self.__p4, counter_name): current = p4gf_util.first_value_for_key( self.__p4.run('counter', '-u', shared_name), 'value') if current and current != '0': # Remove our PID from the list of shared lock holders. value = self._lock_value() lines = current.splitlines() lines = [line for line in lines if line != value] value = '\n'.join(lines) if value: self.__p4.run('counter', '-u', shared_name, value) else: if self.__last_release_func: self.__last_release_func() try: self.__p4.run('counter', '-u', '-d', shared_name) except P4Exception: LOG.warn("lock counter deletion failed: {}".format(shared_name)) self.__has = False
def repo_from_template_client( p4, view_name, view_name_p4client, client_name , client_root, enable_mismatched_rhs): ''' Create a new Perforce client spec <client_name> using existing Perforce client spec <view_name> as a template (just use its View). ''' # view_name_p4client is the p4client # view_name is the gfinternal repo name # view_name differs from view_name_p4client if latter contains special chars # or was configured with --p4client argument if not p4gf_util.spec_exists(p4, 'client', view_name_p4client): return INIT_REPO_NOVIEW client = p4.run('client', '-o', view_name_p4client)[0] if 'Stream' in client: return _repo_from_stream(p4, view_name, client.get('Stream'), client_name, client_root, enable_mismatched_rhs) with Validator.from_template_client(view_name, p4, view_name_p4client) as validator: if not validator.is_valid(enable_mismatched_rhs): return INIT_REPO_CONFIG_FILE_BAD # Seed a new client using the view's view as a template. LOG.info("Git Fusion client %s does not exist," " creating from existing Perforce client %s" , client_name, view_name_p4client) view = p4gf_util.first_value_for_key( p4.run('client', '-o', '-t', view_name_p4client, client_name), 'View') create_repo_client(p4, view_name, client_name, client_root, view, None) return INIT_REPO_OK
def _fetch(ctx): """If the Perforce server has a 'p4 typemap' configured, return it as a P4.Map instance. To accomodate mappings of multiple right-hand patterns to the same <type> append the <rhs> to the left hand <type>. Mapping will be unique, but require stripping the appended <rhs> postfix. If not, return None. """ r = ctx.p4gfrun('typemap', '-o') raw_lines = p4gf_util.first_value_for_key(r, "TypeMap") if not raw_lines: return None typem = P4.Map() for mapline in raw_lines: lhs, sep, rhs = mapline.partition( ' ') # the P4 typemap types are delimited by the first ' ' # if <rhs> does not start with '/', # insert one when constructing new <lhs> = <type>/<rhs> # We dont care that '"' are embedded in the constructed new <lhs> # We do need to ensure that the <type> is followed immediately by a '/' # So we may append the <type> with the <rhs> as <type><rhs> or <type>/<rhs> # and <rhs> may itself start with '/' or '"' or neither. # Our regex lookup will detect on the first '/' sep = '' # concatenate <lhs><sep><rhs> if not rhs.startswith('/'): # if there is no '/' .. then add '/' sep = '/' lhs = lhs + sep + rhs typem.insert(lhs, rhs) return typem
def for_user_and_view(cls, p4, p4user, view_name): """Factory to fetch user's permissions on a view.""" LOG.debug("for_user_and_view() {u} {v}".format(u=p4user, v=view_name)) group_list = p4.run('groups', '-i', p4user) group_dict = {group['group']:group for group in group_list} LOG.debug("group_dict.keys()={}".format(group_dict.keys())) vp = ViewPerm() vp.p4user_name = p4user vp.view_name = view_name vp.view_pull = p4gf_const.P4GF_GROUP_VIEW_PULL.format(view=view_name) in group_dict vp.view_push = p4gf_const.P4GF_GROUP_VIEW_PUSH.format(view=view_name) in group_dict vp.global_pull = p4gf_const.P4GF_GROUP_PULL in group_dict vp.global_push = p4gf_const.P4GF_GROUP_PUSH in group_dict value = p4gf_util.first_value_for_key( p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PERMISSION_GROUP_DEFAULT), 'value') if value == '0': value = DEFAULT_PERM vp.default_pull = value == PERM_PULL vp.default_push = value == PERM_PUSH LOG.debug("counter={}".format(value)) LOG.debug(vp) return vp
def _toggle_filetype(self, p4path, isx): """Returns the new file type for the named file, switching the executable state based on the isx value. Args: p4path: Path of the file to modify. isx: True if currently executable. Returns: New type for the file; may be None. """ p4type = None if isx: p4type = '+x' else: # To remove a previously assigned modifier, the whole filetype # must be specified. for tipe in ['headType', 'type']: # For a file that was executable, is being renamed (with # edits), and is no longer executable, we need to handle the # fact that it's not yet in Perforce and so does not have a # headType. try: p4type = p4gf_util.first_value_for_key( self.ctx.p4.run(['fstat', '-T' + tipe, p4path]), tipe) except P4.P4Exception: pass if p4type: p4type = p4gf_p4filetype.remove_mod(p4type, 'x') return p4type
def for_user_and_view(cls, p4, p4user, view_name, required_perm): """Factory to fetch user's permissions on a view.""" LOG.debug("for_user_and_view() {u} {v} {r}".format(u=p4user, v=view_name, r=required_perm)) group_list = p4.run('groups', '-i', p4user) group_dict = {group['group']: group for group in group_list} LOG.debug3("group_dict.keys()={}".format(group_dict.keys())) vp = ViewPerm() vp.p4user_name = p4user vp.view_name = view_name vp.view_pull = p4gf_const.P4GF_GROUP_VIEW_PULL.format( view=view_name) in group_dict vp.view_push = p4gf_const.P4GF_GROUP_VIEW_PUSH.format( view=view_name) in group_dict vp.global_pull = p4gf_const.P4GF_GROUP_PULL in group_dict vp.global_push = p4gf_const.P4GF_GROUP_PUSH in group_dict value = p4gf_util.first_value_for_key( p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PERMISSION_GROUP_DEFAULT), 'value') if value == '0': value = DEFAULT_PERM vp.default_pull = value == PERM_PULL vp.default_push = value == PERM_PUSH LOG.debug("counter={}".format(value)) LOG.debug(vp) return vp
def ensure_protect(protect_lines): """Require that 'p4 protect' table includes grant of admin to git-fusion-user. """ with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protects', '-m', '-u', p4gf_const.P4GF_USER) if p4gf_p4msg.find_msgid(p4, p4gf_p4msgid.MsgDm_ProtectsEmpty): report(INFO, "Protect table empty. Setting....") report(DEBUG, 'p4 protects -mu git-fusion-user\n{}'.format(r)) perm = p4gf_util.first_value_for_key(r, KEY_PERM_MAX) if perm and perm in ['admin', 'super']: report(INFO, ( "Protect table already grants 'admin'" + " to user {}. Not changing").format(p4gf_const.P4GF_USER)) return False l = protect_lines if p4gf_version.p4d_version_supports_admin_user(p4): perm = 'admin' else: perm = 'super' l.append('{perm} user {user} * //...'.format(perm=perm, user=p4gf_const.P4GF_USER)) p4gf_util.set_spec(p4, 'protect', values={KEY_PROTECTIONS : l}) report(INFO, "Protect table modified. {} granted admin permission." .format(p4gf_const.P4GF_USER)) return True
def _upgrade_p4gf(p4): """Perform upgrade from earlier versions of P4GF. This should be invoked using _maybe_perform_init() to avoid race conditions across hosts. """ # If updating from 12.2 to 13.1 we need to create global config file # (this does nothing if file already exists) c = p4gf_config.create_file_global(p4) if c: _info(_("Global config file '{}' created.") .format(p4gf_config.depot_path_global())) else: _info(_("Global config file '{}' already exists.") .format(p4gf_config.depot_path_global())) # Ensure the time zone name has been set, else default to something sensible. r = p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) tzname = p4gf_util.first_value_for_key(r, 'value') if tzname == "0" or tzname is None: msg = _("Counter '{}' not set, using UTC as default." \ " Change this to your Perforce server's time zone.") \ .format(p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) LOG.warn(msg) sys.stderr.write(NTR('Git Fusion: {}\n').format(msg)) tzname = None else: # Sanity check the time zone name. try: pytz.timezone(tzname) except pytz.exceptions.UnknownTimeZoneError: LOG.warn("Time zone name '{}' unrecognized, using UTC as default".format(tzname)) tzname = None if tzname is None: p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME, 'UTC')
def __get_timezone_offset(self, timestamp): """ Determine the time zone offset for the given timestamp, using the. time zone name set in the P4GF time zone counter. For example, with time zone 'US/Pacific' and timestamp 1371663968, the offset returned will be the string '-0700'. """ try: ts = int(timestamp) except ValueError: LOG.error("__get_timezone_offset() given non-numeric input {}".format(timestamp)) if self.__tzname is None: r = self.ctx.p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) value = p4gf_util.first_value_for_key(r, 'value') if value == '0' or value is None: # Upgrade from an EA system, perhaps, in which the upgrade counters # have been set but the later changes where not applied during init. msg = _("Counter '{}' not set, using UTC as default." " Change this to your Perforce server's time zone.") \ .format(p4gf_const.P4GF_COUNTER_TIME_ZONE_NAME) LOG.warn(msg) value = NTR('UTC') self.__tzname = value try: mytz = pytz.timezone(self.__tzname) except pytz.exceptions.UnknownTimeZoneError: LOG.warn("Time zone name '{}' unrecognized, using UTC as default".format(self.__tzname)) mytz = pytz.utc LOG.debug("__get_timezone_offset({}) with {}".format(ts, mytz)) dt = datetime.datetime.fromtimestamp(ts, tz=pytz.utc) ct = dt.astimezone(mytz) return ct.strftime('%z')
def ensure_protect(protect_lines): """Require that 'p4 protect' table includes grant of admin to git-fusion-user. And review to git-fusion-reviews-* """ with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protects', '-m', '-u', p4gf_const.P4GF_USER) if p4gf_p4msg.find_msgid(p4, p4gf_p4msgid.MsgDm_ProtectsEmpty): Verbosity.report(Verbosity.INFO, _("Protect table empty. Setting....")) l = None gfuser_perms_set = False reviews_users_perms_set = False Verbosity.report(Verbosity.DEBUG, NTR('p4 protects -mu git-fusion-user\n{}').format(r)) perm = p4gf_util.first_value_for_key(r, KEY_PERM_MAX) if perm and perm in ['admin', 'super']: Verbosity.report(Verbosity.INFO, _("Protect table already grants 'admin' to user '{user}'. Not changing") .format(user=p4gf_const.P4GF_USER)) else: l = protect_lines l.append('admin user {user} * //...'.format(user=p4gf_const.P4GF_USER)) gfuser_perms_set = True review_perm = 'review user git-fusion-reviews-* * //...' review_perm_user_mask = 'review user git-fusion-reviews-*' review_perm_exists = False for perm in protect_lines: if perm.startswith(review_perm_user_mask): # Do not insert a newline into this line even # though it is long. Makes it too hard to test # in p4gf_super_init.t Verbosity.report( Verbosity.INFO, _("Protect table already grants 'review' to users 'git-fusion-reviews-*'." " Not changing")) review_perm_exists = True break if not review_perm_exists: if not l: l = protect_lines l.append(review_perm) reviews_users_perms_set = True if l: p4gf_p4spec.set_spec(p4, 'protect', values={KEY_PROTECTIONS: l}) if gfuser_perms_set: Verbosity.report( Verbosity.INFO, _("Protect table modified. User '{user}' granted admin permission.") .format(user=p4gf_const.P4GF_USER)) if reviews_users_perms_set: Verbosity.report( Verbosity.INFO, _("Protect table modified. git-fusion-reviews-* granted reviews permission."))
def get_last_change(p4): """Fetch the last change number with which we synced the keys to the SSH configuration file. Returns a positive number, or zero if no saved counter value. """ counter = p4.run('counter', '-u', _get_counter_name()) if counter: return int(p4gf_util.first_value_for_key(counter, 'value')) else: return 0
def set_proxy_protects_key(): """Get server's dm.proxy_protects and store as a key for Git Fusion.""" v = p4gf_util.first_value_for_key( p4.run('configure', 'show', CONFIGURABLE_PROXY_PROTECTS), KEY_VALUE) v = 'false' if v == '0' else 'true' P4Key.set(p4, p4gf_const.P4GF_P4KEY_PROXY_PROTECTS, v) Verbosity.report( Verbosity.INFO, _("Configurable '{configurable}' is set to {value}.") .format(configurable=CONFIGURABLE_PROXY_PROTECTS, value=v))
def _prevent_access(p4): """ Prevent further access to Git Fusion while deleting everything. Return the previous value of the counter so it can be restored later. """ result = p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PREVENT_NEW_SESSIONS) old_value = None if result: old_value = p4gf_util.first_value_for_key(result, 'value') p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PREVENT_NEW_SESSIONS, 'true') return old_value
def get_heartbeat(self): ''' Return the current heartbeat value, if any. Might provide clue to what holds the lock. ''' result = self.__p4__.run('counter', '-u', self.heartbeat_counter_name()) value = p4gf_util.first_value_for_key(result, 'value') if not value or value == '0': return None return value
def get_heartbeat(self): ''' Return the current heartbeat value, if any. Might provide clue to what holds the lock. ''' value = p4gf_util.first_value_for_key( self.__p4__.run('counter', '-u', self.heartbeat_counter_name()), 'value') if not value or value == '0': return None return value
def create_p4_client(p4, view_name, client_name, client_root): """Create the p4 client to contain Git meta-data mirror. Keyword arguments: p4 -- Perforce client API view_name -- client view of repository to clone client_name -- client that will be created client_root -- path for client workspace Returns one of the INIT_REPO_* constants. """ # Ensure the client root directory has been created. if not os.path.exists(client_root): os.makedirs(client_root) # If a client for this view already exists, we're probably done. # # Make sure the client root is correct. if p4gf_util.spec_exists(p4, 'client', client_name): LOG.debug("%s client already exists for %s", client_name, view_name) p4gf_util.ensure_spec_values(p4, 'client', client_name, {'Root':client_root}) return INIT_REPO_EXISTS # Client does not yet exist. We'll have to configure it manually, using # the view's name as a template. if not p4gf_util.spec_exists(p4, 'client', view_name): LOG.warn("requested client %s does not exist, required for creating a mirror", view_name) sys.stderr.write("View {} does not exist\n".format(view_name)) return INIT_REPO_NOVIEW # Seed a new client using the view's view as a template. LOG.info("client %s does not exist, creating from view %s", client_name, view_name) view = p4gf_util.first_value_for_key( p4.run('client', '-o', '-t', view_name, client_name), 'View') if not view_depots_ok(p4, view_name, view): # nature of problem already reported return INIT_REPO_BADVIEW desc = ("Created by Perforce Git Fusion for work in {view}." .format(view=view_name)) p4gf_util.set_spec(p4, 'client', spec_id=client_name, values={'Owner' : p4gf_const.P4GF_USER, 'LineEnd' : 'unix', 'View' : view, 'Root' : client_root, 'Host' : None, 'Description' : desc}) LOG.debug("successfully created client %s", client_name) return INIT_REPO_OK
def get_keys_latest_change(p4): """Retrieve the most recent change made to the user keys in Perforce. Returns a postive number, or zero if no changes have been made to the keys (i.e. no keys have been added). """ try: change = p4.run('changes', '-m', '1', KEYS_PATH) if change: return int(p4gf_util.first_value_for_key(change, 'change')) else: return 0 except P4Exception: return 0
def fetch_triggers(): """Return trigger table as a list of lines.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('triggers', '-o') if p4.errors: Verbosity.report(Verbosity.ERROR, _("Unable to run 'p4 triggers -o'.")) for e in p4.errors: Verbosity.report(Verbosity.ERROR, e) sys.exit(2) triggers = p4gf_util.first_value_for_key(r, KEY_TRIGGERS) LOG.debug('retrieved triggers: %s', triggers) return triggers
def fetch_triggers(): """Return trigger table as a list of lines.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('triggers','-o') if p4.errors: Verbosity.report(Verbosity.ERROR, _("Unable to run 'p4 triggers -o'.")) for e in p4.errors: Verbosity.report(Verbosity.ERROR, e) sys.exit(2) triggers = p4gf_util.first_value_for_key(r, KEY_TRIGGERS) return triggers
def fetch_protect(): """Return protect table as a list of protect lines.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protect', '-o') Verbosity.report(Verbosity.DEBUG, NTR('p4 protect:\n{result}').format(result=r)) if p4.errors: Verbosity.report(Verbosity.ERROR, _("Unable to run 'p4 protect -o'.")) for e in p4.errors: Verbosity.report(Verbosity.ERROR, e) sys.exit(2) protections = p4gf_util.first_value_for_key(r, KEY_PROTECTIONS) LOG.debug('retrieved protections: %s', protections) return protections
def _clear_heartbeat(self): ''' Clear the heartbeat counter associated with this lock. ''' # Clearing the heartbeat means we start all over with heartbeat tracking. self.__heartbeat_time = None self.__heartbeat_content = None # Suppress P4 exceptions here. # Don't care if fail, only lock counter matters. with self.__p4__.at_exception_level(P4.RAISE_NONE): counter_name = self.heartbeat_counter_name() result = self.__p4__.run('counter', '-u', '-d', counter_name) if p4gf_util.first_value_for_key(result, 'counter'): LOG.getChild("heartbeat").debug("deleted counter {}".format(counter_name))
def fetch_protect(): """Return protect table as a list of protect lines.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protect','-o') Verbosity.report(Verbosity.DEBUG, NTR('p4 protect:\n{}').format(r)) if p4.errors: Verbosity.report(Verbosity.ERROR, _("Unable to run 'p4 protect -o'.")) for e in p4.errors: Verbosity.report(Verbosity.ERROR, e) sys.exit(2) protections = p4gf_util.first_value_for_key(r, KEY_PROTECTIONS) return protections
def ensure_protects_configurable(): """Grant 'p4 protects -u' permission to admin users.""" v = p4gf_util.first_value_for_key( p4.run('configure', 'show', CONFIGURABLE_ALLOW_ADMIN), KEY_VALUE) if v == '1': Verbosity.report(Verbosity.INFO, _("Configurable '{}' already set to 1. Not setting.") .format(CONFIGURABLE_ALLOW_ADMIN)) return False p4.run('configure', 'set', '{}=1'.format(CONFIGURABLE_ALLOW_ADMIN)) Verbosity.report(Verbosity.INFO, _("Configurable '{}' set to 1.") .format(CONFIGURABLE_ALLOW_ADMIN)) return True
def get_branch_dict(self): """Get a branch dictionary for this repo. If the p4gf_config exists, use that. Else if the p4 client exists create a branch dict containing a branch from the client views. Else return None """ LOG.debug("get_branch_dict for {0}".format(self.view_name)) # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(self.view_name) config_exists = p4gf_util.depot_file_exists(self.p4, config_path) if config_exists: self.config = p4gf_config.get_repo(self.p4, self.view_name) if self.config: return p4gf_branch.dict_from_config(self.config, self.p4) else: return None else: LOG.debug("checking if client {0} exists.".format(self.view_name)) if not p4gf_util.spec_exists(self.p4, 'client', self.view_name): LOG.debug(" client {0} NOT exists.".format( self.view_name)) return None view_lines = p4gf_util.first_value_for_key( self.p4.run('client', '-o', '-t', self.view_name, self.p4client), 'View') if not view_lines: return None else: # create a Branch object to manage this client view if isinstance(view_lines, str): view_lines = view_lines.splitlines() LOG.debug( "create branch from client views: {0}".format(view_lines)) branch = p4gf_branch.Branch() branch.branch_id = 'master' branch.git_branch_name = 'master' branch.view_p4map = P4.Map(view_lines) branch.view_lines = view_lines LOG.debug( "create branch from client branch view_p4map: {0}".format( branch.view_p4map)) LOG.debug( "create branch from client branch view_lines: {0}".format( branch.view_lines)) branch_dict = {} branch_dict[branch.branch_id] = branch return branch_dict
def fetch_protect(): """Return protect table as a list of protect lines.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protect', '-o') report(DEBUG, 'p4 protect:\n{}'.format(r)) if p4.errors: report(ERROR, "Unable to run 'p4 protect -o'.") for e in p4.errors: report(ERROR, e) exit(2) protections = p4gf_util.first_value_for_key(r, KEY_PROTECTIONS) return protections
def canceled(self): ''' Has our lock counter been cleared? This is one way to remote-kill a long-running Git Fusion task. ''' value = p4gf_util.first_value_for_key( self.__p4__.run('counter', '-u', self.counter_name()), 'value') if value != "0": # Compare as strings, "0" != int(0) return False LOG.error("Lock canceled: {name}={value}".format( name=self.counter_name(), value=value)) return True
def create_default_perm(p4, perm=DEFAULT_PERM): """Create the 'stick all users into this pull/push permission group' default counter. If counter already exists with non-zero value, leave it unchanged. """ counter = p4gf_util.first_value_for_key( p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PERMISSION_GROUP_DEFAULT), 'value') if counter != None and counter != '0': # Somebody already set it. return False p4.run('counter', '-u', p4gf_const.P4GF_COUNTER_PERMISSION_GROUP_DEFAULT, perm) return True
def _acquire_attempt(self): """Attempt an atomic increment. If the result is 1, then we now own the lock. Any other value means somebody else owns the lock. """ value = p4gf_util.first_value_for_key( self.__p4__.run('counter', '-u', '-i', self.counter_name()), 'value') acquired = value == "1" # Compare as strings, "1" != int(1) LOG.debug("_acquire_attempt {name} pid={pid} acquired={a} value={value}" .format(pid=os.getpid(), a=acquired, name=self.counter_name(), value=value)) return acquired
def ensure_protects_configurable(): """Grant 'p4 protects -u' permission to admin users.""" v = p4gf_util.first_value_for_key( p4.run('configure', 'show', CONFIGURABLE_ALLOW_ADMIN), KEY_VALUE) if v == '1': Verbosity.report( Verbosity.INFO, _("Configurable '{configurable}' already set to 1. Not setting.") .format(configurable=CONFIGURABLE_ALLOW_ADMIN)) return False p4.run('configure', 'set', '{}=1'.format(CONFIGURABLE_ALLOW_ADMIN)) Verbosity.report(Verbosity.INFO, _("Configurable '{configurable}' set to 1.") .format(configurable=CONFIGURABLE_ALLOW_ADMIN)) return True
def canceled(self): ''' Has our lock counter been cleared? This is one way to remote-kill a long-running Git Fusion task. ''' value = p4gf_util.first_value_for_key( self.__p4__.run('counter', '-u', self.counter_name()), 'value') if value != "0": # Compare as strings, "0" != int(0) return False LOG.error("Lock canceled: {name}={value}" .format(name=self.counter_name(), value=value)) return True
def get_auth_method(): """Return the configured auth method perforce or ldap.""" with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('configure', 'show', 'auth.default.method') if p4.errors: Verbosity.report(Verbosity.ERROR, _("Unable to run 'p4 configure show auth.default.method'.")) for e in p4.errors: Verbosity.report(Verbosity.ERROR, e) sys.exit(2) auth_method = None if len(r): auth_method = p4gf_util.first_value_for_key(r, 'Value') LOG.debug('auth.default.method = %s', auth_method) return auth_method
def get_branch_dict(self): """Get a branch dictionary for this repo. If the p4gf_config exists, use that. Else if the p4 client exists create a branch dict containing a branch from the client views. Else return None """ LOG.debug("get_branch_dict for {0}".format(self.view_name)) # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(self.view_name) config_exists = p4gf_util.depot_file_exists(self.p4, config_path) if config_exists: self.config = p4gf_config.get_repo(self.p4, self.view_name) if self.config: return p4gf_branch.dict_from_config(self.config, self.p4) else: return None else: LOG.debug("checking if client {0} exists.".format(self.view_name)) if not p4gf_util.spec_exists(self.p4, 'client', self.view_name): LOG.debug(" client {0} NOT exists.".format(self.view_name)) return None view_lines = p4gf_util.first_value_for_key( self.p4.run('client', '-o', '-t', self.view_name, self.p4client), 'View') if not view_lines: return None else: # create a Branch object to manage this client view if isinstance(view_lines, str): view_lines = view_lines.splitlines() LOG.debug("create branch from client views: {0}".format(view_lines)) branch = p4gf_branch.Branch() branch.branch_id = 'master' branch.git_branch_name = 'master' branch.view_p4map = P4.Map(view_lines) branch.view_lines = view_lines LOG.debug("create branch from client branch view_p4map: {0}". format(branch.view_p4map)) LOG.debug("create branch from client branch view_lines: {0}". format(branch.view_lines)) branch_dict = {} branch_dict[branch.branch_id] = branch return branch_dict
def ensure_protects_configurable(): """Grant 'p4 protects -u' permission to admin users.""" if not p4gf_version.p4d_version_supports_admin_user(p4): return v = p4gf_util.first_value_for_key( p4.run('configure', 'show', CONFIGURABLE_ALLOW_ADMIN), KEY_VALUE) if v == '1': report( INFO, 'Configurable {} already set to 1. Not setting.'.format( CONFIGURABLE_ALLOW_ADMIN)) return False p4.run('configure', 'set', '{}=1'.format(CONFIGURABLE_ALLOW_ADMIN)) report(INFO, 'Configurable {} set to 1.'.format(CONFIGURABLE_ALLOW_ADMIN)) return True
def ensure_protects_configurable(): """Grant 'p4 protects -u' permission to admin users.""" if not p4gf_version.p4d_version_supports_admin_user(p4): return v = p4gf_util.first_value_for_key( p4.run('configure', 'show', CONFIGURABLE_ALLOW_ADMIN), KEY_VALUE) if v == '1': report(INFO, 'Configurable {} already set to 1. Not setting.' .format(CONFIGURABLE_ALLOW_ADMIN)) return False p4.run('configure', 'set', '{}=1'.format(CONFIGURABLE_ALLOW_ADMIN)) report(INFO, 'Configurable {} set to 1.' .format(CONFIGURABLE_ALLOW_ADMIN)) return True
def acquire(self): """Attempt to acquire a shared lock. """ counter_name = host_view_lock_name(self.__host_name, self.__view_name) counter_lock = CounterLock(self.__p4, counter_name, self.__timeout_secs) shared_name = shared_host_view_name(self.__host_name, self.__view_name) start_time = time.time() while not self.__has: with counter_lock: # Get the current value for the shared lock. current = p4gf_util.first_value_for_key( self.__p4.run('counter', '-u', shared_name), 'value') if current and current[0] == '*': # Exclusive lock in place, check if process still alive (pid, args) = current.split('\t', 1) pid = pid.strip('*') if not check_process_alive(pid, args): # Seems the lock holder has died try: self.__p4.run('counter', '-u', '-d', shared_name) except P4Exception: LOG.warn("lock counter deletion failed: {}".format(shared_name)) continue if current is None or current == '0' or current[0] != '*': # Add our PID to the list of shared lock holders. value = self._lock_value() if current and current != '0': value = current + '\n' + value self.__p4.run('counter', '-u', shared_name, value) self.__has = True # If current was empty, then we're the first acquisition. if (current is None or current == '0') \ and self.__first_acquire_func: self.__first_acquire_func() if not self.__has: elapsed = time.time() - start_time if self.__timeout_secs and self.__timeout_secs <= elapsed: msg = _('Unable to acquire lock: {}').format(shared_name) if current: msg += _('\nLock holder: {}').format(current) raise RuntimeError(msg) # Having released the lock and not acquired the shared lock, # pause briefly before trying again. time.sleep(_RETRY_PERIOD)
def acquire(self): """Attempt to acquire a shared lock. """ counter_name = host_view_lock_name(self.__host_name, self.__view_name) counter_lock = CounterLock(self.__p4, counter_name, self.__timeout_secs) shared_name = shared_host_view_name(self.__host_name, self.__view_name) start_time = time.time() while not self.__has: with counter_lock: # Get the current value for the lock. current = p4gf_util.first_value_for_key( self.__p4.run('counter', '-u', shared_name), 'value') if current is None or current == '0': # Set the value to our PID wrapped in asterisks to signal # that it is an exclusive lock. value = self._lock_value() self.__p4.run('counter', '-u', shared_name, value) self.__has = True else: # Shared locks in place, check if processes still alive okay_to_steal = True for row in current.splitlines(): (pid, args) = row.split('\t', 1) if check_process_alive(pid, args): okay_to_steal = False break if okay_to_steal: # Seems the lock holders have all died try: self.__p4.run('counter', '-u', '-d', shared_name) except P4Exception: LOG.warn("lock counter deletion failed: {}".format(shared_name)) continue if not self.__has: elapsed = time.time() - start_time if self.__timeout_secs and self.__timeout_secs <= elapsed: msg = _('Unable to acquire lock: {}').format(shared_name) if current: msg += _('\nLock holder: {}').format(current) raise RuntimeError(msg) # Having released the lock and not acquired the shared lock, # pause briefly before trying again. time.sleep(_RETRY_PERIOD)
def release(self): """Release a previously acquired hold on the shared lock. Does nothing if the lock is not currently held by this instance. """ if not self.__has: return counter_name = host_view_lock_name(self.__host_name, self.__view_name) shared_name = shared_host_view_name(self.__host_name, self.__view_name) with CounterLock(self.__p4, counter_name): current = p4gf_util.first_value_for_key( self.__p4.run('counter', '-u', shared_name), 'value') expected = self._lock_value() if current == expected: # Delete the exclusive lock try: self.__p4.run('counter', '-u', '-d', shared_name) except P4Exception: LOG.warn("lock counter deletion failed: {}".format(shared_name)) else: LOG.warn("Lock {0} does not belong to us, not releasing".format(shared_name)) self.__has = False
def get_shared_host_view_lock_state(p4, host_name, view_name): '''Does any process already hold this shared host+view lock? Returns None if none do EXCLUSIVE if one process holds an exclusive lock SHARED if one or more processes hold shared locks Returns None if there is a stale lock suitable for stealing, but does not actually steal the lock. ''' shared_name = shared_host_view_name(host_name, view_name) # Get the current value for the shared lock. current = p4gf_util.first_value_for_key( p4.run('counter', '-u', shared_name), 'value') if (not current) or (current == '0'): return None if current[0] == '*': # Exclusive lock in place, check if process still alive. (pid, args) = current.split('\t', 1) pid = pid.strip('*') if not check_process_alive(pid, args): # Seems the lock holder has died. return None else: return EXCLUSIVE # Shared lock(s) in place, check if process(es) still alive. okay_to_steal = True for row in current.splitlines(): (pid, args) = row.split('\t', 1) if check_process_alive(pid, args): okay_to_steal = False break if okay_to_steal: # Seems the lock holders have all died. return None return SHARED
def ensure_protect(protect_lines): """Require that 'p4 protect' table includes grant of admin to git-fusion-user. And review to git-fusion-reviews-* """ with p4.at_exception_level(p4.RAISE_NONE): r = p4.run('protects', '-m', '-u', p4gf_const.P4GF_USER) if p4gf_p4msg.find_msgid(p4, p4gf_p4msgid.MsgDm_ProtectsEmpty): Verbosity.report(Verbosity.INFO, _("Protect table empty. Setting....")) l = None Verbosity.report(Verbosity.DEBUG, NTR('p4 protects -mu git-fusion-user\n{}').format(r)) perm = p4gf_util.first_value_for_key(r, KEY_PERM_MAX) if perm and perm in ['admin', 'super']: Verbosity.report(Verbosity.INFO, _("Protect table already grants 'admin' to user '{}'. Not changing") .format(p4gf_const.P4GF_USER)) else: l = protect_lines l.append('admin user {user} * //...'.format(user=p4gf_const.P4GF_USER)) review_perm = 'review user git-fusion-reviews-* * //...' if review_perm in protect_lines: # Do not insert a newline into this line even # though it is long. Makes it too hard to test # in p4gf_super_init.t Verbosity.report(Verbosity.INFO, _("Protect table already grants 'review' to users 'git-fusion-reviews-*'." "Not changing")) else: if not l: l = protect_lines l.append(review_perm) if l: p4gf_util.set_spec(p4, 'protect', values={KEY_PROTECTIONS : l}) Verbosity.report(Verbosity.INFO, _("Protect table modified. User '{}' granted admin permission.") .format(p4gf_const.P4GF_USER))
def repo_from_template_client(p4, view_name, view_name_p4client, client_name, client_root, enable_mismatched_rhs): ''' Create a new Perforce client spec <client_name> using existing Perforce client spec <view_name> as a template (just use its View). ''' # view_name_p4client is the p4client # view_name is the gfinternal repo name # view_name differs from view_name_p4client if latter contains special chars # or was configured with --p4client argument if not p4gf_util.spec_exists(p4, 'client', view_name_p4client): return INIT_REPO_NOVIEW client = p4.run('client', '-o', view_name_p4client)[0] if 'Stream' in client: return _repo_from_stream(p4, view_name, client.get('Stream'), client_name, client_root, enable_mismatched_rhs) with Validator.from_template_client(view_name, p4, view_name_p4client) as validator: if not validator.is_valid(enable_mismatched_rhs): return INIT_REPO_CONFIG_FILE_BAD # Seed a new client using the view's view as a template. LOG.info( "Git Fusion client %s does not exist," " creating from existing Perforce client %s", client_name, view_name_p4client) view = p4gf_util.first_value_for_key( p4.run('client', '-o', '-t', view_name_p4client, client_name), 'View') create_repo_client(p4, view_name, client_name, client_root, view, None) return INIT_REPO_OK
def get_timezone(self): """get server's timezone via p4 info""" server_date = p4gf_util.first_value_for_key(self.p4.run("info"), 'serverDate') self.timezone = server_date.split(" ")[2]
def get_timezone_serverversion(self): """get server's timezone and server version via p4 info""" r = self.p4.run_info() server_date = p4gf_util.first_value_for_key(r, 'serverDate') self.timezone = server_date.split(" ")[2] self.server_version = p4gf_util.first_value_for_key(r, 'serverVersion')