Asterisk и не только. Виртуальные файловые системы. Шаг назад или два вперед?
Описывая участие в проекте по модернизации VoIP оператора связи Часть 1 и Часть 2, одной из задач, которая выпала из поля зрения, было создание унифицированного инструмента для визуализации и мониторинга работы сервера Asterisk. По сути, после выхода из данного проекта, навязчивая идея привести отображение информации Asterisk к более удобному виду вылилась в проект создания прототипа унифицированной виртуальной файловой системы, объединяющей возможности всех разрозненных инструментов доступных в Asterisk.
Думаю что многие из администраторов, которые имели дело с Asterisk, зачастую удивлялись тому количеству различных команд, при помощи которых из Asterisk можно получать данные. Речь пойдёт об учётных записях для абонентских устройств, пользователях для аутентификации, каналах, а также о нестандартном применении виртуальных файловых систем.
Остановимся подробнее на различных методах получения данных.
Первый самый распространённый.
asterisk-macomnet*CLI> sip show peers
Name/username Host Dyn Forcerport Comedia ACL Port Status Description Realtime
6001 (Unspecified) D Auto (No) No 0 Unmonitored
person/person (Unspecified) D Auto (No) No 0 UNKNOWN
3 sip peers [Monitored: 0 online, 1 offline Unmonitored: 1 online, 1 offline]
asterisk-macomnet*CLI> sip show peer 6001
* Name : 6001
Description :
Realtime peer: No
Secret :
MD5Secret :
Remote Secret:
Context : web
Record On feature : automon
Record Off feature : automon
Subscr.Cont. :
Language : ru
Tonezone :
AMA flags : Unknown
Transfer mode: open
CallingPres : Presentation Allowed, Not Screened
Callgroup :
Pickupgroup :
Named Callgr :
Nam. Pickupgr:
MOH Suggest :
Mailbox :
VM Extension : asterisk
LastMsgsSent : 0/0
Call limit : 0
Max forwards : 0
Dynamic : Yes
Callerid : "6001" <6001>
MaxCallBR : 384 kbps
Expire : -1
Insecure : no
Force rport : Auto (No)
Symmetric RTP: No
ACL : No
DirectMedACL : No
T.38 support : No
T.38 EC mode : Unknown
T.38 MaxDtgrm: 4294967295
DirectMedia : No
PromiscRedir : No
User=Phone : No
Video Support: No
Text Support : No
Ign SDP ver : No
Trust RPID : No
Send RPID : No
Path support : No
Path : N/A
TrustIDOutbnd: Legacy
Subscriptions: Yes
Overlap dial : No
DTMFmode : rfc2833
Timer T1 : 500
Timer B : 32000
ToHost :
Addr->IP : (null)
Defaddr->IP : (null)
Prim.Transp. : UDP
Allowed.Trsp : UDP,WS
Def. Username:
SIP Options : (none)
Codecs : (gsm|g729|ulaw|alaw)
Auto-Framing : No
Status : Unmonitored
Useragent :
Reg. Contact :
Qualify Freq : 60000 ms
Keepalive : 0 ms
Sess-Timers : Accept
Sess-Refresh : uas
Sess-Expires : 1800 secs
Min-Sess : 90 secs
RTP Engine : asterisk
Parkinglot :
Use Reason : No
Encryption : Yes
asterisk-macomnet*CLI> sip show users
Username Secret Accountcode Def.Context ACL Forcerport
6001 MegaPass12345 web No No
person E346fz8Vam users_context No No
asterisk-macomnet*CLI> sip show user 6001
* Name : 6001
Secret :
MD5Secret :
Context : web
Language : ru
AMA flags : Unknown
Tonezone :
Transfer mode: open
MaxCallBR : 384 kbps
CallingPres : Presentation Allowed, Not Screened
Call limit : 0
Callgroup :
Pickupgroup :
Named Callgr :
Nam. Pickupgr:
Callerid : "6001" <6001>
ACL : No
Sess-Timers : Accept
Sess-Refresh : uas
Sess-Expires : 1800 secs
Sess-Min-SE : 90 secs
RTP Engine : asterisk
Auto-Framing: No
Не правда ли знакомые команды? И многие ими пользуются почти ежедневно. Ни для кого наверное не является секретом, что в Asterisk есть дерево объектов. Доступ к нему предоставляется через провайдеров. Информация в дереве полнее чем мы видели раньше.
asterisk-macomnet*CLI> data show providers
/asterisk/channel/iax2/peers (get) [chan_iax2.c]
/asterisk/channel/iax2/users (get) [chan_iax2.c]
/asterisk/channel/dahdi/version (get) [chan_dahdi.c]
/asterisk/channel/dahdi/status (get) [chan_dahdi.c]
/asterisk/channel/dahdi/channels (get) [chan_dahdi.c]
/asterisk/channel/sip/peers (get) [chan_sip.c]
/asterisk/core/hints (get) [pbx.c]
/asterisk/core/channels (get) [channel.c]
/asterisk/core/channeltypes (get) [channel.c]
/asterisk/application/queue/list (get) [app_queue.c]
Доступ к дереву объектов, позволяет иметь полную информацию о каждом из них.
asterisk-macomnet*CLI> data get /asterisk/channel/sip/peers
peers
peer
vmexten: "asterisk"
is_realtime: False
amaflags
text: "Unknown"
value: 0
transports: "UDP,WS"
inuse: 0
timer_t1: 500
callingpres
text: "Presentation Allowed, Not Screened"
value: 0
unsolicited_mailbox: ""
host_dynamic: True
subscribecontext: ""
useragent: ""
rtptimeout: 0
cid_num: "6001"
mohinterpret: "default"
parkinglot: ""
mwi_from: ""
call_limit: 0
fullcontact: ""
rtpholdtimeout: 0
qualifyfreq: 60000
maxms: 0
rtpkeepalive: 0
allowtransfer
text: "open"
value: 0
regexten: ""
onhold: 0
ringing: 0
fromdomain: ""
timer_b: 32000
fromuser: ""
username: ""
secret: "MegaPass12345"
accountcode: ""
t38_maxdatagram: -1
remotesecret: ""
md5secret: ""
maxcallbitrate: 384
mohsuggest: ""
lastms: 0
sipoptions
sec_agree: False
histinfo: False
join: False
resource-priority: False
precondition: False
eventlist: False
outbound: False
recipient-list-invite: False
early-session: False
recipient-list-subscribe: False
sdp-anat: False
100rel: False
replaces: False
from-change: False
replace: False
gruu: False
tdialog: False
privacy: False
norefersub: False
timer: False
path: False
pref: False
tohost: ""
autoframing: False
name: "6001"
cid_name: "6001"
language: "ru"
context: "web"
description: ""
type: "friend"
engine: "asterisk"
codecs
codec
frame_length: 33
samplespersecond: 8000
name: "gsm"
description: "GSM"
codec
frame_length: 10
samplespersecond: 8000
name: "g729"
description: "G.729A"
codec
frame_length: 80
samplespersecond: 8000
name: "ulaw"
description: "G.711 u-law"
codec
frame_length: 80
samplespersecond: 8000
name: "alaw"
description: "G.711 a-law"
peer
vmexten: "asterisk"
is_realtime: False
amaflags
text: "Unknown"
value: 0
transports: "UDP,WS"
inuse: 0
timer_t1: 500
callingpres
text: "Presentation Allowed, Not Screened"
value: 0
unsolicited_mailbox: ""
host_dynamic: True
subscribecontext: ""
useragent: ""
rtptimeout: 0
cid_num: "person"
mohinterpret: "default"
parkinglot: ""
mwi_from: ""
call_limit: 0
fullcontact: ""
rtpholdtimeout: 0
qualifyfreq: 60000
maxms: 2000
rtpkeepalive: 0
allowtransfer
text: "open"
value: 0
regexten: ""
onhold: 0
ringing: 0
fromdomain: ""
timer_b: 32000
fromuser: ""
username: "person"
secret: "E346fz8Vam"
accountcode: ""
t38_maxdatagram: -1
remotesecret: ""
md5secret: ""
maxcallbitrate: 384
mohsuggest: ""
lastms: 0
sipoptions
sec_agree: False
histinfo: False
join: False
resource-priority: False
precondition: False
eventlist: False
outbound: False
recipient-list-invite: False
early-session: False
recipient-list-subscribe: False
sdp-anat: False
100rel: False
replaces: False
from-change: False
replace: False
gruu: False
tdialog: False
privacy: False
norefersub: False
timer: False
path: False
pref: False
tohost: ""
autoframing: False
name: "person"
cid_name: "person"
language: "en"
context: "users_context"
description: ""
type: "friend"
engine: "asterisk"
codecs
codec
frame_length: 33
samplespersecond: 8000
name: "gsm"
description: "GSM"
codec
frame_length: 10
samplespersecond: 8000
name: "g729"
description: "G.729A"
codec
frame_length: 80
samplespersecond: 8000
name: "ulaw"
description: "G.711 u-law"
codec
frame_length: 80
samplespersecond: 8000
name: "alaw"
description: "G.711 a-law"
Наверное мало кто знает, из-за отсутствия нормальной документации на команду «data get», но в ней есть поиск и группировка полей.
asterisk-macomnet*CLI> data get /asterisk/channel/sip/peers peers/peer/name=6001
или
asterisk-macomnet*CLI> data get /asterisk/channel/sip/peers peers/peer/secret=MegaPass12345
peers
peer
vmexten: "asterisk"
is_realtime: False
amaflags
text: "Unknown"
value: 0
transports: "UDP,WS"
inuse: 0
timer_t1: 500
callingpres
text: "Presentation Allowed, Not Screened"
value: 0
unsolicited_mailbox: ""
host_dynamic: True
subscribecontext: ""
useragent: ""
rtptimeout: 0
cid_num: "6001"
mohinterpret: "default"
parkinglot: ""
mwi_from: ""
call_limit: 0
fullcontact: ""
rtpholdtimeout: 0
qualifyfreq: 60000
maxms: 0
rtpkeepalive: 0
allowtransfer
text: "open"
value: 0
regexten: ""
onhold: 0
ringing: 0
fromdomain: ""
timer_b: 32000
fromuser: ""
username: ""
secret: "MegaPass12345"
accountcode: ""
t38_maxdatagram: -1
remotesecret: ""
md5secret: ""
maxcallbitrate: 384
mohsuggest: ""
lastms: 0
sipoptions
sec_agree: False
histinfo: False
join: False
resource-priority: False
precondition: False
eventlist: False
outbound: False
recipient-list-invite: False
early-session: False
recipient-list-subscribe: False
sdp-anat: False
100rel: False
replaces: False
from-change: False
replace: False
gruu: False
tdialog: False
privacy: False
norefersub: False
timer: False
path: False
pref: False
tohost: ""
autoframing: False
name: "6001"
cid_name: "6001"
language: "ru"
context: "web"
description: ""
type: "friend"
engine: "asterisk"
codecs
codec
frame_length: 33
samplespersecond: 8000
name: "gsm"
description: "GSM"
codec
frame_length: 10
samplespersecond: 8000
name: "g729"
description: "G.729A"
codec
frame_length: 80
samplespersecond: 8000
name: "ulaw"
description: "G.711 u-law"
codec
frame_length: 80
samplespersecond: 8000
name: "alaw"
description: "G.711 a-law"
Запрос данных из дерева поддерживает фильтрацию параметров. При этом параметр поиска должен иметь значение »1=1», любое другое условие поиска описанное выше игнорируется автоматически.
asterisk-macomnet*CLI> data get /asterisk/channel/sip/peers 1=1 peers/peer/cid_num,peers/peer/name,peers/peer/username,peers/peer/secret,peers/peer/lastms,peers/peer/fullcontact
peers
peer
cid_num: "6001"
fullcontact: "sip:6001@192.168.200.247:5060"
username: ""
secret: "MegaPass12345"
lastms: 0
name: "6001"
peer
cid_num: "person"
fullcontact: ""
username: "person"
secret: "E346fz8Vam"
lastms: 0
name: "person"
Доступ к дереву это очень удобно, но дерево по умолчанию содержит только записи жёстко прописанные в конфигурационных файлах либо записи кешированных realtime подключений. Для получения полноценного дерева необходимо загрузить из базы данных все параметры всех realtime подключений.
Отображение данных realtime пиров производится командой:
asterisk-macomnet*CLI> sip show peer load
Usage: sip show peer [load]
Shows all details on one SIP peer and the current status.
Option "load" forces lookup of peer in realtime storage.
К сожалению для того, чтобы загрузить данные, нужно знать что загружать.
asterisk-macomnet*CLI> realtime load sippeers name zhecka
Usage: realtime load
Prints out a list of variables using the RealTime driver.
You must supply a family name, a column to match on, and a value to match to.
Как мы здесь видим, для загрузки данных realtime подключения необходимо также знать что грузить. Но есть «грязный хак». Параметры «colmatch» и «value» передаются в sql провайдер «как есть». Поэтому можно сделать запрос вида «realtime load sippeers 1 1» и получить все данные за один запрос.
asterisk-macomnet*CLI> realtime load sippeers 1 1
Column Name Column Value
-------------------- --------------------
dtlssetup actpass
id 1325
name zhecka
ipaddr
port 0
regseconds 0
defaultuser zhecka
fullcontact
regserver
useragent
lastms 0
host dynamic
type friend
context web
permit
deny
secret zhecka
md5secret
remotesecret
transport udp,tcp
dtmfmode
directmedia no
nat
callgroup
pickupgroup
language
disallow
allow
insecure
trustrpid
progressinband
promiscredir
useclientcode
accountcode
setvar
callerid
amaflags
callcounter
busylevel
allowoverlap
allowsubscribe
videosupport
maxcallbitrate
rfc2833compensate
mailbox
session-timers
session-expires
session-minse
session-refresher
t38pt_usertpsource
regexten
fromdomain
fromuser
qualify
defaultip
rtptimeout
rtpholdtimeout
sendrpid
outboundproxy
callbackextension
timert1
timerb
qualifyfreq
constantssrc
contactpermit
contactdeny
usereqphone
textsupport
faxdetect
buggymwi
auth
fullname
trunkname
cid_number
callingpres
mohinterpret
mohsuggest
parkinglot
hasvoicemail
subscribemwi
vmexten
autoframing
rtpkeepalive
call-limit
g726nonstandard
ignoresdpversion
allowtransfer
dynamic
path
supportpath
encryption no
avpf yes
force_avp yes
icesupport yes
dtlsenable yes
dtlsverify fingerprint
dtlscertfile /usr/local/share/asterisk/keys/cert.crt
dtlsprivatekey /usr/local/share/asterisk/keys/private.pem
dtlscafile /usr/local/share/asterisk/keys/ca.crt
dtlssetup actpass
id 1327
name zhecka1
ipaddr
port
regseconds
defaultuser zhecka1
fullcontact
regserver
useragent
lastms
host dynamic
type friend
context web
permit
deny
secret
md5secret
remotesecret
transport ws
dtmfmode
directmedia no
nat
callgroup
pickupgroup
language
disallow
allow
insecure
trustrpid
progressinband
promiscredir
useclientcode
accountcode
setvar
callerid
amaflags
callcounter
busylevel
allowoverlap
allowsubscribe
videosupport
maxcallbitrate
rfc2833compensate
mailbox
session-timers
session-expires
session-minse
session-refresher
t38pt_usertpsource
regexten
fromdomain
fromuser
qualify
defaultip
rtptimeout
rtpholdtimeout
sendrpid
outboundproxy
callbackextension
timert1
timerb
qualifyfreq
constantssrc
contactpermit
contactdeny
usereqphone
textsupport
faxdetect
buggymwi
auth
fullname
trunkname
cid_number
callingpres
mohinterpret
mohsuggest
parkinglot
hasvoicemail
subscribemwi
vmexten
autoframing
rtpkeepalive
call-limit
g726nonstandard
ignoresdpversion
allowtransfer
dynamic
path
supportpath
encryption yes
avpf yes
force_avp yes
icesupport yes
dtlsenable yes
dtlsverify fingerprint
dtlscertfile /usr/local/share/asterisk/keys/cert.crt
dtlsprivatekey /usr/local/share/asterisk/keys/private.pem
dtlscafile /usr/local/share/asterisk/keys/ca.crt
dtlssetup actpass
Данный запрос покажет все realtime пиры, которые имеются в базе данных.
Далее необходимо по имени каждого пира выполнить команду
asterisk-macomnet*CLI> sip show peer zhecka load
* Name : zhecka
Description :
Realtime peer: Yes, cached
Secret :
MD5Secret :
Remote Secret:
Context : web
Record On feature : automon
Record Off feature : automon
Subscr.Cont. :
Language : ru
Tonezone :
AMA flags : Unknown
Transfer mode: open
CallingPres : Presentation Allowed, Not Screened
Callgroup :
Pickupgroup :
Named Callgr :
Nam. Pickupgr:
MOH Suggest :
Mailbox :
VM Extension : asterisk
LastMsgsSent : 0/0
Call limit : 0
Max forwards : 0
Dynamic : Yes
Callerid : "" <>
MaxCallBR : 384 kbps
Expire : 120
Insecure : no
Force rport : Auto (No)
Symmetric RTP: No
ACL : No
DirectMedACL : No
T.38 support : No
T.38 EC mode : Unknown
T.38 MaxDtgrm: 4294967295
DirectMedia : No
PromiscRedir : No
User=Phone : No
Video Support: No
Text Support : No
Ign SDP ver : No
Trust RPID : No
Send RPID : No
Path support : No
Path : N/A
TrustIDOutbnd: Legacy
Subscriptions: Yes
Overlap dial : No
DTMFmode : rfc2833
Timer T1 : 500
Timer B : 32000
ToHost :
Addr->IP : (null)
Defaddr->IP : (null)
Prim.Transp. : UDP
Allowed.Trsp : UDP,TCP
Def. Username: zhecka
SIP Options : (none)
Codecs : (ulaw|alaw|gsm|h263)
Auto-Framing : No
Status : Unmonitored
Useragent :
Reg. Contact :
Qualify Freq : 60000 ms
Keepalive : 0 ms
Sess-Timers : Accept
Sess-Refresh : uas
Sess-Expires : 1800 secs
Min-Sess : 90 secs
RTP Engine : asterisk
Parkinglot :
Use Reason : No
Encryption : No
После этих операций во внутреннем дереве данных появится запись realtime пира «zhecka», находящегося в данный момент в «offline»
asterisk-macomnet*CLI> data get /asterisk/channel/sip/peers 1=1 peers/peer/cid_num,peers/peer/name,peers/peer/username,peers/peer/secret,peers/peer/lastms,peers/peer/fullcontact
peers
peer
cid_num: ""
fullcontact: ""
username: "zhecka"
secret: "zhecka"
lastms: 4294967295
name: "zhecka"
peer
cid_num: "6001"
fullcontact: ""
username: ""
secret: "MegaPass12345"
lastms: 0
name: "6001"
peer
cid_num: "person"
fullcontact: ""
username: "person"
secret: "E346fz8Vam"
lastms: 0
name: "person"
Как видно из представленной последовательности, для получения данных необходимо проделать несколько различных операций для каждого имеющегося в realtime базе пира.
Наверное многие из Вас скажут: «Есть же REST! используй его!»
Отвечу: «Да. REST есть. Но он слабоват и не даёт нужной информации»
Вывод REST запроса http://localhost:8088/ari/endpoints/SIP
[
{
"technology": "SIP",
"resource": "person",
"state": "unknown",
"channel_ids": []
},
{
"technology": "SIP",
"resource": "mtrf",
"state": "unknown",
"channel_ids": []
},
{
"technology": "SIP",
"resource": "6001",
"state": "unknown",
"channel_ids": []
}
]
В выводе к сожалению отсутствуют нужные поля и для их добавления необходимо переписать или дописать REST модуль.
Итак, что же предлагается? А предлагается перенести дерево Asterisk в файловую систему на манер devfs/procfs и отслеживать все настройки в одном месте.
Выглядит это примерно вот так.
# tree /mnt
/mnt
├── config -> /usr/local/etc/asterisk
├── peers
│ ├── iax2
│ │ ├── corbina
│ │ │ └── full
│ │ └── msm
│ │ └── full
│ └── sip
│ ├── _offline
│ │ └── mtrf -> ../mtrf
│ ├── _online
│ └── mtrf
│ ├── codecs
│ ├── contexts
│ ├── credentials
│ ├── full
│ └── huntgroups
└── users
├── iax2
│ └── macomnet
│ └── full
├── sipfriends
│ ├── 6001
│ │ ├── codecs
│ │ ├── contexts
│ │ ├── credentials
│ │ ├── full
│ │ └── huntgroups
│ ├── _offline
│ │ ├── 6001 -> ../6001
│ │ └── person -> ../person
│ ├── _online
│ │ └── zhecka -> ../zhecka
│ ├── person
│ │ ├── codecs
│ │ ├── contexts
│ │ ├── credentials
│ │ ├── full
│ │ └── huntgroups
│ └── zhecka
│ ├── codecs
│ ├── contexts
│ ├── credentials
│ ├── full
│ └── huntgroups
└── sipusers
├── _offline
│ └── zhecka1 -> ../zhecka1
├── _online
└── zhecka1
├── codecs
├── contexts
├── credentials
├── full
└── huntgroups
Это реальный слепок файловой системы тестового сервера Asterisk. Так как мы не ограничены в форматировании выдаваемых данных, то выдача параметров для подключения будет выглядеть следующим образом.
# cat /mnt/users/sipfriends/_online/zhecka/credentials
name=zhecka
username=zhecka
secret=zhecka
md5secret=
fromuser=
fromdomain=
cid_name=
remotesecret=
fullcontact=sip:zhecka@10.6.207.135:64802;transport=udp;rinstance=d42eec40501a7c4d
useragent=Bria release 2.5 RC4 stamp 47242
regexten=
transports=UDP,TCP
Или контексты
# cat /mnt/users/sipfriends/_online/zhecka/contexts
context=web
subscribecontext=
Все эти данные получаются в реальном режиме времени, ну или с минимальной задержкой при обработке Asterisk Events, во время отключения/подключения устройства.
Как это работает? AstFS написана на языке Python и использует библиотеку Fuse.
Библиотека Fuse позволяет на каждый соответствующий event (open, readdir, getattr, close и т.д.), приходящий от файловой системы, вызвать процедуру обрабатывающую входящие и формирующую конечные данные пользователя.
Так как библиотека Fuse как и большинство event based библиотек имеет внутренний loop, то передать ей данные с внешней программы довольно тяжело. Необходим общий объект или pipe для работы.
С точки зрения использования TCP/UDP socket pipe или чего-то похожего, в случае отказа сервера или демона обеспечивающего доставку данных, запрос к файловой системе бесконтрольно повиснет. Опять же из-за собственного внутреннего цикла библиотека не может быть корректно внедрена в асинхронные фреймворки типа twisted (у меня не получилось по крайней мере). Попытка завести всё через Threading вызвала кучу разных проблем и от этой идеи пришлось отказаться. Вызов соединения из процедур напрямую к Asterisk вызывал дичайший оверхед, т.к. на каждый «чих» необходимо было открывать новое соединение к AMI.
Единственным более менее нормальным решением было разделение процесса обслуживающего запросы к файловой системе и процесса получающего данные из Asterisk используя multiprocessing. У данного компонента имеется возможность контроля за порождаемыми процессами, а также имеются объекты, которые можно «шарить» между процессами.
Таким образом система состоит из трёх процессов:
- Manager
- FuseFS
- Asterisk Event Handler
Между ними «шарится» общий словарь упакованный в JSON. Дело в том, что общие объекты имеют структуру отличную от python структур и с ними не совместимы. При обновлении данных из Asterisk, данный словарь распаковывается, обновляется и запаковывается обратно.
Соответственно процесс обслуживающий запросы из файловой системы при необходимости распаковывает словарь и формирует конечные данные для пользователя.
Исходный код AstFS содержит все необходимые классы и пример для создания виртуальной файловой системы. Код конечно далёк от идеала, но будет модифицироваться в лучшую сторону по возможности (есть коментарии на русском).
Основной упор был сделан на гибкость структуры файловой системы.
Под гибкостью подразумевается, что в качестве обработчика ветки дерева или файла может использоваться либо класс либо функция либо просто статические данные.
Определение дерева довольно простое:
root = {
'helpfilefunc': [fsstruct.stat_info['file'], fusefscore.returnhelp],
'link_to_tmp': [fsstruct.stat_info['link'], '/tmp'],
'staticfile': [fsstruct.stat_info['file'], 'contents of staticfile'],
'customchmodfile': [{'st_mode': 0x8000 | int('0600', 8), 'st_uid': 0, 'st_gid': 0}, 'contents of customchmodfile'],
'nulldir': {},
'nullfile': [{}, ''],
'demotreeclass': fsstruct.BasicTree('/demotreeclass', mpd, ''),
'tree': {'peer': [{'st_mode': 0x8000 | int('0644', 8)}, 'contents of peer'], 'friend': {}, 'user': {}}
}
# tree -ghpu /mnt/
/mnt/
├── [-rw------- root wheel 27] customchmodfile
├── [drwxr-xr-x asterisk asterisk 512] demotreeclass
│ ├── [-rw-r--r-- asterisk asterisk 12] help
│ └── [-rw-r--r-- asterisk asterisk 12] info
├── [-rw-r--r-- asterisk asterisk 33] helpfilefunc
├── [lrwxr-xr-x asterisk asterisk 4] link_to_tmp -> /tmp
├── [drwxr-xr-x asterisk asterisk 512] nulldir
├── [-rw-r--r-- asterisk asterisk 0] nullfile
├── [-rw-r--r-- asterisk asterisk 22] staticfile
└── [drwxr-xr-x asterisk asterisk 512] tree
├── [drwxr-xr-x asterisk asterisk 512] friend
├── [-rw-r--r-- asterisk asterisk 16] peer
└── [drwxr-xr-x asterisk asterisk 512] user
6 directories, 7 files
Если нужно определить файл, то просто задаётся массив (list) состоящий из двух параметров. Первый это тип файла и права доступа к нему, второй это либо статическое содержимое файла, либо имя функции возвращающей содержимое файла. Если нужно определить директорию, то просто определяется словарь (dict) содержащий в свою очередь либо список файлов, либо список директорий. При определении директории в которой находится динамически изменяемая структура, можно задать подкласс, который будет обрабатывать все запросы файловой системы.
По сути для базового функционала в классе достаточно описать 4 метода:
- getattr
- read
- readdir
- readlink
Остановимся поподробнее об особенностях реализации.
getattr вызывается для определения типа файла или директории, возвращает только атрибуты! Никакой другой информации из данного метода не получается.
readdir вызывается при открытии директории и запросе файлов, возвращает только список файлов находящихся в директории без атрибутов!
При обычном вызове списка файлов
# ls /mnt
customchmodfile demotreeclass helpfilefunc link_to_tmp nulldir nullfile staticfile tree
происходит следующая последовательность
2017-03-14 13:13:04,559 - DEBUG - [FuseFSCore.getattr] path / fh None. Context: {'gid': 0, 'pid': 99276, 'uid': 0}
2017-03-14 13:13:04,559 - DEBUG - [FuseFSCore.returntree] Instance Dict > retdict: {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}
2017-03-14 13:13:04,560 - DEBUG - [FuseFSCore.getattr] Found data {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []} for path /
2017-03-14 13:13:04,560 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object / is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 9, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:13:04,561 - DEBUG - [FuseFSCore.statfs] Request FileSystem Info. Context: {'gid': 0, 'pid': 99276, 'uid': 0}
2017-03-14 13:13:04,561 - DEBUG - [FuseFSCore.readdir] Path /:0. Context: {'gid': 0, 'pid': 99276, 'uid': 0}
2017-03-14 13:13:04,562 - DEBUG - [FuseFSCore.returntree] Instance Dict > retdict: {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}
2017-03-14 13:13:04,562 - DEBUG - [FuseFSCore.readdir] Found data {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []} for path /
2017-03-14 13:13:04,562 - DEBUG - [FuseFSCore.readdir] SrcTree: {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}, DirTree: :{'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}
- через getattr запрашивается наличие файла или пути »/»
- функция обрабатывающая дерево файлов returntree возвращает содержимое запрошенной ветки дерева с добавлением элемента '__dirstat__', который в последующих функциях либо выдаётся в качестве ответа на getattr, либо удаляется если запрашивается список файлов в директории.
- на выходе из getattr удаляется список файлов и системе возвращаются только атрибуты директории.
- запрашивается размер блока на томе через statfs
- система запрашивает содержимое директории »/»
- returntree возвращает список файлов с атрибутами директории.
- readdir убирает атрибуты директории из списка и отдаёт системе только список файлов.
Попробуем открыть файл
2017-03-14 13:26:45,137 - DEBUG - [FuseFSCore.open] /staticfile:0. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,138 - DEBUG - [FuseFSCore.getattr] path /staticfile fh None. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,139 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,139 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,140 - DEBUG - [FuseFSCore.getattr] Found data [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile'] for path /staticfile
2017-03-14 13:26:45,140 - DEBUG - [FuseFSCore.getattr] List :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 22, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,140 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /staticfile is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 22, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,141 - DEBUG - [FuseFSCore.read] read file /staticfile:4096:0. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,141 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,142 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,142 - DEBUG - [FuseFSCore.read] dirtree data [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile'] for path /staticfile
2017-03-14 13:26:45,142 - DEBUG - [FuseFSCore.read] FoundTree: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,143 - DEBUG - [FuseFSCore.read] read file /staticfile:4096:22. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,143 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,143 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,143 - DEBUG - [FuseFSCore.read] dirtree data [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile'] for path /staticfile
2017-03-14 13:26:45,144 - DEBUG - [FuseFSCore.read] FoundTree: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,144 - DEBUG - [FuseFSCore.truncate] /staticfile:22:None. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,144 - DEBUG - [FuseFSCore.getattr] path /staticfile fh None. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
2017-03-14 13:26:45,144 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,144 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile']
2017-03-14 13:26:45,145 - DEBUG - [FuseFSCore.getattr] Found data [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, 'contents of staticfile'] for path /staticfile
2017-03-14 13:26:45,145 - DEBUG - [FuseFSCore.getattr] List :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 22, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,145 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /staticfile is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 33188, 'st_dev': 0, 'st_size': 22, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}
2017-03-14 13:26:45,145 - DEBUG - [FuseFSCore.release] /staticfile:0. Context: {'gid': 0, 'pid': 99297, 'uid': 0}
Последовательность похожа.
- через open указывается имя файла
- через getattr запрашиваются атрибуты файла
- производится проверка файла на чтение с нулевым размером читаемого блока, если всё хорошо — производится чтение блока из файла равного размеру блока файловой системы или если размер файла меньше, то размеру файла.
- файл закрывается и освобождается
Так работает статическая структура. А что же с динамической ?
Откроем директорию /mnt/demotreeclass, обработчик которой ссылается на класс
fsstruct.BasicTree.
2017-03-14 13:29:16,819 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99300, 'uid': 0}
2017-03-14 13:29:16,819 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass
2017-03-14 13:29:16,819 - DEBUG - [BasicTree._getattr] started for path
2017-03-14 13:29:16,820 - DEBUG - [BasicTree._getattr] path len 1, path ['']
2017-03-14 13:29:16,820 - DEBUG - [BasicTree._getattr] return full tree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:29:16,820 - DEBUG - [FuseFSCore.getattr] Object return dirtree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:29:16,821 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:29:16,821 - DEBUG - [FuseFSCore.statfs] Request FileSystem Info. Context: {'gid': 0, 'pid': 99300, 'uid': 0}
2017-03-14 13:29:16,822 - DEBUG - [FuseFSCore.readdir] Path /demotreeclass:0. Context: {'gid': 0, 'pid': 99300, 'uid': 0}
2017-03-14 13:29:16,822 - DEBUG - [FuseFSCore.readdir] Found data for path /demotreeclass
2017-03-14 13:29:16,822 - DEBUG - [BasicTree._readdir] path len 1, path ['']
2017-03-14 13:29:16,822 - DEBUG - [BasicTree._readdir] return full tree {'info': {}, 'help': {}}
2017-03-14 13:29:16,822 - DEBUG - [FuseFSCore.readdir] Object return dirtree {'info': {}, 'help': {}}
2017-03-14 13:29:16,822 - DEBUG - [FuseFSCore.readdir] SrcTree: , DirTree: :{'info': {}, 'help': {}}
Происходит всё тоже самое, что и при открытии статической директории, только при обходе дерева объектов в структуре директории, обработчик видит, что ссылка на директорию — это класс и передаёт управление ему. Далее класс сам в зависимости от запрашиваемой операции формирует ответ и отдаёт его основному процессу.
Сложнее всего дело обстоит если кто-то запрашивает команду
# ls -al /mnt/demotreeclass
total 2
drwxr-xr-x 1 asterisk asterisk 512 14 мар 11:10 .
drwxr-xr-x 9 asterisk asterisk 512 14 мар 11:10 ..
-rw-r--r-- 1 asterisk asterisk 12 14 мар 11:10 help
-rw-r--r-- 1 asterisk asterisk 12 14 мар 11:10 info
2017-03-14 13:40:46,539 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,540 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass
2017-03-14 13:40:46,540 - DEBUG - [BasicTree._getattr] started for path
2017-03-14 13:40:46,540 - DEBUG - [BasicTree._getattr] path len 1, path ['']
2017-03-14 13:40:46,540 - DEBUG - [BasicTree._getattr] return full tree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,541 - DEBUG - [FuseFSCore.getattr] Object return dirtree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,541 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,541 - DEBUG - [FuseFSCore.statfs] Request FileSystem Info. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,542 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,542 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass
2017-03-14 13:40:46,542 - DEBUG - [BasicTree._getattr] started for path
2017-03-14 13:40:46,542 - DEBUG - [BasicTree._getattr] path len 1, path ['']
2017-03-14 13:40:46,543 - DEBUG - [BasicTree._getattr] return full tree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,543 - DEBUG - [FuseFSCore.getattr] Object return dirtree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,543 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,543 - DEBUG - [FuseFSCore.readdir] Path /demotreeclass:0. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,544 - DEBUG - [FuseFSCore.readdir] Found data for path /demotreeclass
2017-03-14 13:40:46,544 - DEBUG - [BasicTree._readdir] path len 1, path ['']
2017-03-14 13:40:46,544 - DEBUG - [BasicTree._readdir] return full tree {'info': {}, 'help': {}}
2017-03-14 13:40:46,544 - DEBUG - [FuseFSCore.readdir] Object return dirtree {'info': {}, 'help': {}}
2017-03-14 13:40:46,545 - DEBUG - [FuseFSCore.readdir] SrcTree: , DirTree: :{'info': {}, 'help': {}}
2017-03-14 13:40:46,545 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,545 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass
2017-03-14 13:40:46,545 - DEBUG - [BasicTree._getattr] started for path
2017-03-14 13:40:46,546 - DEBUG - [BasicTree._getattr] path len 1, path ['']
2017-03-14 13:40:46,546 - DEBUG - [BasicTree._getattr] return full tree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,546 - DEBUG - [FuseFSCore.getattr] Object return dirtree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,546 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,546 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,547 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass
2017-03-14 13:40:46,547 - DEBUG - [BasicTree._getattr] started for path
2017-03-14 13:40:46,547 - DEBUG - [BasicTree._getattr] path len 1, path ['']
2017-03-14 13:40:46,547 - DEBUG - [BasicTree._getattr] return full tree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,547 - DEBUG - [FuseFSCore.getattr] Object return dirtree {'__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}, '']}
2017-03-14 13:40:46,548 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,548 - DEBUG - [FuseFSCore.getattr] path / fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,548 - DEBUG - [FuseFSCore.returntree] Instance Dict > retdict: {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}
2017-03-14 13:40:46,548 - DEBUG - [FuseFSCore.getattr] Found data {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []} for path /
2017-03-14 13:40:46,549 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object / is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 9, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,549 - DEBUG - [FuseFSCore.getattr] path / fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,549 - DEBUG - [FuseFSCore.returntree] Instance Dict > retdict: {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []}
2017-03-14 13:40:46,549 - DEBUG - [FuseFSCore.getattr] Found data {'nullfile': [], 'demotreeclass': [], '__dirstat__': [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}], 'link_to_tmp': [], 'helpfilefunc': [], 'tree': [], 'nulldir': [], 'customchmodfile': [], 'staticfile': []} for path /
2017-03-14 13:40:46,549 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object / is :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 9, 'st_gid': 931, 'st_dev': 0, 'st_size': 512, 'st_ino': 0, 'st_uid': 931, 'st_mode': 16877, 'st_atime': 1489479051}
2017-03-14 13:40:46,550 - DEBUG - [FuseFSCore.getattr] path /demotreeclass/info fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0}
2017-03-14 13:40:46,550 - DEBUG - [FuseFSCore.getattr] Found data for path /demotreeclass/info
2017-03-14 13:40:46,550 - DEBUG - [BasicTree._getattr] started for path info
2017-03-14 13:40:46,550 - DEBUG - [BasicTree._getattr] path len 1, path ['info']
2017-03-14 13:40:46,550 - DEBUG - [FuseFSCore.getattr] Object return dirtree [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 0, 'st_ino': 0, 'st_uid': 931, 'st_mode': 33188, 'st_atime': 1489479051}, >]
2017-03-14 13:40:46,550 - DEBUG - [BasicTree._getfile] started for path info
2017-03-14 13:40:46,550 - DEBUG - [FuseFSCore.getattr] List :{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931