ADCT Python
Python client API implementation for publishing ADCT data
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Protected Member Functions | Static Protected Attributes | List of all members
adctk.multifile_publisher.MultiFilePublisher Class Reference

This plugin generates writes each message to the configured file, with \<adct-json>\</adct-json> delimiters surrounding it. More...

Inheritance diagram for adctk.multifile_publisher.MultiFilePublisher:
Inheritance graph
[legend]
Collaboration diagram for adctk.multifile_publisher.MultiFilePublisher:
Collaboration graph
[legend]

Public Member Functions

 __init__ (self)
 
int config (self, **kwargs)
 
int initialize (self)
 Set up any resources needed for reuse in publish().
 
int publish (self, builder.Builder b)
 Publish json extracted from builder.
 
None finalize (self)
 Clean up any resources set up in initialize (files, pipes, etc).
 
- Public Member Functions inherited from adctk.publisher.Publisher
str name (self)
 Get the name which a factory or error message should use.
 
str version (self)
 
dict[str, str] get_option_defaults (self)
 
None pause (self)
 Cause publish calls to be ignored until resume is called.
 
None resume (self)
 Cancel an prior calls to pause.
 
int publish_str (self, str json_string)
 
int publish_obj (self, obj)
 

Public Attributes

 defaults
 
 paused
 
 plugin_prefix
 
 debug
 
 fname
 
 fdir
 
 topdir
 
 user
 
 host
 
 pid
 
 rank
 
 abspath
 
 fappend
 
 app_out
 
 mode
 
 state
 
- Public Attributes inherited from adctk.publisher.Publisher
 defaults
 
 plugin_prefix
 
 paused
 
 state
 
 mode
 

Protected Member Functions

 _create_directory_like (self, newdir, dirlike)
 
 _create_directories (self)
 
 _create_stream (self, app)
 
- Protected Member Functions inherited from adctk.publisher.Publisher
str _get (self, dict[str, str] opts, str field)
 
bool _get_bool (self, dict[str, str] opts, str field)
 

Static Protected Attributes

str _name = "multifile"
 
str _version = "1.0.0"
 
- Static Protected Attributes inherited from adctk.publisher.Publisher
str _name = "unknown"
 
str _version = "0.0.0"
 

Additional Inherited Members

- Static Public Member Functions inherited from adctk.publisher.Publisher
tuple[float] get_timeout_defaults ()
 Get the adc server connect and response timeouts defined in the environment ADC_SERVER_CONNECT_TIMEOUT and ADC_SERVER_POST_TIMEOUT as a tuple or default values if not defined.
 
str get_url_default ()
 Get the alleged adc server url base from environment ADC_SERVER_URL or the default if it is not present.
 
str get_port_default ()
 Get the adc server port number to use if it is properly defined in the environment ADC_SERVER_PORT or the default if it is not.
 
- Static Public Attributes inherited from adctk.publisher.Publisher
 API_VERSION = adc_version.AdcVersion()._dict()
 

Detailed Description

This plugin generates writes each message to the configured file, with \<adct-json>\</adct-json> delimiters surrounding it.

The output directory is "." by default, but may be overriden with a full path defined in env("ADC_MULTIFILE_PLUGIN_DIRECTORY"). The resulting mass of files can be reduced independently later by concatenating all files in the tree or more selectively with a single call to the consolidate_multifile_logs() function.

Multiple independent multifile publishers may be created; exact filenames are not user controlled, to avoid collisions. Likewise there is no append mode.

DIR/$user/[$adc_wfid.]H_$host.P$pid.T$start.p$publisherptr/$application.R$rank.XXXXXX

Files opened will remain opened until the publisher is finalized.

Debug output (to stderr) is enabled if env("ADC_MULTIFILE_PLUGIN_DEBUG") is a number other than 0.

Environment Variables

Constructor & Destructor Documentation

◆ __init__()

adctk.multifile_publisher.MultiFilePublisher.__init__ (   self)

Reimplemented from adctk.publisher.Publisher.

54 def __init__(self):
55 super().__init__()
56 self.defaults = _defaults
57 self.paused = False
58 self.plugin_prefix = "ADC_MULTIFILE_PLUGIN_"
59 self.debug = 0
60 self.fname = None
61 self.fdir = None # composed from user, host, time, pid, wfid, selfpointer
62 self.topdir = None # from DIRECTORY env
63 self.user = getpass.getuser()
64 self.host = socket.gethostname()
65 self.pid = str(os.getpid())
66 self.rank = None
67 self.abspath = None
68 self.fappend = False
69 self.app_out = {}
70 self.mode = None
71

Member Function Documentation

◆ _create_directories()

adctk.multifile_publisher.MultiFilePublisher._create_directories (   self)
protected
124 def _create_directories(self):
125 # return if fdir exists already
126 fdir = pathlib.Path( self.fdir)
127 if fdir.exists():
128 if fdir.is_dir():
129 return
130 raise NotADirectoryError(f"{fdir}: cannot replace file of the same name")
131 # if dirname(fdir) exists already, make last subdir
132 bdir = fdir.parent
133 if bdir.exists():
134 if bdir.is_dir():
135 self._create_directory_like(fdir, bdir)
136 return
137 raise NotADirectoryError(f"{bdir}: cannot make subdirectory in a file")
138 # if topdir/user/ exists already, create rest w/perm, sticky from user/
139 du_path = pathlib.Path(self.topdir)
140 du_path = du_path / self.user
141 if du_path.exists():
142 if du_path.is_dir():
143 self._create_directory_like(self.fdir, du_path)
144 return
145 raise NotADirectoryError(f"{du_path}: cannot replace file of the same name")
146 # create whole tree at self.topdir
147 pathlib.Path(self.topdir).mkdir(mode=0o1750)
148 self._create_directory_like(du_path, self.topdir)
149 self._create_directory_like(fdir, self.topdir)
150

◆ _create_directory_like()

adctk.multifile_publisher.MultiFilePublisher._create_directory_like (   self,
  newdir,
  dirlike 
)
protected
Create a directory and use dirlik perms. parent of newdir must exist
117 def _create_directory_like(self, newdir, dirlike):
118 """Create a directory and use dirlik perms. parent of newdir must exist"""
119 likestat = os.stat(dirlike)
120 likemode = stat.S_IMODE(likestat.st_mode)
121 os.mkdir(newdir)
122 os.chmod(newdir, likemode)
123

◆ _create_stream()

adctk.multifile_publisher.MultiFilePublisher._create_stream (   self,
  app 
)
protected
allocate file or None in app_out table. If allocated, truncated opened ug+rw
151 def _create_stream(self, app):
152 """allocate file or None in app_out table. If allocated, truncated opened ug+rw"""
153 if app in self.app_out:
154 return
155 try:
156 (fd, n) = tempfile.mkstemp(dir=self.fdir, prefix=f"{app}.R{self.rank}.", text=False)
157 os.close(fd)
158 self.app_out[app] = open(n, "wb")
159 cperm = os.stat(n).st_mode
160 os.chmod(n, cperm | stat.S_IRGRP | stat.S_IWGRP )
161 except IOError as e:
162 self.app_out[app] = None
163

◆ config()

int adctk.multifile_publisher.MultiFilePublisher.config (   self,
**  kwargs 
)
The config options for the file plugin are directory=<directory>,
   file=<leafname>, append="true" and debug=<int>.
   If not specified, then the environment variables ADC_MULTIFILE_PLUGIN_*
   are checked, where * is DIRECTORY, FILE, and DEBUG.

Reimplemented from adctk.publisher.Publisher.

72 def config(self, **kwargs) -> int:
73 """The config options for the file plugin are directory=<directory>,
74 file=<leafname>, append="true" and debug=<int>.
75 If not specified, then the environment variables ADC_MULTIFILE_PLUGIN_*
76 are checked, where * is DIRECTORY, FILE, and DEBUG.
77 """
78 try:
79 self.topdir = self._get(kwargs, "DIRECTORY")
80 self.debug = int( self._get(kwargs, "DEBUG") )
81 self.rank = self._get(kwargs, "RANK")
82 if self.rank:
83 try:
84 irank = int(self.rank)
85 except ValueError:
86 indirect = os.getenv(self.rank)
87 if indirect:
88 try:
89 irank = int(indirect)
90 self.rank = str(irank)
91 except ValueError:
92 log.logger.error("adctk.Publisher.get_timeout_defaults: unable to "\
93 "convert %s to int from %s%s",
94 self.rank, self.plugin_prefix,"RANK")
95
96 wfid = os.getenv("ADC_WFID")
97 if wfid:
98 wname = "wfid_" + wfid + "."
99 else:
100 wname = ""
101 t = time.monotonic_ns()
102 sec = str(t // 1000000000)
103 nsec = str(t % 1000000000)
104 start = ".".join((sec, nsec))
105 self.fdir = (self.topdir + "/" + self.user + "/" + wname +
106 "H_" + self.host +".P" + self.pid + ".T" + start +
107 ".p" + hex(id(self)) )
108 if self.debug > 0:
109 log.logger.info("multifile plugin configured")
110 self.mode = publisher.Publisher.Mode.INIT
111 return 0
112 except: # fixme: catch more specific exceptions here instead of global
113 log.logger.error("adctk.MultiFilePublisher.config: invalid options or "+
114 "environment ADC_MULTIFILE_PLUGIN_* values")
115 return 1
116

◆ finalize()

None adctk.multifile_publisher.MultiFilePublisher.finalize (   self)

Clean up any resources set up in initialize (files, pipes, etc).

Reimplemented from adctk.publisher.Publisher.

238 def finalize(self) -> None:
239 self.state = publisher.Publisher.State.OK
240 self.paused = False
241 self.mode = publisher.Publisher.Mode.CONFIG
242 for app, f in self.app_out.items():
243 if f:
244 f.close()
245 self.app_out[app] = None
246 self.app_out = {}

◆ initialize()

int adctk.multifile_publisher.MultiFilePublisher.initialize (   self)

Set up any resources needed for reuse in publish().

Reimplemented from adctk.publisher.Publisher.

164 def initialize(self) -> int:
165 # pylint: disable=too-many-return-statements
166 if self.state == publisher.Publisher.State.ERR:
167 return errno.ENOTRECOVERABLE
168 if self.mode == publisher.Publisher.Mode.CONFIG:
169 self.config()
170 if self.mode != publisher.Publisher.Mode.INIT:
171 return errno.EINVAL
172 if self.state == publisher.Publisher.State.ERR:
173 log.logger.error("adctk.MultiFilePublisher.initialize: unexpected 'state' found.")
174 return errno.ENOTRECOVERABLE
175 checkdir = pathlib.Path(self.fdir)
176 dirmade = False
177 try:
178 self._create_directories()
179 dirmade = True
180 except NotADirectoryError:
181 log.logger.error("adctk.MultiFilePublisher.initialize: An existing "\
182 "file cannot be converted to a directory in path %s", self.fdir)
183 self.state = publisher.Publisher.State.ERR
184 return errno.EEXIST
185 except PermissionError:
186 log.logger.error("adctk.MultiFilePublisher.initialize: permission denied "\
187 "along path %s", self.fdir)
188 self.state = publisher.Publisher.State.ERR
189 return errno.EPERM
190 except OSError as e:
191 log.logger.error("adctk.MultiFilePublisher.initialize: unexpected OSError")
192 self.state = publisher.Publisher.State.ERR
193 return errno.EIO
194 if not dirmade:
195 self.state = publisher.Publisher.State.ERR
196 return errno.EINVAL
197 try:
198 # make sure we can write into target location
199 with tempfile.NamedTemporaryFile(dir=self.fdir, prefix=".test",
200 mode="w+b", encoding="utf-8") as f:
201 f.write("test")
202 self.mode = publisher.Publisher.Mode.PUB_OR_FINAL
203 return 0
204 except (FileNotFoundError, PermissionError, IsADirectoryError, IOError, OSError) as e:
205 log.logger.error("adctk.MultiFilePublisher.initialize: unable to open file in %s : %s",
206 self.fdir, e)
207 self.state = publisher.Publisher.State.ERR
208 return errno.EBADF
209

◆ publish()

int adctk.multifile_publisher.MultiFilePublisher.publish (   self,
builder.Builder  b 
)

Publish json extracted from builder.

Reimplemented from adctk.publisher.Publisher.

210 def publish(self, b: builder.Builder) -> int:
211 if self.paused:
212 return 0
213 if self.state != publisher.Publisher.State.OK:
214 return 1
215 if self.mode != publisher.Publisher.Mode.PUB_OR_FINAL:
216 return 2
217 app = b.get_value_string("/header/application")
218 if not app:
219 log.logger.error("adctk.MultiFilePublisher.publish: cannot publish "\
220 "without /header/application")
221
222 log.logger.info("adctk.MultiFilePublisher.publish: received %s", b.serialize())
223 log.logger.info("adctk.MultiFilePublisher.publish: has sections %s",
224 b.get_section_names())
225 return 1
226 self._create_stream(app)
227 try:
228 message = f"<adct-json>{b.serialize()}</adct-json>\n"
229 if self.app_out[app]:
230 self.app_out[app].write(message.encode('utf-8'))
231 except (FileNotFoundError, PermissionError, IsADirectoryError, IOError, OSError):
232 self.state = publisher.Publisher.State.ERR
233 log.logger.error("adctk.MultiFilePublisher.publish: write failed to %s", self.app_out[app].name)
234 self.app_out[app].close()
235 return 1
236 return 0
237

Member Data Documentation

◆ _name

str adctk.multifile_publisher.MultiFilePublisher._name = "multifile"
staticprotected

◆ _version

str adctk.multifile_publisher.MultiFilePublisher._version = "1.0.0"
staticprotected

◆ abspath

adctk.multifile_publisher.MultiFilePublisher.abspath

◆ app_out

adctk.multifile_publisher.MultiFilePublisher.app_out

◆ debug

adctk.multifile_publisher.MultiFilePublisher.debug

◆ defaults

adctk.multifile_publisher.MultiFilePublisher.defaults

◆ fappend

adctk.multifile_publisher.MultiFilePublisher.fappend

◆ fdir

adctk.multifile_publisher.MultiFilePublisher.fdir

◆ fname

adctk.multifile_publisher.MultiFilePublisher.fname

◆ host

adctk.multifile_publisher.MultiFilePublisher.host

◆ mode

adctk.multifile_publisher.MultiFilePublisher.mode

◆ paused

adctk.multifile_publisher.MultiFilePublisher.paused

◆ pid

adctk.multifile_publisher.MultiFilePublisher.pid

◆ plugin_prefix

adctk.multifile_publisher.MultiFilePublisher.plugin_prefix

◆ rank

adctk.multifile_publisher.MultiFilePublisher.rank

◆ state

adctk.multifile_publisher.MultiFilePublisher.state

◆ topdir

adctk.multifile_publisher.MultiFilePublisher.topdir

◆ user

adctk.multifile_publisher.MultiFilePublisher.user

The documentation for this class was generated from the following file: