Описывая участие в проекте по модернизации 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 : <Set> MD5Secret : <Not set> Remote Secret: <Not set> Context : web Record On feature : automon Record Off feature : automon Subscr.Cont. : <Not set> Language : ru Tonezone : <Not set> 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 : <Set> MD5Secret : <Not set> Context : web Language : ru AMA flags : Unknown Tonezone : <Not set> 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 <name> load Usage: sip show peer <name> [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 <family> <colmatch> <value> 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 : <Set> MD5Secret : <Not set> Remote Secret: <Not set> Context : web Record On feature : automon Record Off feature : automon Subscr.Cont. : <Not set> Language : ru Tonezone : <Not set> 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 <type 'dict'>:{'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: <type 'dict'>:{'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 <type 'dict'>:{'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 <type 'dict'>:{'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 <type 'dict'>:{'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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> return dirtree {'info': {}, 'help': {}} 2017-03-14 13:29:16,822 - DEBUG - [FuseFSCore.readdir] SrcTree: <astfs.fsstruct.BasicTree object at 0x8058fe210>, DirTree: <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> return dirtree {'info': {}, 'help': {}} 2017-03-14 13:40:46,545 - DEBUG - [FuseFSCore.readdir] SrcTree: <astfs.fsstruct.BasicTree object at 0x8058fe210>, DirTree: <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <type 'dict'>:{'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 <type 'dict'>:{'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 <type 'dict'>:{'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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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 <astfs.fsstruct.BasicTree object at 0x8058fe210> 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}, <bound method BasicTree._getfile of <astfs.fsstruct.BasicTree object at 0x8058fe210>>] 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 <type 'dict'>:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 12, 'st_ino': 0, 'st_uid': 931, 'st_mode': 33188, 'st_atime': 1489479051} 2017-03-14 13:40:46,551 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass/info is <type 'dict'>:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 12, 'st_ino': 0, 'st_uid': 931, 'st_mode': 33188, 'st_atime': 1489479051} 2017-03-14 13:40:46,551 - DEBUG - [FuseFSCore.getattr] path /demotreeclass/help fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0} 2017-03-14 13:40:46,551 - DEBUG - [FuseFSCore.getattr] Found data <astfs.fsstruct.BasicTree object at 0x8058fe210> for path /demotreeclass/help 2017-03-14 13:40:46,551 - DEBUG - [BasicTree._getattr] started for path help 2017-03-14 13:40:46,551 - DEBUG - [BasicTree._getattr] path len 1, path ['help'] 2017-03-14 13:40:46,552 - DEBUG - [FuseFSCore.getattr] Object <astfs.fsstruct.BasicTree object at 0x8058fe210> 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}, <bound method BasicTree._getfile of <astfs.fsstruct.BasicTree object at 0x8058fe210>>] 2017-03-14 13:40:46,552 - DEBUG - [BasicTree._getfile] started for path help 2017-03-14 13:40:46,552 - DEBUG - [FuseFSCore.getattr] List <type 'dict'>:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 12, 'st_ino': 0, 'st_uid': 931, 'st_mode': 33188, 'st_atime': 1489479051} 2017-03-14 13:40:46,552 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass/help is <type 'dict'>:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_gid': 931, 'st_dev': 0, 'st_size': 12, 'st_ino': 0, 'st_uid': 931, 'st_mode': 33188, 'st_atime': 1489479051} 2017-03-14 13:40:46,553 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0} 2017-03-14 13:40:46,554 - DEBUG - [FuseFSCore.getattr] Found data <astfs.fsstruct.BasicTree object at 0x8058fe210> for path /demotreeclass 2017-03-14 13:40:46,554 - DEBUG - [BasicTree._getattr] started for path 2017-03-14 13:40:46,554 - DEBUG - [BasicTree._getattr] path len 1, path [''] 2017-03-14 13:40:46,554 - 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,554 - DEBUG - [FuseFSCore.getattr] Object <astfs.fsstruct.BasicTree object at 0x8058fe210> 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,554 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is <type 'dict'>:{'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,554 - DEBUG - [FuseFSCore.getattr] path /demotreeclass fh None. Context: {'gid': 0, 'pid': 99321, 'uid': 0} 2017-03-14 13:40:46,555 - DEBUG - [FuseFSCore.getattr] Found data <astfs.fsstruct.BasicTree object at 0x8058fe210> for path /demotreeclass 2017-03-14 13:40:46,555 - DEBUG - [BasicTree._getattr] started for path 2017-03-14 13:40:46,555 - DEBUG - [BasicTree._getattr] path len 1, path [''] 2017-03-14 13:40:46,555 - 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,555 - DEBUG - [FuseFSCore.getattr] Object <astfs.fsstruct.BasicTree object at 0x8058fe210> 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,555 - DEBUG - [FuseFSCore.getattr] Attrstat Result for object /demotreeclass is <type 'dict'>:{'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}
В данном случае происходит рекурсивный запрос по всем объектам внутри директории и если их довольно много, то это может создать довольно существенную нагрузку на систему, особенно если включен дебаг.
За счёт того, что чтение файлов производится по возвращаемым системой атрибутам, возникает необходимость постоянного пересчёта размера файлов при запросах. Неверно выданный размер приводит к "обрезанию" файла либо ошибке файловой системы.
При обращении к ссылке на ресурс(symlink)
# ls /mnt/link_to_tmp .ICE-unix aguilera.ulaw crontab.B0fGYRPx73~ macroform-the_simplicity.wav mycachedir pymp-Ri0pXJ pymp-eysf7M socket .X11-unix cachedir crontab.SyEoUZMExj~ manolo_camp-morning_coffee.ulaw mysql.sock pymp-SZfCWa pymp-hH6kLC treecache .XIM-unix crontab.1yo8gxs5HE~ errlog manolo_camp-morning_coffee.wav pymp-6oVKsj
2017-03-14 13:47:55,593 - DEBUG - [FuseFSCore.readlink] path: /link_to_tmp. Context: {'gid': 0, 'pid': 99341, 'uid': 0} 2017-03-14 13:47:55,594 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051} 2017-03-14 13:47:55,594 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, '/tmp'] 2017-03-14 13:47:55,594 - DEBUG - [FuseFSCore.readlink] FoundTree: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, '/tmp'] 2017-03-14 13:47:55,595 - DEBUG - [FuseFSCore.readlink] path: /link_to_tmp. Context: {'gid': 0, 'pid': 99341, 'uid': 0} 2017-03-14 13:47:55,595 - DEBUG - [FuseFSCore.returntree] Instance List > itemstat:{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051} 2017-03-14 13:47:55,595 - DEBUG - [FuseFSCore.returntree] data: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, '/tmp'] 2017-03-14 13:47:55,596 - DEBUG - [FuseFSCore.readlink] FoundTree: [{'st_ctime': 1489479051, 'st_mtime': 1489479051, 'st_nlink': 1, 'st_mode': 41453, 'st_dev': 0, 'st_size': 0, 'st_gid': 931, 'st_uid': 931, 'st_ino': 0, 'st_atime': 1489479051}, '/tmp']
Происходят всё те же операции:
- через getattr запрашиваются атрибуты объекта
- система "видит", что объект является символьной ссылкой и вызывает функцию readlink
- readlink отдаёт в обычном текстовом виде адрес ресурса на который нужно перенаправить вызов.
Так в чём же красота решения?
Красота решения в том, что на любую ветку дерева можно навесить абсолютно любую логику. Скажем Вы хотите видеть любимый сайт торрентов в файловом виде у себя в системе. Вы просто создаёте класс, который на запрос данных директории "дёргает" трекер и выдаёт Вам список имеющихся торентов в виде списка файлов. При открытии или копировании нужного файла, класс просто "дёргает" нужный файл с трекера и отдаёт его Вам.
Или скажем у вас есть сервер с динамически строящимис�� отчётами. Вам нет необходимости хранить все отчёты на локальной файловой системе, т.к. они есть в базе. Просто создаётся класс, который смотрит в базу и при запросе выдаёт необходимую информацию о наличии отчётов. Или при запросе отчёта в режиме реального времени строит его.
Или у вас есть логи демонов, которые нужно обрабатывать реалтайм. Можно конечно повесить хендлер, который будет отслеживать изменение лога, или сделать pipe для отправки лога в какой-то скрипт. С помощью виртуальной файловой системы с помощью спецкласса Вы можете писать логи в базу напрямую если это необходимо, при этом уже в подготовленном виде.
Еще одно применение такого плана файловых систем это изоляция данных пользователей друг от друга. При обращении к файлу находящемуся в виртуальной файловой системе в процедуры передаётся переменное окружение в виде номера процесса, uid и gid пользователя. На основе этих данных можно формировать различное содержимое для одного и того же файла, т.е. указатель на файл один, а содержимое для всех разное, скажем файл с набором паролей для конкретного пользователя зашифрованный ключём этого пользователя. Речь конечно о многопользовательских системах, где на одном сервере работает куча народу.
Есть конечно и явный минус заключающийся в недостаточной скорости работы при большой вложенности, но чем-то всё равно приходится жертвовать.
Так что же всё-таки использование виртуальных файловых систем — это шаг назад или два вперед ?
PS: если кто пропустил ссылку на проект на github, вот она
© Aborche 2017

