1 """Functionality common to all account classes.
3 Each subclass of Account must provide 6 methods: create() and
4 destroy(), which are static; configure(), is_running(), start(), and stop(),
5 which are not. configure() takes a record as its only argument, and 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()
47 return [pw_ent for pw_ent in pwd.getpwall() if pw_ent[6] in shell_acct_class]
50 """Return the names of all accounts on the system with recognized shells."""
51 return [pw_ent[0] for pw_ent in allpwents()]
54 """Return the worker object for a particular username. If no such object exists, create it first."""
55 name_worker_lock.acquire()
57 if name not in name_worker: name_worker[name] = Worker(name)
58 return name_worker[name]
59 finally: name_worker_lock.release()
63 def __init__(self, rec):
64 logger.verbose('Initing account %s'%rec['name'])
65 self.name = rec['name']
67 self.initscriptchanged = False
71 def create(name, vref = None): abstract
73 def destroy(name): abstract
75 def configure(self, rec):
76 """Write <rec['keys']> to my authorized_keys file."""
77 logger.verbose('%s: in accounts:configure'%self.name)
78 new_keys = rec['keys']
79 if new_keys != self.keys:
81 dot_ssh = '/home/%s/.ssh' % self.name
82 if not os.access(dot_ssh, os.F_OK): os.mkdir(dot_ssh)
83 os.chmod(dot_ssh, 0700)
84 tools.write_file(dot_ssh + '/authorized_keys', lambda f: f.write(new_keys))
85 logger.log('%s: installing ssh keys' % self.name)
86 user = pwd.getpwnam(self.name)[2]
87 group = getgrnam("slices")[2]
88 os.chown(dot_ssh, user, group)
89 os.chown(dot_ssh + '/authorized_keys', user, group)
91 def start(self, delay=0): pass
93 def is_running(self): pass
96 def __init__(self, name):
97 self.name = name # username
98 self._acct = None # the account object currently associated with this worker
100 def ensure_created(self, rec, startingup = Startingup):
101 """Check account type is still valid. If not, recreate sliver. If still valid,
102 check if running and configure/start if not."""
103 curr_class = self._get_class()
104 next_class = type_acct_class[rec['type']]
105 if next_class != curr_class:
106 self._destroy(curr_class)
108 try: next_class.create(self.name, rec['vref'])
109 finally: create_sem.release()
110 if not isinstance(self._acct, next_class): self._acct = next_class(rec)
112 not self.is_running() or \
113 next_class != curr_class or \
114 self._acct.initscriptchanged:
116 else: self._acct.configure(rec)
118 def ensure_destroyed(self): self._destroy(self._get_class())
120 def start(self, rec, d = 0):
121 self._acct.configure(rec)
122 self._acct.start(delay=d)
124 def stop(self): self._acct.stop()
126 def is_running(self):
127 if (self._acct != None) and self._acct.is_running():
131 logger.verbose("Worker(%s): is not running" % self.name)
134 def _destroy(self, curr_class):
137 destroy_sem.acquire()
138 try: curr_class.destroy(self.name)
139 finally: destroy_sem.release()
141 def _get_class(self):
142 try: shell = pwd.getpwnam(self.name)[6]
143 except KeyError: return None
144 return shell_acct_class[shell]