Skip to content

Commit 68df6a8

Browse files
committed
fsutil: add iostat() for collecting iostat on path or device.
iostat() is ported from s2/src s2iostat.py; fsutil.iostat(): read sector size from /sys/block dir; Other changes: - Refine README.md, move out config to standalone file `config.md`.
1 parent 6477f2f commit 68df6a8

File tree

7 files changed

+534
-85
lines changed

7 files changed

+534
-85
lines changed

README.md

+1-84
Original file line numberDiff line numberDiff line change
@@ -111,59 +111,9 @@ jobq.run([0, 1, 2], [add1, printarg])
111111

112112
# Configuration
113113

114-
`pykit` provides a way to setup config for it.
115-
Config module tries to import `pykitconfig` in which a user sets config.
116-
Example:
117-
118-
```
119-
> cat pykitconfig.py
120-
uid = 2
121-
gid = 3
122-
123-
> cat foo.py
124-
from pykit import fsutil
125-
fsutil.write_file('bar', '123') # write_file sets file uid and gid to 2 and 3.
126-
```
127-
128-
## Supported config
129-
130-
- `uid`: specifies default user-id when file created, directory made.
131-
- `gid`: specifies default group-id when file created, directory made.
132-
- `log_dir`: specifies default base_dir when logger created.
133-
- `cat_stat_dir`: specifies default stat_dir for all log cat class instances.
134-
135-
- `zk_node_id`:
136-
specifies a string to identify this host, by default it is `uuid.getnode()`.
137-
138-
`ZKLock` uses it to differentiate what host actually holds a lock.
139-
140-
In `ZKLock`:
141-
142-
- Different host must specify different `node_id`
143-
144-
- But A host could specify different `node_id` for different locks.
145-
146-
- `zk_acl`:
147-
default acl for new created zk-node.
148-
Such as `(('xp', '123', 'cdrwa'), ('foo', 'bar', 'rw'))`
149-
150-
- `zk_auth`:
151-
default auth info for new connection.
152-
Such as `('digest', 'xp', '123')`
153-
154-
- `zk_hosts`:
155-
default comma separated host list for new connection.
156-
Such as `'127.0.0.1:2181,128.0.0.8:2181'`
157-
158-
- `zk_lock_dir`:
159-
default base dir for `ZKLock` to create lock node.
114+
See [config.md](config.md)
160115

161116

162-
163-
164-
165-
See the `README.md` of sub modules for detail.
166-
167117
# Test
168118

169119
Run one of following to test all, a module, a TestCase or a function.
@@ -184,41 +134,8 @@ See [Details](script/README.md)
184134
There are several scripts for developers.
185135
See [script](script).
186136

187-
## Config
188-
189-
`pykit.config` is used internally by `pykit` modules.
190-
191-
### How it works
192-
193-
It try to load `pykitconfig` from a top level module of the project.
194-
195-
Then it load predefined config via ```getattr(pykitconfig, key, default)```.
196-
197-
So module can get config via `config.xxx` by import it.
198-
199-
### How to use
200-
201-
- Add predefined config in config.py
202-
203-
```
204-
uid=_get('uid')
205-
gid=_get('gid')
206-
```
207-
208-
- Import config in you own module
209-
210-
```
211-
from pykit import config
212-
```
213-
214-
- Handle user define config in you code
215137

216-
```
217-
uid=uid or config.uid
218-
gid=gid or config.gid
219-
```
220138

221-
- Update the configuration supported config in this doc
222139

223140
# Author
224141

config.md

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Pykit configuration
2+
3+
`pykit` provides a way to setup config for it.
4+
An end user can config `pykit` behavior without modify source code.
5+
6+
7+
## How it works
8+
9+
Modules those support configuration will load `pykit.config`
10+
and `pykit.config` tries to import `pykitconfig` from `sys.path`.
11+
12+
The `pykit.config` loads attributes defined in `pykitconfig` those it recognizes.
13+
14+
15+
## Usage
16+
17+
18+
### For pykit user
19+
20+
For end user one just creates a file `pykitconfig.py` and adds config into it.
21+
E.g.:
22+
23+
`cat pykitconfig.py`:
24+
25+
```python
26+
import my_project_conf
27+
28+
uid = 2
29+
gid = 3
30+
log_dir = my_project_conf.log_dir
31+
```
32+
33+
And pykit will automatically load `pykitconfig.py` and use them.
34+
35+
```python
36+
from pykit import fsutil
37+
fsutil.write_file('bar', '123') # write_file sets file uid and gid to 2 and 3.
38+
```
39+
40+
### For pykit module developer
41+
42+
- Add predefined config in `config.py`, to specify the field name default
43+
value.
44+
45+
```python
46+
# cat pykit/config.py
47+
uid=_get('uid')
48+
gid=_get('gid')
49+
50+
my_conf = _get('my_conf', "default_value")
51+
```
52+
53+
- Import config in you own module
54+
55+
```python
56+
# cat pykit/mymodule/foo.py
57+
from pykit import config
58+
59+
print config.my_conf # "default_value"
60+
```
61+
62+
## Supported config
63+
64+
- `uid`:
65+
specifies default user-id when file created, directory made.
66+
67+
- `gid`:
68+
specifies default group-id when file created, directory made.
69+
70+
- `log_dir`:
71+
specifies default base_dir when logger created.
72+
73+
- `cat_stat_dir`:
74+
specifies default stat_dir for all log cat class instances.
75+
76+
- `iostat_stat_path`:
77+
specifies the default path to store io stat for `fsutil.iostat`.
78+
79+
- `zk_node_id`:
80+
specifies a string to identify this host, by default it is `uuid.getnode()`.
81+
82+
`ZKLock` uses it to differentiate what host actually holds a lock.
83+
84+
In `ZKLock`:
85+
86+
- Different host must specify different `node_id`
87+
88+
- But A host could specify different `node_id` for different locks.
89+
90+
- `zk_acl`:
91+
default acl for new created zk-node.
92+
Such as `(('xp', '123', 'cdrwa'), ('foo', 'bar', 'rw'))`
93+
94+
- `zk_auth`:
95+
default auth info for new connection.
96+
Such as `('digest', 'xp', '123')`
97+
98+
- `zk_hosts`:
99+
default comma separated host list for new connection.
100+
Such as `'127.0.0.1:2181,128.0.0.8:2181'`
101+
102+
- `zk_lock_dir`:
103+
default base dir for `ZKLock` to create lock node.
104+
105+
- `zk_record_dir`:
106+
specifies zk node path base dir to store **records** for `zktx`.
107+
It must ends with `/`.
108+
109+
- `zk_tx_dir`:
110+
specifies the base dir of zk node path to store tx related info.

config.py

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def _get(key, default=None):
2525
gid = _get('gid')
2626
log_dir = _get('log_dir')
2727
cat_stat_dir = _get('cat_stat_dir')
28+
iostat_stat_path = _get('iostat_stat_path', '/tmp/pykit-iostat')
2829
zk_acl = _get('zk_acl') # (('xp', '123', 'cdrwa'), ('foo', 'bar', 'rw'))
2930
zk_auth = _get('zk_auth') # ('digest', 'xp', '123')
3031
zk_hosts = _get('zk_hosts', '127.0.0.1:2181')

fsutil/README.md

+67
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
- [Stat methods](#stat-methods)
3535
- [fsutil.get_path_inode_usage](#fsutilget_path_inode_usage)
3636
- [fsutil.get_path_usage](#fsutilget_path_usage)
37+
- [fsutil.iostat](#fsutiliostat)
38+
- [Implementation](#implementation)
3739
- [Author](#author)
3840
- [Copyright and License](#copyright-and-license)
3941

@@ -667,6 +669,71 @@ then it can not use the reserved space.
667669
Thus this function provides with the `available` bytes by default.
668670
669671
672+
## fsutil.iostat
673+
674+
**syntax**:
675+
`fsutil.iostat(device=None, path=None, stat_path=None)`
676+
677+
Collect IO stat.
678+
679+
**Synopsis**:
680+
681+
```python
682+
print fsutil.iostat('/dev/sda1') # {'read': 6151, 'write': 34073, 'ioutil': 0}
683+
print fsutil.iostat(path='/') # {'read': 6151, 'write': 34073, 'ioutil': 100}
684+
```
685+
686+
It accepts either `device` or `path` as target to collect IO stat from:
687+
688+
- `device` should be a path starts with `/dev`, such as `/dev/sda1`.
689+
690+
- `path` is any path on a valid mounted fs. If `path` is used and `device` is
691+
`None`, it uses the device on which the `path` is mounted.
692+
693+
One must specify either `device` or `path`.
694+
695+
696+
### Implementation
697+
698+
`/proc/diskstats` provides accumulated IO stat since a host boots up.
699+
Such as total count of read/write operation on a disk.
700+
701+
This function records changes in `/proc/diskstats` and calculates the diff
702+
between two recorded stat as return value.
703+
704+
`fsutil.iostat` reads instant IO stat from `/proc/diskstats` and save it in
705+
`stat_path`. When next time `fsutil.iostat` is called, it calculates the
706+
difference between the current stat from `/proce/diskstats` and the saved stat.
707+
708+
If no previous recorded stat saved in `stat_path`, it waits a second and load
709+
`/proc/diskstats` again, and calculate the diff.
710+
711+
**arguments**:
712+
713+
- `device`:
714+
specifies from which device to collect IO stat.
715+
716+
- `path`:
717+
specifies from which fs path to collect IO stat.
718+
719+
- `stat_path`:
720+
specifies where to store and load IO stat.
721+
722+
By default it is `None`, then it uses `config.iostat_stat_path`(`/tmp/pykit-iostat`) to save
723+
stat.
724+
725+
**return**:
726+
a dict contains 3 field:
727+
```json
728+
{
729+
'read': 6151,
730+
'write': 34073,
731+
'ioutil': 0
732+
}
733+
```
734+
735+
`read` and `write` is in byte/second.
736+
`ioutil` is a percentage number between 0 and 100.
670737
671738
672739
# Author

fsutil/__init__.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
Cat
3131
)
3232

33+
from .iostat import (
34+
DeviceNotFound,
35+
36+
iostat,
37+
)
38+
3339
__all__ = [
3440
"FSUtilError",
3541
"NotMountPoint",
@@ -58,5 +64,9 @@
5864
"LockTimeout",
5965
"NoData",
6066
"NoSuchFile",
61-
"Cat"
67+
"Cat",
68+
69+
"DeviceNotFound",
70+
71+
"iostat",
6272
]

0 commit comments

Comments
 (0)