1 """Functionality common to all account classes.
3 Each subclass of Account must provide five methods: create() and
4 destroy(), which are static; configure(), start(), and stop(), which
5 are not. configure(), which takes a record as its only argument, does
6 things like set up ssh keys. In addition, an Account subclass must
7 provide static member variables SHELL, which contains the unique shell
8 that it uses; and TYPE, a string that is used by the account creation
9 code. For no particular reason, TYPE is divided hierarchically by
10 periods; at the moment the only convention is that all sliver accounts
11 have type that begins with sliver.
18 from grp import getgrnam
25 # When this variable is true, start after any ensure_created
27 # shell path -> account class association
29 # account type -> account class association
32 # these semaphores are acquired before creating/destroying an account
33 create_sem = threading.Semaphore(1)
34 destroy_sem = threading.Semaphore(1)
36 def register_class(acct_class):
37 """Call once for each account class. This method adds the class to the dictionaries used to look up account classes by shell and type."""
38 shell_acct_class[acct_class.SHELL] = acct_class
39 type_acct_class[acct_class.TYPE] = acct_class
42 # private account name -> worker object association and associated lock
43 name_worker_lock = threading.Lock()
44 # dict of account_name: <Worker Object>
48 return [pw_ent for pw_ent in pwd.getpwall() if pw_ent[6] in shell_acct_class]
51 """Return the names of all accounts on the system with recognized shells."""
52 return [pw_ent[0] for pw_ent in allpwents()]
55 """Return the worker object for a particular username. If no such object exists, create it first."""
56 name_worker_lock.acquire()
58 if name not in name_worker:
59 logger.verbose("Accounts:get(%s) new Worker" % name)
60 name_worker[name] = Worker(name)
61 return name_worker[name]
62 finally: name_worker_lock.release()
66 def __init__(self, rec):
67 logger.verbose('Initing account %s'%rec['name'])
68 self.name = rec['name']
70 self.initscriptchanged = False
74 def create(name, vref = None): abstract
76 def destroy(name): abstract
78 def configure(self, rec):
79 """Write <rec['keys']> to my authorized_keys file."""
80 logger.verbose('%s: in accounts:configure'%self.name)
81 new_keys = rec['keys']
82 if new_keys != self.keys:
84 dot_ssh = '/home/%s/.ssh' % self.name
85 if not os.access(dot_ssh, os.F_OK): os.mkdir(dot_ssh)
86 os.chmod(dot_ssh, 0700)
87 tools.write_file(dot_ssh + '/authorized_keys', lambda f: f.write(new_keys))
88 logger.log('%s: installing ssh keys' % self.name)
89 user = pwd.getpwnam(self.name)[2]
90 group = getgrnam("slices")[2]
91 os.chown(dot_ssh, user, group)
92 os.chown(dot_ssh + '/authorized_keys', user, group)
94 def start(self, delay=0): pass
96 def is_running(self): pass
99 def __init__(self, name):
100 self.name = name # username
101 self._acct = None # the account object currently associated with this worker
103 def ensure_created(self, rec, startingup = Startingup):
104 """Check account type is still valid. If not, recreate sliver. If still valid,
105 check if running and configure/start if not."""
106 curr_class = self._get_class()
107 next_class = type_acct_class[rec['type']]
108 if next_class != curr_class:
109 self._destroy(curr_class)
111 try: next_class.create(self.name, rec['vref'])
112 finally: create_sem.release()
113 if not isinstance(self._acct, next_class): self._acct = next_class(rec)
115 not self.is_running() or \
116 next_class != curr_class or \
117 self._acct.initscriptchanged:
119 else: self._acct.configure(rec)
121 def ensure_destroyed(self): self._destroy(self._get_class())
123 def start(self, rec, d = 0):
124 self._acct.configure(rec)
125 self._acct.start(delay=d)
127 def stop(self): self._acct.stop()
129 def is_running(self):
130 if self._acct.is_running():
134 logger.verbose("Worker(%s): is not running" % self.name)
137 def _destroy(self, curr_class):
140 destroy_sem.acquire()
141 try: curr_class.destroy(self.name)
142 finally: destroy_sem.release()
144 def _get_class(self):
145 try: shell = pwd.getpwnam(self.name)[6]
146 except KeyError: return None
147 return shell_acct_class[shell]