బాక్స్ వెలుపల ఆస్టరిస్క్‌తో Zabbixని ఎలా కనెక్ట్ చేయాలి

మునుపటి వ్యాసంలో "Zabbix - విస్తరిస్తున్న స్థూల సరిహద్దులు" అధికార సెషన్‌ను ఎలా స్వీకరించాలో మరియు దానిని స్థానిక హోస్ట్ మాక్రోలో ఎలా భర్తీ చేయాలో నేను మీకు చెప్పాను. బాహ్య స్క్రిప్ట్‌లు మరియు సాఫ్ట్‌వేర్ లేకుండా ఆస్టరిస్క్‌తో Zabbixని ఎలా కనెక్ట్ చేయాలో ఈ వ్యాసంలో నేను మీకు చెప్తాను.

అదనపు సాఫ్ట్‌వేర్ లేదా స్క్రిప్ట్‌లను ఇన్‌స్టాల్ చేయకుండా, ఈ రెండు సిస్టమ్‌ల యొక్క "స్నేహితులను" చేయాలనే ఆలోచన చాలా కాలం క్రితం పుట్టింది. త్వరిత గూగ్లింగ్ అనేక సాధ్యమైన పరిష్కారాలను అందించింది, సర్వర్‌కు స్క్రిప్ట్‌లను (పైహా, బాష్, పైథాన్, మొదలైన వాటిలో) అప్‌లోడ్ చేయడం వల్ల మీరు సంతోషంగా ఉంటారు. బాహ్య స్క్రిప్ట్‌లు లేకుండా మరియు మానిటరింగ్ మరియు PBXతో సర్వర్‌లో అదనపు సాఫ్ట్‌వేర్‌ను ఇన్‌స్టాల్ చేయడం ద్వారా "బాక్స్ వెలుపల" పర్యవేక్షణను అమలు చేయాలనుకుంటున్నాను.

నేను దీనితో మొత్తం 4 పని దినాలు గడిపాను, కానీ ఫలితం విలువైనది. AMI ఇంటర్‌ఫేస్, తక్కువ-స్థాయి గుర్తింపు, ట్రిగ్గర్లు మరియు ముఖ్యంగా, PBX మరియు అన్ని ఇతర సెట్టింగ్‌లను కనెక్ట్ చేయడానికి ఇప్పుడు దాదాపు 15 నిమిషాలు పడుతుంది.

Zabbix 4.4 అందుబాటులో ఉంది, ఆస్టరిస్క్ వెర్షన్ 100 యొక్క 13 ముక్కలు. కొన్ని PBXలు FreePBX వెబ్ ఇంటర్‌ఫేస్‌తో వస్తాయి, కొన్ని బేర్ కన్సోల్‌తో, కొన్ని ఉపాయాలు మరియు డయల్‌ప్లాన్ ద్వారా ఇంటిగ్రేషన్‌తో వస్తాయి.

PBX నుండి డేటాను స్వీకరిస్తోంది

పరిష్కరించాల్సిన మొదటి మరియు ప్రధాన అంశం సహచరులు మరియు SIP రిజిస్ట్రేషన్‌ల గురించి డేటాను పొందడం. ఈ ప్రయోజనం కోసం, PBX AGI, AMI, ARI మరియు SSH కన్సోల్ ఇంటర్‌ఫేస్‌లను కలిగి ఉంది. స్పష్టమైన కారణాల వల్ల, నేను అదనపు మాడ్యూళ్లను పరిగణించలేదు.

ముందుగా ఈ అగి, అమీ, ఆరి ఏమిటో మనం గుర్తించాలి...

  • AGI - డయల్‌ప్లాన్‌లో స్క్రిప్ట్‌లను ఉపయోగించడం. కాల్ మేనేజ్‌మెంట్ కోసం ప్రధానంగా ఉపయోగించబడుతుంది.
  • AMI - అవసరమైన అన్ని సమాచారాన్ని అందించగలదు, టెల్నెట్ మాదిరిగానే పోర్ట్ 5038 ద్వారా పనిచేస్తుంది. మాకు సరిపోతుంది!
  • ARI - ఆధునిక, ఫ్యాషన్, JSON. అనేక అవకాశాలు ఉన్నాయి, Zabbix కోసం డేటా ఫార్మాట్ అర్థమయ్యేలా ఉంది, కానీ నాకు ప్రధాన విషయం లేదు: మీరు సిప్ నమోదును నియంత్రించలేరు. మరో ప్రతికూలత ఏమిటంటే, పీర్‌లకు ఆన్‌లైన్/ఆఫ్‌లైన్‌లో రెండు రాష్ట్రాలు మాత్రమే ఉన్నాయి, అయినప్పటికీ ఎక్కువ రాష్ట్రాలు ఉన్నాయి మరియు రోగనిర్ధారణ చేసేటప్పుడు వాటిని పరిగణనలోకి తీసుకోవడం ఉపయోగకరంగా ఉంటుంది.
  • SSH ప్రతిదీ చేయగలదు, కానీ కొన్నిసార్లు "భద్రతా కారణాల" కారణంగా ఇది అనుమతించబడదు. పరిగణనలు భిన్నంగా ఉండవచ్చు, నేను వాటిలోకి వెళ్లను.

అయినప్పటికీ, దాని అన్ని లోపాలతో, ARI అన్ని పర్యవేక్షణ అవసరాలలో 90% కవర్ చేస్తుంది.

Zabbix మరియు Telnet - నా నిరాశ

నాకు AMI బాగా తెలుసు; ఒకప్పుడు నేను రిమోట్ ఆఫీసులు, కాల్ మేనేజ్‌మెంట్ మొదలైన వాటి ద్వారా విభజనతో సంభాషణలలో నష్టాలను ట్రాక్ చేయడం అమలు చేసాను. టెల్నెట్తో, ప్రతిదీ కూడా చాలా స్పష్టంగా ఉంది: కనెక్షన్ తెరవండి, ఆదేశాలను పంపండి మరియు ప్రతిస్పందనను చదవండి. నేను చేసినది అదే, కానీ ఫలితం నన్ను నిరాశపరిచింది.

Zabbixలోని టెల్నెట్ Linux కన్సోల్‌లో ఉన్నట్లే కాదు, ఇది కొంచెం సరళమైనది మరియు లాగిన్/పాస్‌వర్డ్ వంటి ప్రామాణిక అధికారం కోసం రూపొందించబడింది. ప్రామాణీకరణ తర్కం భిన్నంగా ఉంటే మరియు లాగిన్/పాస్‌వర్డ్ జత కోసం ఎటువంటి అభ్యర్థన లేకపోతే, లోపం ఏర్పడుతుంది. అధికార అవసరాన్ని దాటవేయడానికి వ్యర్థమైన ప్రయత్నాల తర్వాత, టెల్నెట్ మాడ్యూల్ యొక్క సోర్స్ కోడ్‌ను చూడటం ఉపయోగకరంగా ఉంటుంది.

సాంప్రదాయ లాగిన్ మరియు పాస్‌వర్డ్ అభ్యర్థన వచ్చే వరకు, నేను ముందుకు వెళ్లనని నేను గ్రహించాను. కేవలం వినోదం కోసం, నేను కోడ్ నుండి అధికారానికి సంబంధించిన అన్నింటినీ తీసివేసి, అన్నింటినీ మళ్లీ సమీకరించాను. పనిచేస్తుంది! కానీ అది అవసరాలకు అనుగుణంగా లేదు. ముందుకి వెళ్ళు…

శోధనకు తిరిగి వెళ్దాం

నేను ARI డాక్యుమెంటేషన్‌ను మళ్లీ మళ్లీ చదివాను, అదనపు పరీక్షలను నిర్వహించాను - ఇక్కడ సిప్ రిజిస్ట్రేషన్‌లు లేవు. విందులు ఉన్నాయి, సంభాషణలు ఉన్నాయి, బ్రీచెస్ ఉన్నాయి, కానీ రిజిస్ట్రేషన్లు లేవు. ఏదో ఒక సమయంలో నేను కూడా అనుకున్నాను, మనకు నిజంగా రాబందు రిజిస్ట్రేషన్ అవసరమా?

ఒక తమాషా యాదృచ్ఛికంగా, ఈ సమయంలో అవుట్‌గోయింగ్ కాల్‌ల సమస్యతో వినియోగదారు నుండి మరొక అభ్యర్థన వచ్చింది. సమస్య ఏమిటంటే సిప్ రిజిస్ట్రేషన్ స్తంభింపజేయడం మరియు మాడ్యూల్‌ను రీబూట్ చేయడం ద్వారా పరిష్కరించబడింది.

asterisk -rx "sip reload"

వెబ్‌లో AMIని యాక్సెస్ చేయడం చాలా బాగుంది: ఇది అన్ని సమస్యలను పరిష్కరిస్తుంది, నేను అనుకున్నాను. నేను ఈ దిశలో త్రవ్వడం ప్రారంభించాను మరియు అక్షరాలా మొదటి శోధన లైన్ అధికారిక ఆస్టరిస్క్ డాక్యుమెంటేషన్‌కు దారి తీస్తుంది, ఇది నా పనులకు ఒక ఎంపిక ఉందని చెబుతుంది వెబ్ ఎనేబుల్ చేయబడింది ఫైల్‌లో /etc/asterisk/manager.conf, విభాగంలో అవును అని సెట్ చేయాలి [సాధారణ]

దీని తర్వాత, ఫారమ్ యొక్క సాధారణ వెబ్ అభ్యర్థన ద్వారా http://ats:8089/mxml?action=SIPshowregistry మేము అవసరమైన అన్ని సమాచారాన్ని పొందుతాము.

FreePBX ఇంటర్‌ఫేస్‌ని ఉపయోగిస్తున్నప్పుడు, మీరు వెబ్ ద్వారా ఈ ఎంపికను ప్రారంభించలేరు; మీరు manager.conf ఫైల్‌కు మార్పులు చేయడం ద్వారా కన్సోల్ ద్వారా దీన్ని ప్రారంభించాలి. వెబ్ ద్వారా కాన్ఫిగరేషన్ మార్పులు చేసినప్పుడు FreePBX దానిని తొలగించదు.

నేను చాలా కాలం పాటు వివిధ రకాల ఆస్టరిస్క్ ఇంటిగ్రేషన్‌లతో పని చేసాను, కానీ ఈ ఫీచర్ ఎక్కడా ప్రస్తావించబడటం నేను ఎప్పుడూ చూడలేదు. PBXతో పరస్పర చర్య చేసే ఈ పద్ధతిని ఎవరూ వివరించకపోవడం నాకు ఆశ్చర్యం కలిగించింది. ఈ అంశంపై సమాచారం కోసం వెతకడం కూడా చాలా ఉపయోగకరంగా ఉంది: ఆచరణాత్మకంగా ఏమీ లేదు లేదా ఇది పూర్తిగా భిన్నమైన పనుల కోసం ఉపయోగించబడింది.

WEB AMI - ఎలాంటి మృగం?

ఒక ఎంపికను జోడిస్తోంది వెబ్ ఎనేబుల్ చేయబడింది ఫైల్ చేయడానికి manager.conf వెబ్ ద్వారా ATS నిర్వహణకు పూర్తి ప్రాప్తిని అందించింది. సాధారణ AMI ద్వారా అందుబాటులో ఉన్న అన్ని ఆదేశాలు ఇప్పుడు వెబ్‌లో ఉన్నాయి, మీరు PBX నుండి సాకెట్ ద్వారా ఈవెంట్‌లను వినవచ్చు. ఆపరేషన్ సూత్రం కన్సోల్ AMI నుండి భిన్నంగా లేదు. ఈ ఎంపికను సక్రియం చేసిన తర్వాత, మీరు క్రింది చిరునామాలలో PBXని సంప్రదించవచ్చు:

https://ats:8089/manager — అభ్యర్థనలను పరీక్షించడానికి మరియు మాన్యువల్‌గా పంపడానికి సులభమైన ఇంటర్‌ఫేస్‌తో కూడిన వెబ్ పేజీ. అన్ని ప్రతిస్పందనలు చదవగలిగే HTMLకి ఫార్మాట్ చేయబడ్డాయి. పర్యవేక్షణకు చాలా సరిఅయినది కాదు.
https://ats:8089/rawman — టెక్స్ట్ అవుట్‌పుట్ మాత్రమే, కన్సోల్ AMI మాదిరిగానే ఫార్మాట్
https://ats:8089/mxml - టెక్స్ట్ అవుట్‌పుట్ మాత్రమే, XML ఆకృతిలో. మాకు సరిపోతుంది!

బాక్స్ వెలుపల ఆస్టరిస్క్‌తో Zabbixని ఎలా కనెక్ట్ చేయాలి

అప్పుడు నేను ఇలా అనుకున్నాను: “ఇదే పరిష్కారం! ఇప్పుడు ప్రతిదీ సిద్ధంగా ఉంటుంది! ఈజీ-పీజీ లెమన్ స్క్వీజీ,” కానీ సంతోషించడానికి చాలా తొందరగా ఉంది. మనకు అవసరమైన సమాచారాన్ని పొందడానికి, అవసరమైన చర్యతో కూడిన GET అభ్యర్థనను ఉపయోగించడం సరిపోతుంది చర్య, ఇది ప్రతిస్పందనగా అన్ని రిజిస్ట్రేషన్‌ల జాబితా మరియు వాటి స్థితితో xmlని అందిస్తుంది. ఇదంతా చాలా బాగుంది, కానీ కుక్కీ నుండి సెషన్‌ను గుర్తుంచుకోవడానికి మీకు అధికారం అవసరం. మీరు బ్రౌజర్‌లో పరీక్షించినప్పుడు, మీరు ఈ ప్రక్రియ గురించి ఆలోచించరు.

అధికార ప్రక్రియ

మొదట మేము చిరునామాను సూచిస్తాము http://ats:8089/mxml?action=login&username=zabbix&secret=zabbix, ప్రతిస్పందనగా, సర్వర్ మాకు అధికార సెషన్‌తో కుక్కీని పంపుతుంది. HTTP అభ్యర్థన ఇలా కనిపిస్తుంది:

https://ats:8089/mxml?action=login&username=zabbix&secret=zabbix

Host: ats:8089
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

సమాధానం:

GET: HTTP/1.1 200 OK
Server: Asterisk/13.29.2
Date: Thu, 18 Jun 2020 17:41:19 GMT
Cache-Control: no-cache, no-store
Content-type: text/xml
Set-Cookie: mansession_id="6f5de42c"; Version=1; Max-Age=600
Pragma: SuppressEvents
Content-Length: 146

<ajax-response>
<response type="object" id="unknown">
<generic response="Success" message="Authentication accepted"/>
</response>
</ajax-response>

అక్కడ పని చేయడానికి మీకు అవసరం mansession_id="6f5de42c", అంటే అధికార కుక్కీ.
మీరు సమాధానం కోసం తనిఖీ చేయవలసిన కంటెంట్ "ప్రమాణీకరణ ఆమోదించబడింది" తర్వాత, PBX సర్వర్‌కి చేసే అన్ని కాల్‌ల కోసం, మేము అభ్యర్థనకు అధికార కుక్కీని జోడించాలి.

https://ats:8089/mxml?action=SIPpeers

Host: ats:8089
Connection: close
Cookie: mansession_id="6f5de42c"

అధికార కుక్కీని ఎలా పొందాలో మరియు దానిని ఇతర అభ్యర్థనలలో ఎలా ఉపయోగించాలో ఇక్కడ చదవండి: "Zabbix - విస్తరిస్తున్న స్థూల సరిహద్దులు»

Zabbixలో ట్రాకింగ్ ఎలిమెంట్‌లను సృష్టించడానికి నేను ఆటో డిటెక్షన్‌ని ఉపయోగిస్తాను.

స్వయంచాలక గుర్తింపు

రిజిస్ట్రేషన్‌లను స్వయంచాలకంగా గుర్తించడానికి మరియు పీర్ స్టేట్‌లను ట్రాక్ చేయడానికి, మీరు క్రింది చిరునామాను సంప్రదించాలి: https://ats:8089/mxml?action=SIPshowregistry లేదా https://ats:8089/mxml?action=SIPpeers

ప్రతిస్పందనగా, PBX మాకు XML ప్రతిస్పందనను అందిస్తుంది:

<ajax-response>
<response type="object" id="unknown">
<generic response="Success" eventlist="start" message="Registrations will follow"/>
</response>
...
<response type="object" id="unknown">
<generic event="RegistryEntry" host="login.mtt.ru" port="5060" username="111111" domain="login.mtt.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
<response type="object" id="unknown">
<generic event="RegistryEntry" host="voip.uiscom.ru" port="5060" username="222222" domain="voip.uiscom.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
<response type="object" id="unknown">
<generic event="RegistryEntry" host="voip.uiscom.ru" port="5060" username="333333" domain="voip.uiscom.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
...
</ajax-response>

ప్రతిస్పందనలో చాలా చెత్త ఉంది, కాబట్టి ప్రీప్రాసెసింగ్‌లో మేము దానిని టెంప్లేట్ ద్వారా ఫిల్టర్ చేస్తాము XPath: //స్పందన/సాధారణ[@హోస్ట్]
అప్పుడు సరదా ప్రారంభమవుతుంది. డిటెక్షన్‌తో పని చేయడానికి మరియు ఎలిమెంట్‌లను డైనమిక్‌గా క్రియేట్ చేయడానికి, ప్రతిస్పందన తప్పనిసరిగా JSON ఫార్మాట్‌లో ఉండాలి. స్వీయ గుర్తింపులకు XML మద్దతు లేదు.

XMLని JSONకి మార్చడానికి, నేను ఆటో రీప్లేస్‌మెంట్‌తో కొంచెం ఆడవలసి వచ్చింది, దాని కోసం నేను JSలో స్క్రిప్ట్‌ని తయారు చేసాను

బాక్స్ వెలుపల ఆస్టరిస్క్‌తో Zabbixని ఎలా కనెక్ట్ చేయాలి

ఒక ఆసక్తికరమైన అంశం: ATS ప్రతిస్పందనలో, అన్ని పారామితులు ఒకే కోట్‌లతో చుట్టుముట్టబడి ఉంటాయి మరియు టెంప్లేట్‌ను వర్తింపజేసిన తర్వాత //స్పందన/సాధారణ[@హోస్ట్] అవి డబుల్ వాటితో భర్తీ చేయబడతాయి.

మూలకాలను సృష్టించడానికి, మేము XML ప్రతిస్పందన (ఇప్పుడు JSON) నుండి వేరియబుల్‌లను ఉపయోగిస్తాము.

బాక్స్ వెలుపల ఆస్టరిస్క్‌తో Zabbixని ఎలా కనెక్ట్ చేయాలి

SIP రిజిస్ట్రీ

సిప్ రిజిస్ట్రేషన్ల కోసం మేము మూడు వేరియబుల్స్ ఉపయోగిస్తాము: <span style="font-family: Mandali; "> యూజర్ పేరు </span>, హోస్ట్, పోర్ట్. మూలకం పేరుతో నేను సంతోషించాను [ఇమెయిల్ రక్షించబడింది]: 5060, మీరు మొత్తం ఐదు వేరియబుల్స్‌ని ఉపయోగించాల్సిన సందర్భాలు ఏవీ నేను కనుగొనలేదు.

అన్ని రిజిస్ట్రేషన్ల గురించి సమాచారాన్ని స్వీకరించే ప్రధాన అంశం, ఆస్టరిస్క్ - AMI SIPshowregistry. నిమిషానికి ఒకసారి ఇది GET అభ్యర్థనను చేస్తుంది https://ats:8089/mxml?action=SIPshowregistry, దీని తర్వాత ప్రతిస్పందన XML డేటా పార్సింగ్ కోసం అన్ని ఆధారిత మూలకాలకు పంపబడుతుంది. ప్రతి రిజిస్ట్రేషన్ కోసం నేను దానిపై ఆధారపడిన మూలకాన్ని సృష్టిస్తాను. మేము ఒక అభ్యర్థనలో తాజా సమాచారాన్ని అందుకుంటాము మరియు ప్రతి అభ్యర్థనకు విడివిడిగా కాదు కాబట్టి ఇది సౌకర్యవంతంగా ఉంటుంది. ఈ అమలులో ముఖ్యమైన లోపం ఉంది - ప్రాసెసర్‌పై లోడ్.

100 డిపెండెంట్ ఎలిమెంట్‌లను పరీక్షించేటప్పుడు, నేను లోడ్‌ను గమనించలేదు, కానీ 1700 ఎలిమెంట్‌లతో, ఇది ప్రాసెసర్‌పై గుర్తించదగిన 15 సెకన్ల లోడ్‌ను ఇచ్చింది. మీకు పెద్ద సంఖ్యలో డిపెండెంట్ ఎలిమెంట్స్ ఉంటే దీన్ని గుర్తుంచుకోండి.

లోడ్‌ను "స్ప్రెడ్ అవుట్" చేయడానికి లేదా ఒక మూలకం కోసం వేర్వేరు పోలింగ్ ఫ్రీక్వెన్సీలను సెట్ చేయడానికి ఒక ఎంపికగా, మీరు ప్రాసెసింగ్ లాజిక్‌ను ప్రతి మూలకానికి విడిగా తరలించవచ్చు.

నేను అందుకున్న సమాచారాన్ని ప్రధాన మూలకంలో నిల్వ చేయను. ముందుగా, దీని అవసరం నాకు కనిపించడం లేదు మరియు రెండవది, ప్రతిస్పందన 64K కంటే ఎక్కువగా ఉంటే, Zabbix దాన్ని ఆపివేస్తుంది.

మేము డిపెండెంట్ ఎలిమెంట్ కోసం పూర్తి XML ప్రతిస్పందనను ఉపయోగిస్తాము కాబట్టి, మేము ప్రీప్రాసెసింగ్‌లో ఈ మూలకం యొక్క విలువను పొందాలి. ద్వారా XPath ఇది ఇలా జరిగింది:
string(//response/generic[@event="RegistryEntry"][@username="{#SIP_REGISTRY_USERNAME}"][@host="{#SIP_REGISTRY_HOST}"][@port="{#SIP_REGISTRY_PORT}"]/@ రాష్ట్రం)
రిజిస్ట్రేషన్ స్టేటస్‌ల కోసం, నేను టెక్స్ట్ స్టేటస్‌లను ఉపయోగించలేదు, కానీ జావాస్క్రిప్ట్ ఉపయోగించి వాటిని సంఖ్యా రూపంలోకి మార్చాను:

switch(value) {
  case 'Registered':
    return 1;
  case 'Unregistered':
    return 0;
  default:
    return -1;
}

SIP సహచరులు

SIP రిజిస్ట్రేషన్‌లతో సారూప్యతతో, ఆస్టరిస్క్ యొక్క ప్రధాన అంశం ఉంది - AMI SIPshowregistry, దానిపై ఆధారపడినవి జోడించబడతాయి.

ఇది రెండు ఆధారిత అంశాలను సృష్టిస్తుంది:

  • వచన రూపంలో పీర్ స్థితి
  • పరికర ప్రతిస్పందన సమయం - స్థితి సరిగ్గా ఉంటే, పరికరం ప్రతిస్పందన సమయం వ్రాయబడుతుంది, లేకపోతే “-1”

మూలకం యొక్క మార్గం కొంచెం సరళమైనది XPath:

string(//response/generic[@objectname="{#SIP_PEER_OBEJECTNAME}"]/@స్థితి)

రెండవ మూలకం కోసం నేను వేరు చేయడానికి జావాస్క్రిప్ట్‌ని ఉపయోగించాను ప్రతిస్పందన సమయం పీర్ స్థితి నుండి, అవి కలిసి నిల్వ చేయబడినందున:

if(value.substring(0,2) == 'OK'){
	return value.match(/(d+)/gm);
}
else {
	return -1;
}

తీర్మానం

బాక్స్ వెలుపల పరిష్కారం సంక్లిష్టంగా ఉంటుంది మరియు వెంటనే స్పష్టంగా ఉండదు. వివిధ వ్యవస్థల మధ్య వశ్యత మరియు పోర్టబిలిటీని పెంచుతుంది

అందరూ సంతోషంగా మరియు సులభంగా ఏకీకరణ! సెటప్ చేయడానికి టెంప్లేట్ మరియు సూచనలు గ్యాలరీలు.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి